google / ghost-userspace

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

BPF verifier rejects `biff.bpf.c`

sarsanaee opened this issue · comments

Hi everyone,

I'm testing BIFF scheduler with the latest open-sourced ghost kernel (the kernel supports version 75, and the user space is also 75). It seems there is an issue regarding the following function with R0 unbounded memory access, make sure to bounds check any such access:

static struct biff_bpf_sw_data *gtid_to_swd(u64 gtid)
{
	struct task_sw_info *swi;
	struct __sw_arr *__swa;
	u32 zero = 0;
	u32 idx;

	swi = bpf_map_lookup_elem(&sw_lookup, &gtid);
	if (!swi)
		return NULL;
	idx = swi->index;
	if (idx >= BIFF_MAX_GTIDS)
		return NULL;
	__swa = bpf_map_lookup_elem(&sw_data, &zero);
	if (!__swa)
		return NULL;
	return &__swa->e[idx];
}

I also include the BPF verifiers error here:

libbpf: elf: skipping unrecognized data section(7) .rodata.str1.1
libbpf: Error in bpf_create_map_xattr(global_rq):Invalid argument(-22). Retrying without BTF.
libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf:
; switch (msg->type) {
0: (69) r2 = *(u16 *)(r1 +40)
; switch (msg->type) {
1: (65) if r2 s> 0x45 goto pc+7
 R1=ctx(id=0,off=0,imm=0) R2_w=invP(id=0,umax_value=69,var_off=(0x0; 0x7f)) R10=fp0
2: (65) if r2 s> 0x42 goto pc+13
 R1=ctx(id=0,off=0,imm=0) R2_w=invP(id=0,umax_value=66,var_off=(0x0; 0x7f)) R10=fp0
3: (15) if r2 == 0x40 goto pc+24
 R1=ctx(id=0,off=0,imm=0) R2_w=invP(id=0,umax_value=66,var_off=(0x0; 0x7f)) R10=fp0
4: (15) if r2 == 0x41 goto pc+25

from 4 to 30: R1=ctx(id=0,off=0,imm=0) R2_w=invP65 R10=fp0
; handle_blocked(msg);
30: (85) call pc+244
caller:
 R10=fp0
callee:
 frame1: R1=ctx(id=0,off=0,imm=0) R2_w=invP65 R10=fp0
; static void __attribute__((noinline)) handle_blocked(struct bpf_ghost_msg *msg)
275: (bf) r6 = r1
; u64 gtid = blocked->gtid;
276: (79) r1 = *(u64 *)(r6 +0)
277: (7b) *(u64 *)(r10 -8) = r1
278: (b4) w1 = 0
; u32 zero = 0;
279: (63) *(u32 *)(r10 -12) = r1
280: (bf) r2 = r10
;
281: (07) r2 += -8
; swi = bpf_map_lookup_elem(&sw_lookup, &gtid);
282: (18) r1 = 0xffff9b2e53183800
284: (85) call bpf_map_lookup_elem#1
; if (!swi)
285: (15) if r0 == 0x0 goto pc+9
 frame1: R0=map_value(id=0,off=0,ks=8,vs=8,imm=0) R6=ctx(id=0,off=0,imm=0) R10=fp0 fp-8=mmmmmmmm fp-16=0000????
; idx = swi->index;
286: (61) r8 = *(u32 *)(r0 +4)
 frame1: R0=map_value(id=0,off=0,ks=8,vs=8,imm=0) R6=ctx(id=0,off=0,imm=0) R10=fp0 fp-8=mmmmmmmm fp-16=0000????
; if (idx >= BIFF_MAX_GTIDS)
287: (25) if r8 > 0xffff goto pc+7
 frame1: R0=map_value(id=0,off=0,ks=8,vs=8,imm=0) R6=ctx(id=0,off=0,imm=0) R8_w=invP(id=0,umax_value=65535,var_off=(0x0; 0xffff)) R10=fp0 fp-8=mmmmmmmm fp-16=0000????
288: (bf) r2 = r10
;
289: (07) r2 += -12
; __swa = bpf_map_lookup_elem(&sw_data, &zero);
290: (18) r1 = 0xffffb6cb8cdf1ef0
292: (85) call bpf_map_lookup_elem#1
293: (bf) r7 = r0
; if (!__swa)
294: (55) if r7 != 0x0 goto pc+1

from 294 to 296: frame1: R0=map_value(id=0,off=0,ks=4,vs=1572864,imm=0) R6=ctx(id=0,off=0,imm=0) R7=map_value(id=0,off=0,ks=4,vs=1572864,imm=0) R8=invP(id=0,umax_value=65535,var_off=(0x0; 0xffff)) R10=fp0 fp-8=mmmmmmmm fp-16=mmmm????
; swd->ran_until = bpf_ktime_get_us();
296: (27) r8 *= 24
297: (0f) r7 += r8
; return bpf_ktime_get_ns() / 1000;
298: (85) call bpf_ktime_get_ns#5
; return bpf_ktime_get_ns() / 1000;
299: (37) r0 /= 1000
; swd->ran_until = bpf_ktime_get_us();
300: (7b) *(u64 *)(r7 +8) = r0
 frame1: R0_w=invP(id=0) R6=ctx(id=0,off=0,imm=0) R7_w=map_value(id=0,off=0,ks=4,vs=1572864,umax_value=1572840,var_off=(0x0; 0x1ffff8),s32_max_value=2097144,u32_max_value=2097144) R8_w=invP(id=0,umax_value=1572840,var_off=(0x0; 0x1ffff8)) R10=fp0 fp-8=mmmmmmmm fp-16=mmmm????
; task_stopped(blocked->cpu);
301: (61) r6 = *(u32 *)(r6 +24)
302: (b4) w1 = 0
; u32 zero = 0;
303: (63) *(u32 *)(r10 -8) = r1
304: (bf) r2 = r10
;
305: (07) r2 += -8
; __ca = bpf_map_lookup_elem(&cpu_data, &zero);
306: (18) r1 = 0xffffb6cb8b619ef0
308: (85) call bpf_map_lookup_elem#1
; if (!__ca)
309: (15) if r0 == 0x0 goto pc+5
 frame1: R0=map_value(id=0,off=0,ks=4,vs=65536,imm=0) R6=invP(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R7=map_value(id=0,off=0,ks=4,vs=1572864,umax_value=1572840,var_off=(0x0; 0x1ffff8),s32_max_value=2097144,u32_max_value=2097144) R8=invP(id=0,umax_value=1572840,var_off=(0x0; 0x1ffff8)) R10=fp0 fp-8=mmmmmmmm fp-16=mmmm????
; if (cpu >= BIFF_MAX_CPUS)
310: (bf) r1 = r6
311: (67) r1 <<= 32
312: (77) r1 >>= 32
313: (b7) r2 = 1024
314: (2d) if r2 > r1 goto pc+1

from 314 to 316: frame1: R0=map_value(id=0,off=0,ks=4,vs=65536,imm=0) R1_w=invP(id=0,umax_value=1023,var_off=(0x0; 0x3ff)) R2_w=invP1024 R6=invP(id=9,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R7=map_value(id=0,off=0,ks=4,vs=1572864,umax_value=1572840,var_off=(0x0; 0x1ffff8),s32_max_value=2097144,u32_max_value=2097144) R8=invP(id=0,umax_value=1572840,var_off=(0x0; 0x1ffff8)) R10=fp0 fp-8=mmmmmmmm fp-16=mmmm????
; return &__ca->e[cpu];
316: (bc) w1 = w6
; pcpu->current = 0;
317: (67) r1 <<= 6
318: (0f) r0 += r1
319: (b7) r1 = 0
; pcpu->current = 0;
320: (7b) *(u64 *)(r0 +0) = r1
 frame1: R0_w=map_value(id=0,off=0,ks=4,vs=65536,umax_value=274877906880,var_off=(0x0; 0x3fffffffc0),s32_max_value=2147483584,u32_max_value=-64) R1_w=invP0 R2_w=invP1024 R6=invP(id=9,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R7=map_value(id=0,off=0,ks=4,vs=1572864,umax_value=1572840,var_off=(0x0; 0x1ffff8),s32_max_value=2097144,u32_max_value=2097144) R8=invP(id=0,umax_value=1572840,var_off=(0x0; 0x1ffff8)) R10=fp0 fp-8=mmmmmmmm fp-16=mmmm????
R0 unbounded memory access, make sure to bounds check any such access
processed 123 insns (limit 1000000) max_states_per_insn 0 total_states 9 peak_states 9 mark_read 2

Thanks,
Alireza

commented

short version - try this:

idx = swi->index;
BPF_MUST_CHECK(idx);
if (idx >= BIFF_MAX_GTIDS)
		return NULL;

if that doesn't work, move the idx check to right above the indexing (return &__swa->e[idx];)

if that doesn't work, try:
idx = BPF_MUST_CHECK(idx)

it's a pain. the compiler is likely optimizing out some checks since it knows idx is in bounds. but the verifier doesn't know. BPF_MUST_CHECK is some inline asm that tries to defeat the compiler, but it's a little spotty.

Hi Barret,

Thank you for the suggestions. I think the code just works, I had some preconfigured clang flags when compiling BPF code to support atomics in the BIFF code. Those were causing these issues.

I played with the BPF_MUST_CHECK macro and enabled and disabled it at different places then checked the BPF asm. It looks like it is able to trick the compiler.

Alireza

commented

i ended up biting the bullet and writing the inline assembly to do the check. that commit will eventually make its way to this repo, pending review, etc. in the meantime, you can try this:

#define BOUNDED_ARRAY_IDX(arr, arr_sz, idx) ({                          \
        typeof(&arr[0]) ___arr = arr;                                   \
        u64 ___idx = idx;                                               \
        if (___arr) {                                                   \
                asm volatile("if %[__idx] > %[__bound] goto 1f; \
                              %[__idx] *= %[__size];            \
                              %[__arr] += %[__idx];             \
                              goto 2f;                          \
                              1:;                               \
                              %[__arr] = 0;                     \
                              2:                                \
                              "                                         \
                             : [__arr]"+r"(___arr), [__idx]"+r"(___idx) \
                             : [__bound]"i"((arr_sz) - 1),              \
                               [__size]"i"(sizeof(typeof(arr[0])))      \
                             : "cc");                                   \
        }                                                               \
        ___arr;                                                         \
})