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

如何通过eBPF数组映射从用户空间正确传递定长字符串?

eBPF中用户空间传递字符串及字符串比较的问题

问题场景与代码

我使用BPF_MAP_TYPE_ARRAY类型的映射存储以下结构体实例:

struct target_d_name {
    unsigned long int len;
    char name[PID_LEN_MAX]; // PID_LEN_MAX = 8,宏定义
};

映射定义如下:

struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(max_entries, 8);
    __type(key, u32);
    __type(value, sizeof(struct target_d_name));
} map_d_name_tgts SEC(".maps");

操作流程:

  • Hook getdents64的exit阶段
  • 遍历所有返回的dirent64结构体
  • 检查dirent64->d_name是否与target_d_name->name匹配

字符串对比代码:

// iterator_limit为filename长度与PID_LEN_MAX中的较小值
for (k = 0; k < iterator_limit; ++k) {
  if (filename[k] != target_d_name_instance->name[k]) break;
}

加载eBPF程序时,验证器报错:

invalid access to map value, value_size=8 off=8 size=1
R5 min value is outside of the allowed memory range

报错指向访问target_d_name_instance->name[k]的操作。经调研,eBPF验证器无法追踪结构体内部字符缓冲区的大小,即使循环范围在合法边界内,仍会判定为可能越界。

更新:以下最小示例会触发相同报错:

u32 index = 0;
struct target_d_name * current_d_name 
    = bpf_map_lookup_elem(&map_d_name_tgts, &index);
if (current_d_name == 0) return -1;

for (int i = 0; i < PID_LEN_MAX; ++i) {
    current_d_name->name[i] = 'u';
}
return 0;

疑问

除当前方式外,如何从用户空间向eBPF程序传递字符串?若需要遍历和比较字符串该怎么做?注意到bpf_strncmp()辅助函数,但它仅支持常量字符串,对动态生成的字符串无效。

解决方案

1. 将结构体中的字符串读取到栈上操作

eBPF验证器能可靠追踪栈上变量的大小,因此可以先将映射中结构体的字符串字段读取到栈数组中,再进行遍历或比较:

u32 index = 0;
struct target_d_name *current_d_name = bpf_map_lookup_elem(&map_d_name_tgts, &index);
if (!current_d_name) return -1;

// 将字符串读取到栈上
char target_name[PID_LEN_MAX];
bpf_probe_read(target_name, sizeof(target_name), &current_d_name->name);

// 使用栈上的字符串进行比较
int iterator_limit = min(strlen(filename), PID_LEN_MAX);
for (int k = 0; k < iterator_limit; ++k) {
    if (filename[k] != target_name[k]) break;
}

2. 使用紧凑结构体消除对齐间隙

给结构体添加__attribute__((packed))修饰,消除字段间的内存对齐间隙,帮助验证器正确计算内存偏移:

struct target_d_name {
    unsigned long int len;
    char name[PID_LEN_MAX];
} __attribute__((packed));

3. 单独存储字符串到映射

若不需要同时传递len字段,可以直接用BPF_MAP_TYPE_ARRAY或BPF_MAP_TYPE_HASH存储字符串数组:

// 数组映射直接存字符串
struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(max_entries, 8);
    __type(key, u32);
    __type(value, char[PID_LEN_MAX]);
} map_direct_name SEC(".maps");

访问时直接读取映射元素到栈上,再进行操作即可。

4. 实现安全的动态字符串比较函数

手动实现支持动态字符串的strncmp替代函数,基于栈上变量操作,避免直接访问映射中的结构体字段:

static __always_inline int safe_strncmp(const char *s1, const char *s2, size_t max_len) {
    for (size_t i = 0; i < max_len; ++i) {
        if (s1[i] != s2[i]) {
            return s1[i] - s2[i];
        }
        if (s1[i] == '\0') {
            return 0;
        }
    }
    return 0;
}

// 使用示例
char target_name[PID_LEN_MAX];
bpf_probe_read(target_name, sizeof(target_name), &current_d_name->name);
if (safe_strncmp(filename, target_name, PID_LEN_MAX) == 0) {
    // 匹配逻辑
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 01:54:53