eBPF程序遍历TCP载荷时触发invalid access to packet错误
解决eBPF遍历TCP载荷时的
invalid access to packet错误 你的eBPF程序触发invalid access to packet错误,核心原因是eBPF验证器无法静态证明循环内的内存访问是安全的。虽然你在循环条件中加入了tcp_data + i <= data_end的检查,但验证器无法将这个动态指针比较和后续的tcp_data[i]访问建立可靠的关联,再加上MAX_PACKET_OFF的取值远大于常规数据包载荷长度,验证器判定存在越界风险。
修复方案:基于精确载荷长度的边界约束
通过计算TCP载荷的实际长度来限制循环次数,给验证器提供明确的静态边界证明,修改后的代码如下:
#include "vmlinux.h" #include <bpf/bpf_helpers.h> #include <bpf/bpf_core_read.h> #include <bpf/bpf_endian.h> #define MAX_PACKET_OFF 0xFFFF #define ETH_P_IP 0x0800 #define TC_ACT_OK 0 #define TC_ACT_SHOT 2 SEC("tc") int handle_tc_ingress(struct __sk_buff *skb) { void *data_end = (void *)(long) skb->data_end; void *data = (void *)(long) skb->data; struct ethhdr *eth_hdr = data; if ((void *)(eth_hdr + 1) > data_end) { return TC_ACT_OK; } if (eth_hdr->h_proto != bpf_htons(ETH_P_IP)) { return TC_ACT_OK; } struct iphdr *ipv4_hdr = (struct iphdr *)(eth_hdr + 1); if ((void *)(ipv4_hdr + 1) > data_end) { return TC_ACT_OK; } // 转换IP总长度为主机字节序,计算IP头长度 __u16 ip_total_len = bpf_ntohs(ipv4_hdr->tot_len); __u8 ip_hdr_len = 4 * ipv4_hdr->ihl; // 计算TCP段总长度,校验合法性 __u16 tcp_seg_len = ip_total_len - ip_hdr_len; if (tcp_seg_len < sizeof(struct tcphdr)) { return TC_ACT_OK; } struct tcphdr *tcp_hdr = (struct tcphdr *)((__u8 *)ipv4_hdr + ip_hdr_len); if ((void *)(tcp_hdr + 1) > data_end) { return TC_ACT_OK; } // 计算TCP头长度,校验合法性 __u8 tcp_hdr_len = 4 * tcp_hdr->doff; if (tcp_hdr_len < sizeof(struct tcphdr)) { return TC_ACT_OK; } // 计算TCP载荷长度,限制最大值 __u16 tcp_payload_len = tcp_seg_len - tcp_hdr_len; if (tcp_payload_len == 0 || tcp_payload_len > MAX_PACKET_OFF) { return TC_ACT_OK; } __u8 *tcp_data = (__u8 *)tcp_hdr + tcp_hdr_len; // 最终校验:确保整个载荷范围都在数据包内 if ((void *)(tcp_data + tcp_payload_len) > data_end) { return TC_ACT_OK; } for (int i = 0; i < tcp_payload_len; ++i) { if (tcp_data[i] == ' ') { bpf_printk("space"); return TC_ACT_SHOT; } } return TC_ACT_OK; }
关键修复点:
- 精确计算载荷长度:通过IP头的
tot_len字段,结合IP/TCP头的实际长度,算出TCP载荷的真实长度,用这个长度作为循环的上限,验证器可以明确判断循环不会访问超出数据包的内存。 - 增加合法性校验:对IP/TCP头长度、TCP段长度做合理性检查,避免非法的头长度导致计算错误。
- 明确边界证明:最后通过
tcp_data + tcp_payload_len <= data_end的检查,给验证器提供清晰的内存边界保证。
你提到的goto pc+2是编译器的循环优化,不是错误根源——验证器能跟踪初始的i=0,但无法关联循环中递增的i和指针约束,这才是问题核心。
内容的提问来源于stack exchange,提问作者tgnottingham
相关产品推荐
相关产品推荐

