]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bpf: verifier: Make sync_linked_regs() scratch registers
authorPuranjay Mohan <puranjay@kernel.org>
Fri, 16 Jan 2026 14:14:35 +0000 (06:14 -0800)
committerAndrii Nakryiko <andrii@kernel.org>
Tue, 20 Jan 2026 19:24:41 +0000 (11:24 -0800)
sync_linked_regs() is called after a conditional jump to propagate new
bounds of a register to all its liked registers. But the verifier log
only prints the state of the register that is part of the conditional
jump.

Make sync_linked_regs() scratch the registers whose bounds have been
updated by propagation from a known register.

Before:

0: (85) call bpf_get_prandom_u32#7    ; R0=scalar()
1: (57) r0 &= 255                     ; R0=scalar(smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff))
2: (bf) r1 = r0                       ; R0=scalar(id=1,smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) R1=scalar(id=1,smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff))
3: (07) r1 += 4                       ; R1=scalar(id=1+4,smin=umin=smin32=umin32=4,smax=umax=smax32=umax32=259,var_off=(0x0; 0x1ff))
4: (a5) if r1 < 0xa goto pc+2         ; R1=scalar(id=1+4,smin=umin=smin32=umin32=10,smax=umax=smax32=umax32=259,var_off=(0x0; 0x1ff))
5: (35) if r0 >= 0x6 goto pc+1

After:

0: (85) call bpf_get_prandom_u32#7    ; R0=scalar()
1: (57) r0 &= 255                     ; R0=scalar(smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff))
2: (bf) r1 = r0                       ; R0=scalar(id=1,smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff)) R1=scalar(id=1,smin=smin32=0,smax=umax=smax32=umax32=255,var_off=(0x0; 0xff))
3: (07) r1 += 4                       ; R1=scalar(id=1+4,smin=umin=smin32=umin32=4,smax=umax=smax32=umax32=259,var_off=(0x0; 0x1ff))
4: (a5) if r1 < 0xa goto pc+2         ; R0=scalar(id=1+0,smin=umin=smin32=umin32=6,smax=umax=smax32=umax32=255) R1=scalar(id=1+4,smin=umin=smin32=umin32=10,smax=umax=smax32=umax32=259,var_off=(0x0; 0x1ff))
5: (35) if r0 >= 0x6 goto pc+1

The conditional jump in 4 updates the bound of R1 and the new bounds are
propogated to R0 as it is linked with the same id, before this change,
verifier only printed the state for R1 but after it prints for both R0
and R1.

Suggested-by: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Signed-off-by: Puranjay Mohan <puranjay@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/bpf/20260116141436.3715322-1-puranjay@kernel.org
kernel/bpf/verifier.c

index 9de0ec0c3ed998b0b135d85d64fe07ee0f8df6d5..76e02e13890f56f71f948cb8893dca20e8810e1b 100644 (file)
@@ -16846,8 +16846,8 @@ static void collect_linked_regs(struct bpf_verifier_state *vstate, u32 id,
 /* For all R in linked_regs, copy known_reg range into R
  * if R->id == known_reg->id.
  */
-static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_state *known_reg,
-                            struct linked_regs *linked_regs)
+static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_state *vstate,
+                            struct bpf_reg_state *known_reg, struct linked_regs *linked_regs)
 {
        struct bpf_reg_state fake_reg;
        struct bpf_reg_state *reg;
@@ -16890,6 +16890,10 @@ static void sync_linked_regs(struct bpf_verifier_state *vstate, struct bpf_reg_s
                        scalar_min_max_add(reg, &fake_reg);
                        reg->var_off = tnum_add(reg->var_off, fake_reg.var_off);
                }
+               if (e->is_reg)
+                       mark_reg_scratched(env, e->regno);
+               else
+                       mark_stack_slot_scratched(env, e->spi);
        }
 }
 
@@ -17076,13 +17080,15 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
        if (BPF_SRC(insn->code) == BPF_X &&
            src_reg->type == SCALAR_VALUE && src_reg->id &&
            !WARN_ON_ONCE(src_reg->id != other_branch_regs[insn->src_reg].id)) {
-               sync_linked_regs(this_branch, src_reg, &linked_regs);
-               sync_linked_regs(other_branch, &other_branch_regs[insn->src_reg], &linked_regs);
+               sync_linked_regs(env, this_branch, src_reg, &linked_regs);
+               sync_linked_regs(env, other_branch, &other_branch_regs[insn->src_reg],
+                                &linked_regs);
        }
        if (dst_reg->type == SCALAR_VALUE && dst_reg->id &&
            !WARN_ON_ONCE(dst_reg->id != other_branch_regs[insn->dst_reg].id)) {
-               sync_linked_regs(this_branch, dst_reg, &linked_regs);
-               sync_linked_regs(other_branch, &other_branch_regs[insn->dst_reg], &linked_regs);
+               sync_linked_regs(env, this_branch, dst_reg, &linked_regs);
+               sync_linked_regs(env, other_branch, &other_branch_regs[insn->dst_reg],
+                                &linked_regs);
        }
 
        /* if one pointer register is compared to another pointer