def _call_packet_copy(i, **kwargs): # If you're thinking 'wow, this function looks complicated,' just go # take a look at kernel/bpf/verifier.c:find_good_pkt_pointers to see # the hoops that we're jumping through here. ret = [] fn, skb, dst_ptr, offset, num_bytes = i.src_vars # TODO: also support ptr to context? if not isinstance(skb, _mem.ArgVar): raise TranslationError( i.starts_line, 'First argument to packet_copy must be SkBuffContext argument') elif not isinstance(num_bytes, _mem.ConstVar): raise TranslationError( i.starts_line, 'Num bytes must not be dynamically defined for packet_copy') skb_data_mem = bi.Mem(_get_var_reg(skb), skb.var_type.data.offset, bi.Size.Word) skb_data_end_mem = bi.Mem(_get_var_reg(skb), skb.var_type.data_end.offset, bi.Size.Word) out_of_bounds = _make_tmp_label() # ret = _mov(dst_ptr, bi.Reg.R1) dst_ptr = _convert_var(dst_ptr) # %r2 = skb->data ret.append(bi.Mov(skb_data_mem, bi.Reg.R2)) # %r2 += offset if isinstance(offset, _mem.ConstVar): off_val = offset.val if hasattr(off_val, 'value'): off_val = off_val.value if off_val != 0: ret.append(bi.Add(bi.Imm(off_val), bi.Reg.R2)) else: ret.extend(_mov(offset, bi.Reg.R3)) ret.append(bi.Add(bi.Reg.R3, bi.Reg.R2)) # %r3 = %r2 ret.append(bi.Mov(bi.Reg.R2, bi.Reg.R3)) # %r2 += num_bytes ret.append(bi.Add(bi.Imm(num_bytes.val), bi.Reg.R2)) # %r4 = skb->data_end ret.append(bi.Mov(skb_data_end_mem, bi.Reg.R4)) # if skb->data + offset + num_bytes > skb->data_end: goto out_of_bounds ret.append(bi.JumpIfGreaterThan(bi.Reg.R4, bi.Reg.R2, out_of_bounds)) ret.extend(_memcpy_packet(i, dst_ptr, bi.Reg.R3, num_bytes.val)) ret.append(bi.Label(out_of_bounds)) return ret
def _delete_subscr(i, **kwargs): if issubclass(i.src_vars[1].var_type, ctypes.Array): raise TranslationError(i.starts_line, 'Cannot delete from array') m, k = i.src_vars if not isinstance(m, _mem.ConstVar): raise TranslationError(i.starts_line, 'Cannot delete from dynamically selected map') return ([bi.Mov(bi.MapFdImm(m.val.fd), bi.Reg.R1)] + _lea(i, k, bi.Reg.R2, **kwargs) + [bi.Call(bi.Imm(funcs.map_delete_elem.num))])
def _call_function(i, **kwargs): fn_var = i.src_vars[0] if not isinstance(fn_var, _mem.ConstVar): raise TranslationError(i.starts_line, 'Function may not be determined dynamically') fn = fn_var.val if isinstance(fn, funcs.PseudoFunc): return _call_pseudo_function(i, **kwargs) if not isinstance(fn, funcs.Func): raise TranslationError( i.starts_line, 'Function must be bpf function from py2bpf.funcs') if fn.num_args != -1 and len(i.src_vars) != fn.num_args + 1: raise TranslationError( i.starts_line, 'Function "{}" expected {} arguments, got {}'.format( fn.name, fn.num_args, len(i.src_vars) - 1)) arg_regs = [bi.Reg.R1, bi.Reg.R2, bi.Reg.R3, bi.Reg.R4, bi.Reg.R5] ret = [] # See explanation for this nonsense in funcs.py arg_vars = [] for idx, var in enumerate(i.src_vars[1:]): arg_vars.append(var) if idx in fn.fill_array_size_args: if issubclass(var.var_type, _types.Ptr): length = ctypes.sizeof(var.var_type.var_type) else: length = ctypes.sizeof(var.var_type) arg_vars.append(_mem.ConstVar(ctypes.c_uint64(length))) # Put the arguments into appropriate registers for arg, reg in zip(arg_vars, arg_regs): if issubclass(arg.var_type, _ctypes._SimpleCData): ret.extend(_mov(arg, reg)) else: ret.extend(_lea(i, arg, reg, **kwargs)) # Call the function ret.append(bi.Call(bi.Imm(fn.num))) # Move the result, if we haven't ignored it if len(i.dst_vars) > 0: ret.extend(_mov(bi.Reg.R0, i.dst_vars[0])) return ret
def _return_value(i, **kwargs): if not issubclass(i.src_vars[0].var_type, _ctypes._SimpleCData): raise TranslationError( i.starts_line, 'Must return int from function. Type is {}'.format( repr(i.src_vars[0].var_type))) return _mov(i.src_vars[0], bi.Reg.R0) + [bi.Ret()]
def _call_pseudo_function(i, **kwargs): fn = i.src_vars[0].val if fn.name == 'memcpy': return _call_memcpy(i, **kwargs) elif fn.name == 'addrof': return _call_addrof(i, **kwargs) elif fn.name == 'ptr': return _call_ptr(i, **kwargs) elif fn.name == 'deref': return _call_deref(i, **kwargs) elif fn.name == 'packet_copy': return _call_packet_copy(i, **kwargs) elif fn.name == 'load_skb_byte': return _call_load_skb_byte(i, **kwargs) elif fn.name == 'load_skb_short': return _call_load_skb_short(i, **kwargs) elif fn.name == 'load_skb_word': return _call_load_skb_word(i, **kwargs) elif fn.name == 'mem_eq': return _call_mem_eq(i, **kwargs) elif fn.name == 'load_xdp_data': return _call_load_xdp_data(i, **kwargs) else: raise TranslationError( i.starts_line, 'Reference to invalid pseudo-function: {}'.format(fn.name))
def _call_mem_eq(i, **kwargs): if (not isinstance(i.src_vars[1], _mem.ConstVar) or not issubclass(i.src_vars[1].var_type, ctypes.Array)): raise TranslationError( i.starts_line, 'first arg to mem_eq must be const ctypes.Array') ret = [] # stack=None because we shouldn't need to allocate anything ret.extend(_lea(i, i.src_vars[2], bi.Reg.R2, stack=None)) false, done = _make_tmp_label(), _make_tmp_label() for off, v in enumerate(i.src_vars[1].val.value): ret.extend([ bi.Mov(bi.Mem(bi.Reg.R2, off, bi.Size.Byte), bi.Reg.R1), bi.JumpIfNotEqual(bi.Imm(v), bi.Reg.R1, false) ]) # If we made it here, it's a match ret.extend(_mov(bi.Imm(1), i.dst_vars[0])) ret.append(bi.Jump(done)) # if we jumped here, it's not a match ret.append(bi.Label(false)) ret.extend(_mov(bi.Imm(0), i.dst_vars[0])) ret.append(bi.Label(done)) return ret
def _call_memcpy(i, **kwargs): fn, dst_addr, src_addr, num_bytes = i.src_vars if not isinstance(num_bytes, _mem.ConstVar): raise TranslationError(i.starts_line, 'memcpy amount must be constant') return (_mov(src_addr, bi.Reg.R1) + _mov(dst_addr, bi.Reg.R2) + _memcpy(i, bi.Reg.R1, bi.Reg.R2, num_bytes.val))
def _lea(i, src, dst, stack, **kwargs): if issubclass(src.var_type, FileDescriptorDatastructure): if not isinstance(src, _mem.ConstVar): raise TranslationError( i.starts_line, 'Cannot handle non-const file descriptor datastructures') return _mov(bi.MapFdImm(src.val.fd), dst) setup = [] if isinstance(src, _mem.ConstVar): # We have to lay it down in memory ourselves, sadly tmp_src = stack.alloc(src.var_type) tmp_reg = _get_var_reg(tmp_src) setup = _mov_const(src.var_type, src.val, tmp_reg, tmp_src.offset) src = tmp_src # TODO: fix types. Right now, dt may by a ulong for addrof, because we # can't plug in real return types yet. # if not isinstance(dst, bi.Reg): # st, dt = src.var_type, dst.var_type # assert issubclass(dt,_types.Ptr) and dt.var_type == st reg = _get_var_reg(src) if src.offset == 0: return setup + _mov(reg, dst) else: return ( setup + [bi.Mov(reg, bi.Reg.R0), bi.Add(bi.Imm(src.offset), bi.Reg.R0)] + _mov(bi.Reg.R0, dst))
def _store_subscr_map(i, **kwargs): v, m, k = i.src_vars if not isinstance(m, _mem.ConstVar): raise TranslationError(i.starts_line, 'Cannot subscript dynamically selected map') return ([bi.Mov(bi.MapFdImm(m.val.fd), bi.Reg.R1)] + _lea(i, k, bi.Reg.R2, **kwargs) + _lea(i, v, bi.Reg.R3, **kwargs) + [ bi.Mov(bi.Imm(0), bi.Reg.R4), bi.Call(bi.Imm(funcs.map_update_elem.num)) ])
def _load_arr_element_addr(i, arr, index, dst_reg): if not isinstance(index, _mem.ConstVar): raise TranslationError(i.starts_line, 'Illegal to use dynamic index to array') if _is_ptr(arr.var_type): el_off = index.val.value * ctypes.sizeof(arr.var_type.var_type._type_) ret = _mov(arr, dst_reg) else: el_off = index.val.value * ctypes.sizeof( arr.var_type._type_) + arr.offset ret = [bi.Mov(_get_var_reg(arr), dst_reg)] if el_off != 0: ret.append(bi.Add(bi.Imm(el_off), dst_reg)) return ret
def _call_load_xdp_data(i, **kwargs): ret = [] fn, skb, offset = i.src_vars data = i.dst_vars[0] if not isinstance(skb, _mem.ArgVar): raise TranslationError( i.starts_line, 'First argument to load_xdp_data must be XdpMetaDataContext argument' ) if isinstance(offset, _mem.ConstVar): assert issubclass(offset.var_type, _ctypes._SimpleCData) offset = offset.val.value skb_data_mem = bi.Mem(_get_var_reg(skb), offset, bi.Size.Word) ret.append(bi.Mov(skb_data_mem, bi.Reg.R2)) data = _convert_var(data) ret.append(bi.Mov(bi.Reg.R2, data)) return ret
def _call_load_skb(i, sz): fn, skb, off = i.src_vars dst = i.dst_vars[0] if not isinstance(skb, _mem.ArgVar): raise TranslationError( i.starts_line, 'First argument to {} must be skb context'.format(fn.val.name)) if isinstance(off, _mem.ConstVar): # Unbox ctypes off_val = off.val if hasattr(off_val, 'value'): off_val = off_val.value return [bi.LoadSkb(bi.Imm(off_val), sz)] + _mov(bi.Reg.R0, dst) else: return (_mov(off, bi.Reg.R0) + [bi.LoadSkb(bi.Reg.R0, sz)] + _mov(bi.Reg.R0, dst))
def _memcpy_packet(i, dst_reg, src_reg, num_bytes): ret = [] # Unpack ctype if isinstance(dst_reg, bi.Mem): off = dst_reg.off d_reg = dst_reg.reg else: raise TranslationError(i.starts_line, 'dst_reg must be bi.Mem') if hasattr(num_bytes, 'value'): num_bytes = num_bytes.value for j in range(num_bytes): dm = bi.Mem(d_reg, off, bi.Size.Byte) sm = bi.Mem(src_reg, j, bi.Size.Byte) ret.extend(_mov(sm, dm)) off += 1 return ret
def _binary_subscr_map(i, **kwargs): m, k, dv = i.src_vars[0], i.src_vars[1], i.dst_vars[0] if not isinstance(m, _mem.ConstVar): raise TranslationError(i.starts_line, 'Cannot subscript dynamically select map') found, done = _make_tmp_label(), _make_tmp_label() ret = [] ret.extend(_mov(bi.MapFdImm(m.val.fd), bi.Reg.R1)) ret.extend(_lea(i, k, bi.Reg.R2, **kwargs)) ret.extend([ bi.Call(bi.Imm(funcs.map_lookup_elem.num)), ]) if _is_ptr(dv.var_type): sz = _get_cdata_size(dv.var_type) ret.extend(_mov(bi.Reg.R0, dv)) else: ret.extend([ bi.JumpIfNotEqual(bi.Imm(0), bi.Reg.R0, found), ]) # Move default value dr = _get_var_reg(dv) ret.extend(_mov_const(dv.var_type, m.val.DEFAULT_VALUE, dr, dv.offset)) ret.extend([ bi.Jump(done), bi.Label(found), ]) # Primitives by value, others by reference if issubclass(dv.var_type, _ctypes._SimpleCData): sz = _get_cdata_size(dv.var_type) ret.extend(_mov(bi.Mem(bi.Reg.R0, 0, sz), dv)) else: ret.extend(_mov(bi.Reg.R0, dv)) ret.append(bi.Label(done)) return ret