我正在尝试从用户空间读取数据并将其存储在字符数组中。读取可能会触发多次,所以我在存储数据时记录了一个偏移量。我对读取长度和偏移量进行了边界检查,但验证器仍然抱怨“超出了允许的内存范围”。
看来在我的边界检查之后,验证者认为 len 和 offset 都在 [0,4096) 范围内。但条件语句
event->len + read < MAX_READ_CONTENT_LENGTH
对验证者来说没有任何意义,并且失败了。
以下是我的程序的一部分
struct ReadArgs{
int fd;
uintptr_t buf; // https://github.com/cilium/ebpf/discussions/1066 work around for pointer in struct
};
struct ReadEvent{
int eventType;
int fd;
int len;
u8 content[MAX_READ_CONTENT_LENGTH];
};
static __always_inline int readData(struct ReadArgs* args, struct ReadEvent* event, int read){
if((void *) args->buf == NULL){
return -1;
}
event->fd = args->fd;
if(event->len > MAX_READ_CONTENT_LENGTH){
return -1;
} else {
event->len &= (MAX_READ_CONTENT_LENGTH-1);
}
if(read > MAX_READ_CONTENT_LENGTH){
read = MAX_READ_CONTENT_LENGTH - 1;
}else{
read &= (MAX_READ_CONTENT_LENGTH-1);
}
if(event->len + read < MAX_READ_CONTENT_LENGTH) {
long res = bpf_probe_read_user(&event->content[event->len], read, (const void *) args->buf); // failed at here
if (res < 0) {
DEBUG("readData: bpf_probe_read_user return %d", res);
return -1;
}
event->len += read;
}
return 0;
}
和验证者日志
; if((void *) args->buf == NULL){
109: (79) r3 = *(u64 *)(r7 +8) ; R3_w=scalar() R7=map_value(off=0,ks=4,vs=16,imm=0)
; if((void *) args->buf == NULL){
110: (15) if r3 == 0x0 goto pc+64 ; R3_w=scalar()
; event->fd = args->fd;
111: (61) r1 = *(u32 *)(r7 +0) ; R1_w=scalar(umax=4294967295,var_off=(0x0; 0xffffffff)) R7=map_value(off=0,ks=4,vs=16,imm=0)
; event->fd = args->fd;
112: (63) *(u32 *)(r6 +4) = r1 ; R1_w=scalar(umax=4294967295,var_off=(0x0; 0xffffffff)) R6=map_value(off=0,ks=8,vs=4108,imm=0)
; if(event->len > MAX_READ_CONTENT_LENGTH){
113: (61) r2 = *(u32 *)(r6 +8) ; R2_w=scalar(umax=4294967295,var_off=(0x0; 0xffffffff)) R6=map_value(off=0,ks=8,vs=4108,imm=0)
114: (67) r2 <<= 32 ; R2_w=scalar(smax=9223372032559808512,umax=18446744069414584320,var_off=(0x0; 0xffffffff00000000),s32_min=0,s32_max=0,u32_max=0)
115: (c7) r2 s>>= 32 ; R2=scalar(smin=-2147483648,smax=2147483647)
; if(event->len > MAX_READ_CONTENT_LENGTH){
116: (65) if r2 s> 0x1000 goto pc+58 ; R2=scalar(smin=-2147483648,smax=4096)
; int retVal = ctx->ret;
117: (bf) r1 = r8 ; R1_w=scalar(id=1) R8=scalar(id=1)
118: (67) r1 <<= 32 ; R1_w=scalar(smax=9223372032559808512,umax=18446744069414584320,var_off=(0x0; 0xffffffff00000000),s32_min=0,s32_max=0,u32_max=0)
119: (77) r1 >>= 32 ; R1_w=scalar(umax=4294967295,var_off=(0x0; 0xffffffff))
120: (b7) r7 = 4095 ; R7_w=4095
; if(read > MAX_READ_CONTENT_LENGTH){
121: (25) if r1 > 0x1000 goto pc+2 ; R1_w=scalar(umax=4096,var_off=(0x0; 0x1fff))
122: (57) r8 &= 4095 ; R8_w=scalar(umax=4095,var_off=(0x0; 0xfff))
123: (bf) r7 = r8 ; R7=scalar(id=13,umax=4095,var_off=(0x0; 0xfff)) R8=scalar(id=13,umax=4095,var_off=(0x0; 0xfff))
; event->len &= (MAX_READ_CONTENT_LENGTH-1);
124: (57) r2 &= 4095 ; R2_w=scalar(umax=4095,var_off=(0x0; 0xfff))
125: (63) *(u32 *)(r6 +8) = r2 ; R2_w=scalar(umax=4095,var_off=(0x0; 0xfff)) R6=map_value(off=0,ks=8,vs=4108,imm=0)
; if(event->len + read < MAX_READ_CONTENT_LENGTH) {
126: (bf) r1 = r2 ; R1_w=scalar(id=14,umax=4095,var_off=(0x0; 0xfff)) R2_w=scalar(id=14,umax=4095,var_off=(0x0; 0xfff))
127: (0f) r1 += r7 ; R1_w=scalar(umax=8190,var_off=(0x0; 0x1fff)) R7=scalar(id=13,umax=4095,var_off=(0x0; 0xfff))
; if(event->len + read < MAX_READ_CONTENT_LENGTH) {
128: (25) if r1 > 0xfff goto pc+10 ; R1_w=scalar(umax=4095,var_off=(0x0; 0xfff))
; long res = bpf_probe_read_user(&event->content[event->len], read, (const void *) args->buf);
129: (bf) r1 = r6 ; R1_w=map_value(off=0,ks=8,vs=4108,imm=0) R6=map_value(off=0,ks=8,vs=4108,imm=0)
130: (0f) r1 += r2 ; R1_w=map_value(off=0,ks=8,vs=4108,umax=4095,var_off=(0x0; 0xfff)) R2_w=scalar(id=14,umax=4095,var_off=(0x0; 0xfff))
131: (07) r1 += 12 ; R1_w=map_value(off=12,ks=8,vs=4108,umax=4095,var_off=(0x0; 0xfff))
; long res = bpf_probe_read_user(&event->content[event->len], read, (const void *) args->buf);
132: (bf) r2 = r7 ; R2_w=scalar(id=13,umax=4095,var_off=(0x0; 0xfff)) R7=scalar(id=13,umax=4095,var_off=(0x0; 0xfff))
133: (85) call bpf_probe_read_user#112
invalid access to map value, value_size=4108 off=4107 size=4095
R1 max value is outside of the allowed memory range
processed 154 insns (limit 1000000) max_states_per_insn 0 total_states 12 peak_states 12 mark_read 4
我对这个问题进行了一些搜索,但找不到解决方案。
即使在访问之前进行了检查,对数据包的访问也无效 eBPF:即使进行边界检查,对映射值的访问也无效 这些问题的答案并没有解决我的问题。
我猜根本情况是验证者没有考虑到
event->len + read < MAX_READ_CONTENT_LENGTH
的情况。但我不知道如何解决它。
TL;DR. 验证者还不够聪明,无法使用
event->len + read < MAX_READ_CONTENT_LENGTH
。
为了让验证者确认
bpf_probe_read_user(&event->content[A], B, ...);
是安全的,需要记住 A + B < value_size
。这就是你想要保证的:
if (event->len + read < MAX_READ_CONTENT_LENGTH)
不幸的是,验证者目前无法理解和保留变量之间的这种关系(本例中为
A
和B
或event->len
和read
)。唯一的例外是在网络 BPF 程序中指向数据包末尾的指针 (ctx->data_end
) 的特殊情况。