From: Alexei Starovoitov <ast@xxxxxxxxxx> Remove i = zero workaround, improve arena based tests, add asm test for this_branch_reg->id == other_branch_reg->id condition. Signed-off-by: Alexei Starovoitov <ast@xxxxxxxxxx> --- .../testing/selftests/bpf/progs/arena_htab.c | 16 ++++++++-- tools/testing/selftests/bpf/progs/iters.c | 14 +-------- .../bpf/progs/verifier_iterating_callbacks.c | 18 ++++++------ .../selftests/bpf/progs/verifier_reg_equal.c | 29 +++++++++++++++++++ 4 files changed, 52 insertions(+), 25 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/arena_htab.c b/tools/testing/selftests/bpf/progs/arena_htab.c index 1e6ac187a6a0..e669db468c5a 100644 --- a/tools/testing/selftests/bpf/progs/arena_htab.c +++ b/tools/testing/selftests/bpf/progs/arena_htab.c @@ -18,25 +18,35 @@ void __arena *htab_for_user; bool skip = false; int zero = 0; +char __arena arr1[100000]; /* works */ +char arr2[1000]; /* ok for small sizes */ SEC("syscall") int arena_htab_llvm(void *ctx) { #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) || defined(BPF_ARENA_FORCE_ASM) struct htab __arena *htab; + char __arena *arr = arr1; __u64 i; htab = bpf_alloc(sizeof(*htab)); cast_kern(htab); htab_init(htab); + cast_kern(arr); + /* first run. No old elems in the table */ - for (i = zero; i < 1000; i++) + for (i = 0; i < 100000 && can_loop; i++) { htab_update_elem(htab, i, i); + arr[i] = i; + } - /* should replace all elems with new ones */ - for (i = zero; i < 1000; i++) + /* should replace some elems with new ones */ + for (i = 0; i < 1000 && can_loop; i++) { htab_update_elem(htab, i, i); + /* Access mem to make the verifier use bounded loop logic */ + arr2[i] = i; + } cast_user(htab); htab_for_user = htab; #else diff --git a/tools/testing/selftests/bpf/progs/iters.c b/tools/testing/selftests/bpf/progs/iters.c index fe65e0952a1e..1a5adffae5d3 100644 --- a/tools/testing/selftests/bpf/progs/iters.c +++ b/tools/testing/selftests/bpf/progs/iters.c @@ -291,10 +291,7 @@ int iter_obfuscate_counter(const void *ctx) { struct bpf_iter_num it; int *v, sum = 0; - /* Make i's initial value unknowable for verifier to prevent it from - * pruning if/else branch inside the loop body and marking i as precise. - */ - int i = zero; + int i = 0; MY_PID_GUARD(); @@ -304,15 +301,6 @@ int iter_obfuscate_counter(const void *ctx) i += 1; - /* If we initialized i as `int i = 0;` above, verifier would - * track that i becomes 1 on first iteration after increment - * above, and here verifier would eagerly prune else branch - * and mark i as precise, ruining open-coded iterator logic - * completely, as each next iteration would have a different - * *precise* value of i, and thus there would be no - * convergence of state. This would result in reaching maximum - * instruction limit, no matter what the limit is. - */ if (i == 1) x = 123; else diff --git a/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c b/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c index bd676d7e615f..b2159d9cd4ad 100644 --- a/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c +++ b/tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c @@ -308,7 +308,6 @@ int iter_limit_bug(struct __sk_buff *skb) } #define ARR_SZ 1000000 -int zero; char arr[ARR_SZ]; SEC("socket") @@ -318,9 +317,10 @@ int cond_break1(const void *ctx) unsigned long i; unsigned int sum = 0; - for (i = zero; i < ARR_SZ && can_loop; i++) + for (i = 0; i < ARR_SZ && can_loop; i++) sum += i; - for (i = zero; i < ARR_SZ; i++) { + + for (i = 0; i < ARR_SZ; i++) { barrier_var(i); sum += i + arr[i]; cond_break; @@ -336,8 +336,8 @@ int cond_break2(const void *ctx) int i, j; int sum = 0; - for (i = zero; i < 1000 && can_loop; i++) - for (j = zero; j < 1000; j++) { + for (i = 0; i < 1000 && can_loop; i++) + for (j = 0; j < 1000; j++) { sum += i + j; cond_break; } @@ -348,7 +348,7 @@ static __noinline int loop(void) { int i, sum = 0; - for (i = zero; i <= 1000000 && can_loop; i++) + for (i = 0; i <= 1000000 && can_loop; i++) sum += i; return sum; @@ -365,7 +365,7 @@ SEC("socket") __success __retval(1) int cond_break4(const void *ctx) { - int cnt = zero; + int cnt = 0; for (;;) { /* should eventually break out of the loop */ @@ -378,7 +378,7 @@ int cond_break4(const void *ctx) static __noinline int static_subprog(void) { - int cnt = zero; + int cnt = 0; for (;;) { cond_break; @@ -392,7 +392,7 @@ SEC("socket") __success __retval(1) int cond_break5(const void *ctx) { - int cnt1 = zero, cnt2; + int cnt1 = 0, cnt2; for (;;) { cond_break; diff --git a/tools/testing/selftests/bpf/progs/verifier_reg_equal.c b/tools/testing/selftests/bpf/progs/verifier_reg_equal.c index dc1d8c30fb0e..bca474a5e7b6 100644 --- a/tools/testing/selftests/bpf/progs/verifier_reg_equal.c +++ b/tools/testing/selftests/bpf/progs/verifier_reg_equal.c @@ -55,4 +55,33 @@ l1_%=: exit; \ : __clobber_all); } +/* + * The tests checks that the verifier doesn't WARN_ON in: + * if (dst_reg->type == SCALAR_VALUE && dst_reg->id && + * !WARN_ON_ONCE(dst_reg->id != other_dst_reg->id)) { + */ +SEC("socket") +__description("check this_branch_reg->id == other_branch_reg->id") +__success +__naked void reg_id(void) +{ + asm volatile (" \ + call %[bpf_ktime_get_ns]; \ + 1:.byte 0xe5; /* may_goto */ \ + .byte 0; \ + .long ((l0_%= - 1b - 8) / 8) & 0xffff; \ + .short 0; \ + r0 &= 1; \ + r2 = r0; \ + /* is_branch_taken will predict fallthrough */ \ + if r2 == 2 goto l0_%=; \ + r0 = 0; \ + exit; \ +l0_%=: r0 = 0; \ + exit; \ +" : + : __imm(bpf_ktime_get_ns) + : __clobber_all); +} + char _license[] SEC("license") = "GPL"; -- 2.43.0