This patch fixes an inaccurate refinement in the verifier uncovered by selftest "(s64)[0x2; 0x100000000] (s32) S64_MIN": 19: w0 = w6 ; R6=scalar(smin=umin=2, ; smax=umax=0x100000000, ; var_off=(0x0; 0x1ffffffff)) 20: w0 = w7 ; R7=0x8000000000000000 21: if w6 s<= w7 goto pc+3 ; R6=scalar(smin=umin=smin32=umin32=1, ; smax=umax=umax32=0x7fffffff, ; var_off=(0x0; 0x7fffffff)) ; R7=0x8000000000000000 [...] ===================== ACTUAL FALSE1: scalar(u64=[1; 2147483647],u32=[1; 2147483647],s64=[0x1; 0x7fffffff],s32=[0x1; S32_MAX]) EXPECTED FALSE1: scalar(u64=[2; 2147483647],u32=[2; 2147483647],s64=[0x2; 0x7fffffff],s32=[0x2; S32_MAX]) [...] Before the conditional jump at instruction 21, R6 has a minimum 64bit value of 2. After the fallthrough, it's minimum value is 1; the 64bit ranges grew. That should never happen after a condition: we learn more information about the register so its ranges should only shrink. This is happening because of the last refinement in deduce_bounds_64_from_32(). Under specific conditions, we run reg->smin_value = reg->s32_min_value; reg->smax_value = reg->s32_max_value; reg->umin_value = reg->s32_min_value; reg->umax_value = reg->s32_max_value; The 64bit ranges are thus updated even if the new values are less precise. We should be using min()/max() instead, as done elsewhere. Instead of fixing this refinement logic, we can just remove it. Indeed, it's a special case of the new logic introduced above, in the same function, by the previous commit ("bpf: Improve 64bits bounds refinement from u32 bounds"). It corresponds to this case: u32: xx xx xx xx |---------------------------|---------------------------| u64: xxxx xxx ^ ^ (s64)S32_MAX (s64)S32_MIN Fixes: 9f5469b84577 ("bpf: Get better reg range with ldsx and 32bit compare") Signed-off-by: Paul Chaignon --- kernel/bpf/verifier.c | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index bcf8a38a4d72..7ce3d979cd6f 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -2318,44 +2318,6 @@ static void deduce_bounds_64_from_32(struct bpf_reg_state *reg) new_smax -= 0x100000000; reg->smax_value = new_smax; } - - /* Here we would like to handle a special case after sign extending load, - * when upper bits for a 64-bit range are all 1s or all 0s. - * - * Upper bits are all 1s when register is in a range: - * [0xffff_ffff_0000_0000, 0xffff_ffff_ffff_ffff] - * Upper bits are all 0s when register is in a range: - * [0x0000_0000_0000_0000, 0x0000_0000_ffff_ffff] - * Together this forms are continuous range: - * [0xffff_ffff_0000_0000, 0x0000_0000_ffff_ffff] - * - * Now, suppose that register range is in fact tighter: - * [0xffff_ffff_8000_0000, 0x0000_0000_ffff_ffff] (R) - * Also suppose that it's 32-bit range is positive, - * meaning that lower 32-bits of the full 64-bit register - * are in the range: - * [0x0000_0000, 0x7fff_ffff] (W) - * - * If this happens, then any value in a range: - * [0xffff_ffff_0000_0000, 0xffff_ffff_7fff_ffff] - * is smaller than a lowest bound of the range (R): - * 0xffff_ffff_8000_0000 - * which means that upper bits of the full 64-bit register - * can't be all 1s, when lower bits are in range (W). - * - * Note that: - * - 0xffff_ffff_8000_0000 == (s64)S32_MIN - * - 0x0000_0000_7fff_ffff == (s64)S32_MAX - * These relations are used in the conditions below. - */ - if (reg->s32_min_value >= 0 && reg->smin_value >= S32_MIN && reg->smax_value <= S32_MAX) { - reg->smin_value = reg->s32_min_value; - reg->smax_value = reg->s32_max_value; - reg->umin_value = reg->s32_min_value; - reg->umax_value = reg->s32_max_value; - reg->var_off = tnum_intersect(reg->var_off, - tnum_range(reg->smin_value, reg->smax_value)); - } } static void __reg_deduce_bounds(struct bpf_reg_state *reg) -- 2.43.0