我正在尝试编写一个可以访问套接字缓冲区数据的简单套接字过滤器 eBPF 程序。
#include <linux/bpf.h>
#include <linux/if_ether.h>
#define SEC(NAME) __attribute__((section(NAME), used))
SEC("socket_filter")
int myprog(struct __sk_buff *skb) {
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
struct ethhdr *eth = data;
if ((void*)eth + sizeof(*eth) > data_end)
return 0;
return 1;
}
Run Code Online (Sandbox Code Playgroud)
我正在使用 clang 进行编译:
clang -I./ -I/usr/include/x86_64-linux-gnu/asm \
-I/usr/include/x86_64-linux-gnu/ -O2 -target bpf -c test.c -o test.elf
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试加载程序时,出现以下验证器错误:
invalid bpf_context access off=80 size=4
Run Code Online (Sandbox Code Playgroud)
我对这个错误的理解是,当您尝试访问尚未检查为在 内的上下文数据时应该抛出它data_end,但是我的代码确实这样做了:
这是我的程序的说明
0000000000000000 packet_counter:
0: 61 12 50 00 00 00 00 00 r2 = *(u32 *)(r1 + 80)
1: 61 11 4c 00 00 00 00 00 r1 = *(u32 *)(r1 + 76)
2: 07 01 00 00 0e 00 00 00 r1 += 14
3: b7 00 00 00 01 00 00 00 r0 = 1
4: 3d 12 01 00 00 00 00 00 if r2 >= r1 goto +1 <LBB0_2>
5: b7 00 00 00 00 00 00 00 r0 = 0
Run Code Online (Sandbox Code Playgroud)
这意味着错误是由读取指针引起的data_end?但是,只有当我稍后不尝试检查边界时才会发生这种情况。
这是因为您的 BPF 程序是\xe2\x80\x9csocket filter\xe2\x80\x9d,并且不允许此类程序进行直接数据包访问(请参阅sk_filter_is_valid_access(),我们在false尝试读取skb->data或skb->data_end时返回的位置)。我不知道它不可用的具体原因,尽管我怀疑这将是一种安全预防措施,因为套接字过滤程序可能可供非特权用户使用。
例如,您的程序作为 TC 分类器加载得很好(bpftool prog load foo.o /sys/fs/bpf/foo type classifier--顺便感谢独立工作的再现器,非常感谢!)。
如果您想访问套接字过滤器的数据,您仍然可以使用bpf_skb_load_bytes()(或bpf_skb_store_bytes()) 帮助器,它会自动检查长度。像这样的东西:
#include <linux/bpf.h>\n\n#define SEC(NAME) __attribute__((section(NAME), used))\n\nstatic void *(*bpf_skb_load_bytes)(const struct __sk_buff *, __u32,\n void *, __u32) =\n (void *) BPF_FUNC_skb_load_bytes;\n\nSEC("socket_filter")\nint myprog(struct __sk_buff *skb)\n{\n __u32 foo;\n\n if (bpf_skb_load_bytes(skb, 0, &foo, sizeof(foo)))\n return 0;\n if (foo == 3)\n return 0;\n return 1;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n关于您最后的评论:
\n\n\n\n\n然而,只有当我稍后不尝试检查边界时才会发生这种情况。
\n
data我怀疑 clang 会编译出和 的分配,data_end如果您不在代码中使用它们,那么它们不再存在,并且不再成为验证者的问题。