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

BPF程序统计文件读取字节出现次数的报错问题求助

BPF_PERCPU_ARRAY访问越界问题排查

我是BPF新手,正在编写程序统计文件读取字节的出现次数(后续用于计算熵)。为规避栈大小限制,我使用两个BPF_PERCPU_ARRAY:一个存储读取文件的前4096字节内容,另一个统计每个可能字节值的出现次数。

初始化代码

struct data_t {
    u8 data[4096];
};

struct counter_t {
    u32 data[256];
};

BPF_PERCPU_ARRAY(storage, struct data_t, 1); // 存储缓冲区
BPF_PERCPU_ARRAY(countarr, struct counter_t, 1); // 统计字节出现次数

函数中使用数组的代码

// 初始化数组索引
int zero = 0;
struct data_t *pstorage = storage.lookup(&zero);
struct counter_t *pcountarr = countarr.lookup(&zero);

// 检查初始化是否成功
if (!pstorage)
    return 0;
if (!pcountarr)
    return 0;

// 将数据复制到storage数组
bpf_probe_read((void*)&pstorage->data, sizeof(pstorage->data), (void*)buf);
    
u8 tmpint = 0;
for(i = 0; i < 4095; i++){
    if (i == count){
        break;
    }
    tmpint = pstorage->data[i];

    // 取消注释以下任意一行都会报错
    // bpf_trace_printk("Current Byte: %d", (int)tmpint); // Line A
    // pcountarr->data[tmpint]++; // Line B
}

错误信息

取消注释Line A或Line B时,出现如下错误:

invalid access to map value, value_size=4096 off=4096 size=1
R8 min value is outside of the allowed memory range
processed 102513 insns (limit 1000000) max_states_per_insn 4 total_states 981 peak_states 977 mark_read 459

其中R8相关信息:

R8_w=map_value(id=0,off=4096,ks=4,vs=4096,imm=0)

问题原因

BPF验证器报错的核心是无法确认数组访问的边界安全性:

  1. 循环中使用的count变量是外部输入(未在代码中限定取值范围),验证器会假设count可能大于等于4096,导致i在循环中超出pstorage->data的索引范围(0-4095),触发越界访问检查。
  2. 原循环条件i < 4095本身未覆盖pstorage->data的全部元素(数组长度为4096,索引应到4095),同时无法阻止count过大时的越界行为。

解决方案

1. 严格限制循环边界

修改循环条件,确保i始终在数组的合法索引范围内:

for(i = 0; i < sizeof(pstorage->data) && i < count; i++){
    tmpint = pstorage->data[i];
    bpf_trace_printk("Current Byte: %d", (int)tmpint);
    pcountarr->data[tmpint]++;
}

用sizeof(pstorage->data)直接限定数组长度,同时结合i < count的条件,BPF验证器能确认i不会超出0-4095的范围。

2. 补充边界检查(备选)

如果需要保留原循环结构,可在循环内部添加边界检查:

for(i = 0; i < 4095; i++){
    if (i >= sizeof(pstorage->data) || i == count){
        break;
    }
    tmpint = pstorage->data[i];
    bpf_trace_printk("Current Byte: %d", (int)tmpint);
    pcountarr->data[tmpint]++;
}

3. 修正bpf_probe_read参数(非必须,但更规范)

pstorage->data本身就是数组首地址,无需取地址,可将复制代码改为:

bpf_probe_read((void*)pstorage->data, sizeof(pstorage->data), (void*)buf);

关于R8的疑问

R8和R8_w是BPF验证器中寄存器的不同状态表示:R8是寄存器的通用名称,R8_w表示该寄存器以32位宽度(word)使用,二者指向同一个物理寄存器,只是验证器在跟踪状态时的不同标记。

内容的提问来源于stack exchange,提问作者johannes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 18:27:30