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

eBPF程序中bpf_probe_read_user触发权限拒绝错误求助

问题

开发eBPF程序时,在kretprobe/sys_read探针中尝试将用户态请求缓冲区数据读取到BPF_MAP_TYPE_ARRAY类型映射中,eBPF验证器报错:

{"error": "field SyscallProbeRetRead: program syscall__probe_ret_read: load program: permission denied: invalid access to map value, value_size=70 off=0 size=16383: R1 min value is outside of the allowed memory range (truncated, 73 line(s) omitted)"}

逻辑说明:若buf_size大于MAX_MSG_SIZE,则将最多TRACE_PARENT_SIZE字节的数据读取到seventy_bytes_array->data中;否则不执行读取操作。

探针代码

#define MAX_MSG_SIZE 16383 
#define TRACE_PARENT_SIZE 70

SEC("kretprobe/sys_read")
int syscall__probe_ret_read(struct pt_regs *ctx)
{
    size_t buf_size = PT_REGS_RC(ctx);
    u64 id = bpf_get_current_pid_tgid();
    struct data_args_t *read_args = bpf_map_lookup_elem(&active_read_args_map, &id);

    if (read_args)
    {
        char *buf = read_args->buf;

        const u32 keyOne = 0;
        struct char_array_value *seventy_bytes_array = bpf_map_lookup_elem(&leftover_buf, &keyOne);

        unsigned int overrided_bytes = (buf_size - MAX_MSG_SIZE) > TRACE_PARENT_SIZE ? TRACE_PARENT_SIZE : ((buf_size - MAX_MSG_SIZE) > 0 ? (buf_size - MAX_MSG_SIZE) : 0);

        if (overrided_bytes > 0 && buf + MAX_MSG_SIZE && seventy_bytes_array)
        {
             asm volatile("%[overrided_bytes] &= 0x3fff;\n" ::[overrided_bytes] "+r"(overrided_bytes) :);
             bpf_probe_read_user(&seventy_bytes_array, overrided_bytes & 0x3fff, buf + MAX_MSG_SIZE);
        }
    }

    return 0;
}

映射定义

struct 
{
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __type(key, u32);
    __type(value, struct char_array_value);
    __uint(max_entries, 1);
} leftover_buf SEC(".maps");

结构体定义

struct char_array_value {
    char data[TRACE_PARENT_SIZE];  // Replace SIZE with the desired size of your char array
};

完整验证器日志

