You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 03:09:55