不定期更新
以64位系统为例,最小的字节对齐是4字节(u32)对齐,最大字节对齐是8字节(u64),按需增加保留字段,否则会被编译器优化填充。
包含未初始化字段的对象被认为不可信,加载时会被verifier报错。
__builtin_memset
__builtin_memcpy
__builtin_memmove
例如:
struct iphdr *ip4 = (struct iphdr*)skb->data + ETH_HLEN; // 首次获取
...
skb_store_bytes(skb, l3_off + offsetof(struct iphdr, saddr), &new_saddr, 4, 0); // skb被操作了,因此ip4的值不可信,此时操作ip4会被拒绝
ip4 = (struct iphdr*)skb->data + ETH_HLEN; // 需要再次获取
if (ip4->protocol == IPPROTO_TCP) { // 重新校验后才能正常使用
// do something
}
从bpf的map中lookup的指针不能再update回去,如果要修改里面的value直接操作指针即可。
ebpf本身不允许循环的存在,因为会被判定为“无法及时退出”,影响内核执行效率。不过可以预见到循环将在有限次执行后退出,可以使用#pragma unroll
在编译期间展开for循环。当然,循环的次数是有限的,因为低内核版本的bpf本身代码指令数就非常有限(4096)。
back-edge from insn xx to xx
指令回跳会增加指令分析的复杂度,所以 verifier 直接禁止出现指令回跳。一般for循环会造成指令回跳:
SEC("kprobe/tcp_sendmsg")
int BPF_KPROBE(tcp_sendmsg, struct sock *sk)
{
int i;
for (i=0;i<1000;i++)
bpf_printk("%d\n", i);
return 0;
}
一般解决方法是:在 for 循环前面添加 #pragma unroll,进行循环展开,避免指令回跳。
Looks like the BPF stack limit of 512 bytes is exceeded. Please move large on stack variables into BPF per-cpu array map
因为 verifier 会保存栈内存的状态,所以栈的大小是有限的,目前是 512 字节。当栈内存大小超过 512 字节时,则会被 verifier 拒绝:
SEC("kprobe/tcp_sendmsg")
int BPF_KPROBE(tcp_sendmsg, struct sock *sk)
{
#define MAX_NUM (512/8)
volatile u64 arr[MAX_NUM + 1] = {};
arr[MAX_NUM] = 0xff;
bpf_printk("%lld\n", arr[MAX_NUM]);
return 0;
}
variable stack access var_off=(0x0; 0x78) off=-128 size=8-
当访问栈时采用变量偏移,会导致无法推测寄存器的状态。所以 4.19 版本只支持常量偏移。下面是使用变量偏移的错误示例:
SEC("kprobe/tcp_sendmsg")
int BPF_KPROBE(tcp_sendmsg, struct sock *sk)
{
u64 volatile arr[16] = {};
arr[bpf_ktime_get_ns() & 0xf] = 0;
return 0;
}
PS:5.10内核已经支持变量类型的栈偏移。
math between fp pointer and register with unbounded min value is not allowed
范围检查主要是用来判断内存访问是否越界:
struct
{
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, int);
__type(value, int);
__uint(max_entries, 1024);
} indexmap SEC(".maps");
SEC("kprobe/tcp_sendmsg")
int BPF_KPROBE(tcp_sendmsg, struct sock *sk)
{
int map_key = 0;
int map_val = 0;
int array[10] = {};
int *map_val_ptr = bpf_map_look_up(&indexmap, &map_key);
if (map_val_ptr)
map_val = *map_val_ptr;
bpf_printk("array[%d] = %d\n", map_val, array[map_val]);
return 0;
}
char LICENSE[] SEC("license") = "GPL";
一般解决方法是:在内存访问时,进行范围检查。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。