599: frame1: R0=map_value_or_null(id=13,off=0,ks=4,vs=70,imm=0) R2=scalar(id=4,umin=16,umax=4294967295,var_off=(0x0; 0xffffffff)) R6=scalar(umin=16384,umax=16453,var_off=(0x4000; 0x7f)) R8=map_value(off=0,ks=8,vs=40,imm=0) R9=map_value(off=0,ks=4,vs=16448,imm=0) R10=fp0 fp-80=mmmm???? fp-88= fp-96=mmmmmmmm fp-104=1
:; unsigned int overrided_bytes = (buf_size - MAX_MSG_SIZE) > TRACE_PARENT_SIZE ? TRACE_PARENT_SIZE : ((buf_size - MAX_MSG_SIZE) > 0 ? (buf_size - MAX_MSG_SIZE) : 0);
:599: (07) r2 += -16383                ; frame1: R2_w=scalar(smin=-16367,smax=4294950912)
:; if (overrided_bytes > 0 && buf + MAX_MSG_SIZE && seventy_bytes_array)
:600: (bf) r1 = r2                     ; frame1: R1_w=scalar(id=255,smin=-16367,smax=4294950912) R2_w=scalar(id=255,smin=-16367,smax=4294950912)
:601: (67) r1 <<= 32                   ; frame1: R1_w=scalar(smax=9223372032559808512,umax=18446744069414584320,var_off=(0x0; 0xffffffff00000000),s32_min=0,s32_max=0,u32_max=0)
:602: (77) r1 >>= 32                   ; frame1: R1_w=scalar(umax=4294967295,var_off=(0x0; 0xffffffff))
:; if (overrided_bytes > 0 && buf + MAX_MSG_SIZE && seventy_bytes_array)
:603: (15) if r1 == 0x0 goto pc+6      ; frame1: R1_w=scalar(umax=4294967295,var_off=(0x0; 0xffffffff))
:604: (15) if r0 == 0x0 goto pc+5      ; frame1: R0=map_value(off=0,ks=4,vs=70,imm=0)
:; asm volatile("%[overrided_bytes] &= 0x3fff;\n" ::[overrided_bytes] "+r"(overrided_bytes) :);
:605: (57) r2 &= 16383                 ; frame1: R2_w=scalar(umax=16383,var_off=(0x0; 0x3fff))
:606: (79) r3 = *(u64 *)(r10 -96)      ; frame1: R3_w=scalar() R10=fp0 fp-96=mmmmmmmm
:607: (07) r3 += 16383                 ; frame1: R3_w=scalar()
:; bpf_probe_read_user(&seventy_bytes_array->data, overrided_bytes & 0x3fff, buf + MAX_MSG_SIZE);
:608: (bf) r1 = r0                     ; frame1: R0=map_value(off=0,ks=4,vs=70,imm=0) R1_w=map_value(off=0,ks=4,vs=70,imm=0)
:609: (85) call bpf_probe_read_user#112
:invalid access to map value, value_size=70 off=0 size=16383
:R1 min value is outside of the allowed memory range
:processed 7704 insns (limit 1000000) max_states_per_insn 1 total_states 336 peak_states 336 mark_read 253
解决方案

问题根源

  1. 目标地址错误:bpf_probe_read_user第一个参数传了&seventy_bytes_array,这是指向map元素指针的地址,而非map元素内部的data数组地址,导致写入越界。
  2. 读取长度约束不明确:验证器无法识别复杂三元表达式的约束,看到代码中存在读取16383字节的可能,但map的value仅70字节,判定存在越界风险。
  3. 冗余汇编操作:手动用汇编限制长度无必要,反而干扰验证器分析约束逻辑。

修复后的代码

#define MAX_MSG_SIZE 16383 
#define TRACE_PARENT_SIZE 70

SEC("kretprobe/sys_read")
int syscall__probe_ret_read(struct pt_regs *ctx)
{
    size_t buf_size = PT_REGS_RC(ctx);
    u64 id = bpf_get_current_pid_tgid();
    struct data_args_t *read_args = bpf_map_lookup_elem(&active_read_args_map, &id);

    if (read_args)
    {
        char *buf = read_args->buf;

        const u32 keyOne = 0;
        struct char_array_value *seventy_bytes_array = bpf_map_lookup_elem(&leftover_buf, &keyOne);

        // 拆分计算逻辑,明确长度约束
        unsigned int extra_bytes = buf_size > MAX_MSG_SIZE ? (buf_size - MAX_MSG_SIZE) : 0;
        unsigned int overrided_bytes = extra_bytes > TRACE_PARENT_SIZE ? TRACE_PARENT_SIZE : extra_bytes;

        if (overrided_bytes > 0 && seventy_bytes_array)
        {
             // 直接写入data数组,目标地址和长度约束清晰
             bpf_probe_read_user(seventy_bytes_array->data, overrided_bytes, buf + MAX_MSG_SIZE);
        }
    }

    return 0;
}

额外说明

  • 移除buf + MAX_MSG_SIZE的检查:当overrided_bytes > 0时,buf_size > MAX_MSG_SIZE成立,此时sys_read返回值有效,说明用户态缓冲区合法,无需额外检查。
  • 拆分长度计算逻辑:让约束关系更直观,帮助验证器识别长度不会超过map value的70字节限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:54:52