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

eBPF:如何在内核空间读取ctx->argv所有元素并转发至用户空间?

问题分析与解决

核心问题

你遇到的两个错误本质都是BPF验证器的限制和地址空间隔离导致的:

  1. 指针无法直接传递到用户空间:内核与用户空间地址空间隔离,你存储到data.argv[i]的用户态指针在用户空间毫无意义,且BPF验证器不允许对未初始化/无效指针的内存访问。
  2. 循环无法被验证器确定终止:动态循环(依赖运行时的NULL判断)会被BPF验证器判定为无限循环,必须用固定的最大迭代次数。

错误原因拆解

1. 指针赋值错误

你的struct data_t中char **argv是指针类型,但:

  • 栈上的data结构中argv未初始化,属于野指针。
  • 即使赋值了用户态的arg指针,用户空间也无法直接访问该地址(地址空间隔离)。
  • BPF验证器会拦截这种对无效内存的写入操作,触发R2 invalid mem access 'scalar'错误。

2. 无限循环错误

sizeof(ctx->argv)得到的是指针的字节数(64位系统为8),所以你的循环会执行8次;如果改成while (ctx->argv[i])这种动态终止条件,BPF验证器无法提前确定循环何时结束,会直接判定为无限循环。

解决方案:复制字符串内容到固定大小数组

正确的做法是在data_t中定义固定大小的字符数组,直接复制参数的字符串内容,同时限制循环的最大次数让验证器放心。

修改后的代码示例

#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <linux/types.h>

struct sys_enter_execve {
    // 保留原有的其他字段
    char __user **argv;
};

// 定义最大参数数量和每个参数的最大长度
#define MAX_ARGC 10
#define MAX_ARG_LEN 256

struct data_t {
    // 保留原有的其他字段
    char argv[MAX_ARGC][MAX_ARG_LEN];
    int argc; // 记录实际参数数量
};

// 声明perf map(假设你已经定义过)
struct bpf_map_def SEC("maps") output = {
    .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
    .key_size = sizeof(int),
    .value_size = sizeof(int),
    .max_entries = 64,
};

SEC("tp/syscalls/sys_enter_execve")
int tp_sys_enter_execve(struct sys_enter_execve *ctx)
{
    struct data_t data = {};
    int i;

    // 初始化其他字段...

    for (i = 0; i < MAX_ARGC; i++) {
        char __user *arg_ptr;
        // 先读取argv[i]的用户态指针
        if (bpf_probe_read_user(&arg_ptr, sizeof(arg_ptr), &ctx->argv[i]) != 0) {
            break; // 读取失败,终止循环
        }
        if (!arg_ptr) {
            break; // 遇到NULL,参数列表结束
        }
        // 读取字符串内容到data的数组中,自动截断超出长度的部分
        bpf_probe_read_user_str(data.argv[i], sizeof(data.argv[i]), arg_ptr);
        bpf_printk("arg%d: %s ", i, data.argv[i]);
    }
    data.argc = i; // 记录实际读取到的参数数量

    // 发送到用户空间
    bpf_perf_event_output(ctx, &output, BPF_F_CURRENT_CPU, &data, sizeof(data));
    return 0;
}

char _license[] SEC("license") = "GPL";

关键修改点说明

  1. 替换指针为固定数组:用char argv[MAX_ARGC][MAX_ARG_LEN]存储参数内容,用户空间可以直接读取字符串,无需处理指针。
  2. 限制循环次数:MAX_ARGC固定了循环的最大迭代次数,BPF验证器能确定循环会终止,不会触发无限循环错误。
  3. 分步读取参数:先读取argv[i]的指针,再读取字符串内容,同时处理读取失败和NULL终止的情况,避免越界。

额外注意事项

  • MAX_ARGC和MAX_ARG_LEN可以根据你的需求调整,但不要设置过大(避免栈溢出或perf事件过大)。
  • 如果需要处理超长参数,可以考虑用BPF环形缓冲区(Ring Buffer)或哈希表分批传递,但对于大部分场景,固定数组已经足够。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 13:07:09