summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@kernel.org>2026-10-27 19:03:08 +0000
committerAlexei Starovoitov <ast@kernel.org>2026-10-28 04:07:03 +0000
commit1536e9b4375f235596ab5b7d6aa0fc4573965c4e (patch)
tree7b70ee7d35da43696000ce00c6b30951d3199838 /kernel
parent1b4ed112beb82a6adcd48a46fa8910c7184070e3 (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.c8
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);