diff options
| author | Alexei Starovoitov <ast@kernel.org> | 2026-10-27 19:03:08 +0000 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2026-10-28 04:07:03 +0000 |
| commit | 1536e9b4375f235596ab5b7d6aa0fc4573965c4e (patch) | |
| tree | 7b70ee7d35da43696000ce00c6b30951d3199838 /kernel | |
| parent | 1b4ed112beb82a6adcd48a46fa8910c7184070e3 (diff) | |
bpf: Don't clamp a register whose type changed in the loop
When a state returns to the head of a counted loop, bpf_clamp_scev_regs()
replaces the range of each register that changes by a constant step. The
new range is the range over all iterations. The function calculates it from
the register in the state that entered the loop. For a pointer this range
is the range of its offsets.
A call in the loop can invalidate the pointer. bpf_skb_pull_data() does it
to packet pointers, bpf_ringbuf_discard() does it to ringbuf memory. Then
the register is an unknown scalar. Scalar evolution does not see that, and
the clamp gives the offsets of the pointer to the scalar.
r6 = bpf_ringbuf_reserve() ; R6=ringbuf_mem
r7 = r6
1: if first iteration goto 2f
r1 = map_value
r1 += r6 ; accepted: R6 is 0 or 1
r0 = *(u8 *)(r1 + 0)
goto 3f
2: bpf_ringbuf_discard(r7) ; R6=scalar()
3: r6 += 1
r9 += 1
if r9 < 2 goto 1b ; clamp: R6=scalar(umax=1)
At run time r6 is the address of the ringbuf memory plus 1. The prog adds
it to a pointer to a map value, and the kernel crashes in the access.
Do not clamp a register that has a different type than at the entry of the
loop. Such a state is not within the state at the head. Thus the verifier
continues with it, and r6 stays an unknown scalar.
Fixes: 6ccf49f79327 ("bpf: Use SCEV to widen bounded loops")
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20261027190314.1440199-14-alexei.starovoitov@gmail.com
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/bpf/scev.c | 8 |
1 files changed, 8 insertions, 0 deletions
diff --git a/kernel/bpf/scev.c b/kernel/bpf/scev.c index 6231d16aa311..89f96682af3f 100644 --- a/kernel/bpf/scev.c +++ b/kernel/bpf/scev.c @@ -2897,6 +2897,14 @@ int bpf_clamp_scev_regs(struct bpf_verifier_env *env, struct bpf_func_state *cur is_unbound_scev_reg(env, entry_st, r)) continue; + /* + * A call in the loop can change the type: it invalidates packet pointers or + * releases a reference. The bounds are the offsets of the pointer at the entry. + * They say nothing about the value that is in the register now. + */ + if (reg->type != entry_reg->type) + continue; + linear_bounds(entry_reg, max_header_count, &coefs, &bounds); if (log->level & BPF_LOG_LEVEL2) { bpf_log(log, "loop header at %d, clamping ", insn_idx); |
