From: Josef Bacik In the branch of a BPF_JSET which is taken, the verifier can learn something about the register only if the mask has a single bit: then this bit is set. regs_refine_cond_op() tests this with is_power_of_2(val) and then ORs val into var_off. val is a u64, but is_power_of_2() takes an unsigned long. On a 32-bit kernel it only sees the lower half of the mask. For if r1 & 0xffffffff80000000 goto l which is a BPF_JSET | BPF_K with an immediate of 0x80000000, the lower half is a power of two. So the verifier takes all 33 bits for set at l, while the branch is taken if any of them is. A mask of 0x100000001 from a register does the same for bit 0. The log of an i386 kernel, for r6 = bpf_get_prandom_u32() << 31: 3: (45) if r6 & 0x80000000 goto pc+2 from 3 to 6: R6=0xffffffff80000000 R10=fp0 6: (77) r6 >>= 63 ; R6=1 When the program runs, r6 >> 63 is 0. From then on the verifier works with known bits which the register does not have at run time. It accepts, for example, an offset into a map value which it derives from these bits and believes to be constant, and the program can access memory outside of the map value. Count the bits of the whole value. Only 32-bit kernels are affected. On a 64-bit kernel unsigned long holds the whole mask, and this patch changes nothing. Fixes: 960ea056561a ("bpf: verifier: teach the verifier to reason about the BPF_JSET instruction") Signed-off-by: Josef Bacik Signed-off-by: Alexei Starovoitov --- kernel/bpf/verifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 430d5309eb5c..d0ba7156ba4f 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -18145,7 +18145,7 @@ static void regs_refine_cond_op(struct bpf_reg_state *reg1, struct bpf_reg_state * this restriction. I.e., !(r1 & 0x3) means neither bit 0 nor * bit 1 is set, which we can readily use in adjustments. */ - if (!is_power_of_2(val)) + if (hweight64(val) != 1) break; if (is_jmp32) { t = tnum_or(tnum_subreg(reg1->var_off), tnum_const(val)); -- 2.55.0