BPF验证程序拒绝代码: [英] BPF verifier rejects code: "invalid bpf_context access"

查看:267
本文介绍了BPF验证程序拒绝代码:的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个简单的套接字过滤器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() ,在尝试读取 skb-> data skb-> data_end <时,我们返回 false /code>).我不知道为什么它不可用的具体原因,尽管我怀疑这将是一种安全预防措施,因为套接字过滤器程序可能对非特权用户可用.

您的程序可以作为TC分类器正常加载,例如( bpftool prog load foo.o/sys/fs/bpf/foo type classifier -顺便感谢独立工作的复制器,非常感谢!).

如果要访问套接字过滤器的数据,仍然可以使用 <代码> #include< linux/bpf.h>#定义SEC(NAME)__attribute __(((section(NAME),已使用)))静态void *(* bpf_skb_load_bytes)(const struct __sk_buff *,__u32,无效*,__u32)=(void *)BPF_FUNC_skb_load_bytes;SEC("socket_filter")int myprog(结构__sk_buff * skb){__u32 foo;如果(bpf_skb_load_bytes(skb,0,& foo,sizeof(foo)))返回0;如果(foo == 3)返回0;返回1;}

关于您的最后评论:

但是,只有在以后我不尝试检查边界时,这种情况才会发生.

如果您未在代码中使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