Since commit b254c6d816e5 ("bpf: Simulate branches to prune based on range violations"), we use range ill-formedness as a signal to detect dead branches. That logic was however not reflected in the reg_bounds selftests and can cause test case failures. For example, the "(s64)[0xffffffff00000002; 0] (u32) S64_MIN+1" test case currently fails with: 19: w0 = w6 ; R6=scalar(smin=0xffffffff00000002,smax=0) 20: w0 = w7 ; R7=0x8000000000000001 21: if w6 == w7 goto pc+3 21: R6=scalar(smin=0xffffffff00000002,smax=0) R7=0x8000000000000001 [...] ACTUAL FALSE1: scalar(u64=[0; U64_MAX],u32=[0; 4294967295],s64=[0xffffffff00000002; 0],s32=[S32_MIN; S32_MAX]) EXPECTED FALSE1: scalar(u64=[0; U64_MAX],u32=[0; 4294967295],s64=[0xffffffff00000002; 0],s32=[S32_MIN; S32_MAX]) ACTUAL FALSE2: scalar(u64=0x8000000000000001,u32=1,s64=S64_MIN+1,s32=0x1) EXPECTED FALSE2: scalar(u64=0x8000000000000001,u32=1,s64=S64_MIN+1,s32=0x1) ACTUAL TRUE1: EXPECTED TRUE1: scalar(u64=[0xffffffff00000002; 0x7fffffffffffffff],u32=[2147483648; 1],s64=[0xffffffff00000002; 0xffffffff00000001],s32=0x1) ACTUAL TRUE2: EXPECTED TRUE2: scalar(u64=0x8000000000000001,u32=1,s64=S64_MIN+1,s32=0x1) The verifier is able to prune the true branch, while reg_bounds tries to refine the true-branch values. This patch fixes it by marking branches as invalid in reg_bounds if their ranges are ill-formed. Fixes: b254c6d816e5 ("bpf: Simulate branches to prune based on range violations") Signed-off-by: Paul Chaignon --- .../selftests/bpf/prog_tests/reg_bounds.c | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c index 71f5240cc5b7..c0b3a357a0f4 100644 --- a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c +++ b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c @@ -761,6 +761,16 @@ static void print_refinement(enum num_t s_t, struct range src, print_range(d_t, new, "\n"); } +static bool is_valid_reg(struct reg_state *x) +{ + enum num_t t; + + for (t = first_t; t <= last_t; t++) + if (!is_valid_range(t, x->r[t])) + return false; + return true; +} + static void reg_state_refine(struct reg_state *r, enum num_t t, struct range x, const char *ctx) { enum num_t d_t, s_t; @@ -792,6 +802,9 @@ static void reg_state_refine(struct reg_state *r, enum num_t t, struct range x, } } + if (!is_valid_reg(r)) + return; + /* keep refining until we converge */ if (keep_going) { keep_going = false; @@ -837,6 +850,8 @@ static void reg_state_cond(enum num_t t, struct reg_state *x, struct reg_state * z2 = y->r[t]; range_cond(t, z1, z2, op, &z1, &z2); + if (!is_valid_range(t, z1) || !is_valid_range(t, z2)) + return; if (newx) { snprintf(buf, sizeof(buf), "%s R1", ctx); @@ -1401,12 +1416,14 @@ static void sim_case(enum num_t init_t, enum num_t cond_t, fr1->valid = fr2->valid = false; tr1->valid = tr2->valid = false; if (*branch_taken != 1) { /* FALSE is possible */ - fr1->valid = fr2->valid = true; reg_state_cond(cond_t, fr1, fr2, rev_op, fr1, fr2, "FALSE"); + if (is_valid_reg(fr1) && is_valid_reg(fr2)) + fr1->valid = fr2->valid = true; } if (*branch_taken != 0) { /* TRUE is possible */ - tr1->valid = tr2->valid = true; reg_state_cond(cond_t, tr1, tr2, op, tr1, tr2, "TRUE"); + if (is_valid_reg(tr1) && is_valid_reg(tr2)) + tr1->valid = tr2->valid = true; } if (env.verbosity >= VERBOSE_VERY) { printf("STEP3 (%s) FALSE R1:", t_str(cond_t)); print_reg_state(fr1, "\n"); -- 2.43.0