如何read/understand bpf kernel verifier分析调试错误?

How to read/understand the bpf kernel verifier analysis to debug the error?

我是 XDP eBPF 的新手。我有一个旨在丢弃 UDP 数据包的 BPF 程序,但它无法加载,因为它被内核验证程序拒绝了。下面是代码:

#include <linux/bpf.h>
#include <linux/icmp.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>

#define SEC(NAME) __attribute__((section(NAME), used))
SEC("xdp")
int dropper(struct xdp_md *ctx) {

    unsigned int ipsize = 0;
    void *data = (void *)(unsigned long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;

    struct ethhdr *eth = data;
    ipsize = sizeof(*eth);
    struct iphdr *ip = (struct iphdr *)((char*)ctx->data + ipsize);
    if (ip->protocol == IPPROTO_UDP) {
             return XDP_DROP;
    }

    return XDP_PASS;
}

char _license[] SEC("license") = "GPL";

这是加载程序时出现的内容:

Prog section 'xdp' rejected: Permission denied (13)!
 - Type:         6
 - Instructions: 30 (0 over limit)
 - License:      GPL

Verifier analysis:

0: (7b) *(u64 *)(r10 -16) = r1
1: (b7) r1 = 0
2: (63) *(u32 *)(r10 -20) = r1
last_idx 2 first_idx 0
regs=2 stack=0 before 1: (b7) r1 = 0
3: (79) r1 = *(u64 *)(r10 -16)
4: (61) r1 = *(u32 *)(r1 +0)
5: (7b) *(u64 *)(r10 -32) = r1
6: (79) r1 = *(u64 *)(r10 -16)
7: (61) r1 = *(u32 *)(r1 +4)
8: (7b) *(u64 *)(r10 -40) = r1
9: (79) r1 = *(u64 *)(r10 -32)
10: (7b) *(u64 *)(r10 -48) = r1
11: (b7) r1 = 14
12: (63) *(u32 *)(r10 -20) = r1
13: (79) r1 = *(u64 *)(r10 -16)
14: (61) r1 = *(u32 *)(r1 +0)
15: (61) r2 = *(u32 *)(r10 -20)
16: (0f) r1 += r2
last_idx 16 first_idx 0
regs=4 stack=0 before 15: (61) r2 = *(u32 *)(r10 -20)
17: (7b) *(u64 *)(r10 -56) = r1
18: (79) r1 = *(u64 *)(r10 -56)
19: (71) r1 = *(u8 *)(r1 +9)
invalid access to packet, off=9 size=1, R1(id=1,off=0,r=0)
R1 offset is outside of the packet
processed 20 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

Error fetching program/map!

我发现错误出现在 UDP 检查语句中 (ip->protocol == IPPROTO_UDP),因为当我评论检查时,它加载时没有错误。

所以,我需要知道的是,为什么内核验证器拒绝 UDP 检查语句(如前所述),以及如何解决它来解决错误。

谢谢。

在 eBPF 的内核文档中 描述了其中一些错误。在您的情况下,请特别参阅 direct packet access.

部分

在 Linux 内核中强制检查您的程序的内核验证器 确保没有 out-of-bound 尝试访问 。您的程序被拒绝,因为它可能会触发此类 out-of-bound 访问。

您需要检查 您的数据包的长度至少等于 IPv4 的长度 header,这样程序就不会尝试在读取 ip->protocol 时加载超出数据包边界 的内容。像这样(未测试):

/*
 * If packet is shorter than a IPv4 header, we cannot
 * dereference and read ip->protocol, and it cannot be
 * UDP anyway: pass the packet to the stack.
 *
 * "ip + 1" is the address at "ip + the length of one
 * additional struct iphdr", in other words, it points
 * to the byte right after the IP header.
 */
if (ip + 1 > data_end)
        return XDP_PASS;

if (ip->protocol == IPPROTO_UDP) { ...

详情请参考this other answer