BPF验证程序拒绝代码: [英] BPF verifier rejects code: "invalid bpf_context access"
问题描述
我正在尝试编写一个简单的套接字过滤器eBPF程序,该程序可以访问套接字缓冲区数据.
<代码> #include< linux/bpf.h>#include< linux/if_ether.h>#定义SEC(NAME)__attribute __(((section(NAME),已使用))SEC("socket_filter")int myprog(struct __sk_buff * skb){无效*数据=(无效*)(长)skb->数据;void * data_end =(void *)(long)skb-> data_end;struct ethhdr * eth =数据;如果((void *)eth + sizeof(* eth)> data_end)返回0;返回1;}
我正在使用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
但是,当我尝试加载程序时,出现以下验证错误:
invalid bpf_context access off = 80 size = 4
我对这个错误的理解是,当您尝试访问尚未检查在 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 + = 143:b7 00 00 00 01 00 00 00 r0 = 14:3d 12 01 00 00 00 00 00 如果 r2 >= r1 goto +1 5:b7 00 00 00 00 00 00 00 r0 = 0
这是否意味着错误是由于读取 data_end
的指针引起的?但是,只有在我以后不尝试检查边界时,这种情况才会发生.
这是因为您的BPF程序是套接字过滤器" ,并且不允许此类程序直接执行数据包访问(请参见 <代码> sk_filter_is_valid_access() ,在尝试读取 您的程序可以作为TC分类器正常加载,例如( skb-> data
或 skb-> data_end <时,我们返回
false
/code>).我不知道为什么它不可用的具体原因,尽管我怀疑这将是一种安全预防措施,因为套接字过滤器程序可能对非特权用户可用. bpftool prog load foo.o/sys/fs/bpf/foo type classifier
-顺便感谢独立工作的复制器,非常感谢!).
关于您的最后评论:
但是,只有在以后我不尝试检查边界时,这种情况才会发生.
如果您未在代码中使用clang,我怀疑clang会编译出 data
和 data_end
的分配,因此它们不再存在且不再是问题验证程序.
I'm trying to write a simple socket filter eBPF program that can access the socket buffer data.
#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;
}
And I'm compiling using 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
However when I try to load the program I get the following verifier error:
invalid bpf_context access off=80 size=4
My understanding of this error is that it should be thrown when you try to access context data that hasn't been checked to be within data_end
, however my code does do that:
Here is the instructions for my program
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
which would imply that the error is being caused by reading the pointer to data_end
? However it only happens if I don't try to check the bounds later.
This is because your BPF program is a "socket filter", and that such programs are not allowed to do direct packet access (see sk_filter_is_valid_access()
, where we return false
on trying to read skb->data
or skb->data_end
for example). I do not know the specific reason why it is not available, although I suspect this would be a security precaution as socket filter programs may be available to unprivileged users.
Your program loads just fine as a TC classifier, for example (bpftool prog load foo.o /sys/fs/bpf/foo type classifier
-- By the way thanks for the standalone working reproducer, much appreciated!).
If you want to access data for a socket filter, you can still use the bpf_skb_load_bytes()
(or bpf_skb_store_bytes()
) helper, which automatically does the check on length. Something like this:
#include <linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))
static void *(*bpf_skb_load_bytes)(const struct __sk_buff *, __u32,
void *, __u32) =
(void *) BPF_FUNC_skb_load_bytes;
SEC("socket_filter")
int myprog(struct __sk_buff *skb)
{
__u32 foo;
if (bpf_skb_load_bytes(skb, 0, &foo, sizeof(foo)))
return 0;
if (foo == 3)
return 0;
return 1;
}
Regarding your last comment:
However it only happens if I don't try to check the bounds later.
I suspect clang compiles out the assignments for data
and data_end
if you do not use them in your code, so they are no longer present and no longer a problem for the verifier.
这篇关于BPF验证程序拒绝代码:的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!