bpf_probe_read_user()`权限被拒绝:ebpf 程序中对映射值的访问无效

问题描述 投票:0回答:1

我有以下结构:

struct ip_event_t {
    __u32 src_ip;
    __u32 dst_ip;
    __u16 src_port;
    __u16 dst_port;
    __u32 payload_length;
    char payload[MAX_PAYLOAD_LENGTH];
};

还有有问题的代码:

if (total_len + to_read > MAX_PAYLOAD_LENGTH) {
    to_read = 0;
    return -1;
}

if (bpf_probe_read_user(&event->payload[total_len], to_read, iov_entry.iov_base) < 0) {
    return -1;
}

验证者似乎无法理解绑定检查:

; if (total_len + to_read > MAX_PAYLOAD_LENGTH) {
191: (bf) r8 = r2                     ; frame1: R2_w=2000 R8_w=2000
192: (0f) r8 += r7                    ; frame1: R7=scalar(id=10,smin=smin32=0,smax=umax=smax32=umax32=1999,var_off=(0x0; 0x7ff)) R8_w=scalar(smin=umin=smin32=umin32=2000,smax=umax=smax32=umax32=3999,var_off=(0x0; 0xfff))
; if (total_len + to_read > MAX_PAYLOAD_LENGTH) {
193: (25) if r8 > 0x7d0 goto pc+88    ; frame1: R8_w=2000
; if (bpf_probe_read_user(&event->payload[total_len], to_read, iov_entry.iov_base) < 0) {
194: (bf) r1 = r6                     ; frame1: R1_w=map_value(map=ip_event_map,ks=4,vs=2016) R6=map_value(map=ip_event_map,ks=4,vs=2016)
195: (0f) r1 += r7                    ; frame1: R1_w=map_value(map=ip_event_map,ks=4,vs=2016,smin=smin32=0,smax=umax=smax32=umax32=1999,var_off=(0x0; 0x7ff)) R7=scalar(id=10,smin=smin32=0,smax=umax=smax32=umax32=1999,var_off=(0x0; 0x7ff))
196: (bf) r3 = r10                    ; frame1: R3_w=fp0 R10=fp0
197: (07) r3 += -56                   ; frame1: R3_w=fp-56
; if (bpf_probe_read_user(&event->payload[total_len], to_read, iov_entry.iov_base) < 0) {
198: (79) r3 = *(u64 *)(r3 +0)        ; frame1: R3_w=scalar() fp-56=mmmmmmmm
; if (bpf_probe_read_user(&event->payload[total_len], to_read, iov_entry.iov_base) < 0) {
199: (07) r1 += 16                    ; frame1: R1_w=map_value(map=ip_event_map,ks=4,vs=2016,off=16,smin=smin32=0,smax=umax=smax32=umax32=1999,var_off=(0x0; 0x7ff))
; if (bpf_probe_read_user(&event->payload[total_len], to_read, iov_entry.iov_base) < 0) {
200: (85) call bpf_probe_read_user#112
invalid access to map value, value_size=2016 off=2015 size=2000
R1 max value is outside of the allowed memory range
processed 278 insns (limit 1000000) max_states_per_insn 2 total_states 21 peak_states 21 mark_read 9
-- END PROG LOAD LOG --
libbpf: prog 'bpf_prog_tcp_sendmsg': failed to load: -13

让验证者了解写操作是安全的正确方法是什么?

ebpf
1个回答
0
投票

TL;DR. 您遇到了 Linux eBPF 验证器的限制,它无法跟踪变量之间的关系。一种解决方案可能是将

to_read
替换为常数。


验证器错误说明

invalid access to map value, value_size=2016 off=2015 size=2000
R1 max value is outside of the allowed memory range

您的程序被拒绝,因为验证程序检测到您可能使用

bpf_probe_read_user
读取超出了地图值范围。更具体地说,它检测到偏移量 2015(
total_len
的最大值 +
offsetof(ip_event_t, payload)
)处的潜在映射值访问,访问大小为 2000(
to_read
的值)。地图值的大小为 2016,因此超出范围。


为什么边界检查无效?

正如您所指出的,边界检查应该可以防止这种情况发生。然而,正如我们在验证器返回的字节码中看到的那样,边界检查发生在寄存器

r8
上(指令 191-193)。该寄存器保存
r2
(
to_read
) 和
r7
(
total_len
) 的加法。通过
bpf_probe_read_user
访问地图,但没有使用
r8
,而是直接使用
r2
r7

您可能想知道为什么验证者无法从

r2
边界检查中推断出
r7
r8
的信息。它可以意识到
r8 < Z
意味着
r2 + r7 < Z
。不幸的是,验证者无法跟踪变量之间的此类条件。跟踪变量之间的关系需要很高的成本,如 Windows eBPF 验证器所示,它确实支持它。


解决方案

我注意到

to_read
的值在运行时是恒定的(根据验证者)。一种解决方案可能是将
to_read
变量替换为常量保持值 2000。然后编译器将从字节码中优化
r2
,它可能足以通过验证程序。

© www.soinside.com 2019 - 2024. All rights reserved.