如何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
我是 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