mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
bpf: Look at all 64 bits of a BPF_JSET mask on 32-bit
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: 960ea05656 ("bpf: verifier: teach the verifier to reason about the BPF_JSET instruction")
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20261017163745.3341787-8-alexei.starovoitov@gmail.com
This commit is contained in:
parent
c6dc0a288d
commit
ee4f44e0e3
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user