summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@kernel.org>2026-10-27 19:03:09 +0000
committerAlexei Starovoitov <ast@kernel.org>2026-10-28 04:07:03 +0000
commit41b06cfd1ceaf10e382b8f2d2d720e3e0abae10b (patch)
treefe31c4a2f745d2b579c41cdb21b174459b2cad3a
parent1536e9b4375f235596ab5b7d6aa0fc4573965c4e (diff)
bpf: Reset the range of a packet pointer that scalar evolution clamps
A compare with pkt_end sets 'range' in all packet pointers that have the same id. Then 'range - offset' bytes from the pointer are in the packet. When a state returns to the head of a counted loop, bpf_clamp_scev_regs() gives a new id to a packet pointer, and the offsets of all iterations. It does not change the range. But the range is valid only together with the old id and the old offsets. 1: ; R6=pkt(id=1,r=0,off=0..100) r5 = r6 r5 += 100 if r5 > pkt_end goto 2f ; R6=pkt(id=1,r=200,off=0..100) r6 += 100 ; R6=pkt(id=1,r=200,off=100..200) r9 += 1 if r9 < 2 goto 1b ; clamp: R6=pkt(id=2,r=200,off=0..100) after bytes from r6 that the verifier accepts ---------------------- --------------------------------------- the compare 100, correct r6 += 100 0, correct the clamp 100, incorrect. Nothing compared this r6 Usually the verifier prunes the state at the head, and the range has no effect. The verifier continues with the state when a different part of it is not within the state at the head. A flag in a 32-bit spill is sufficient. Then the prog reads and writes after pkt_end. A write to skb_shared_info crashes the kernel. Take the range that the pointer has at the entry of the loop. No compare in the loop found this range, so it is valid in all iterations. The state at the head has the same range, so the verifier prunes as before. Fixes: 6ccf49f79327 ("bpf: Use SCEV to widen bounded loops") Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261027190314.1440199-15-alexei.starovoitov@gmail.com
-rw-r--r--kernel/bpf/scev.c7
1 files changed, 7 insertions, 0 deletions
diff --git a/kernel/bpf/scev.c b/kernel/bpf/scev.c
index 89f96682af3f..7b82fd79f9cc 100644
--- a/kernel/bpf/scev.c
+++ b/kernel/bpf/scev.c
@@ -2914,6 +2914,13 @@ int bpf_clamp_scev_regs(struct bpf_verifier_env *env, struct bpf_func_state *cur
bpf_log(log, "\n");
}
scratch_widened_reg_id(env, reg);
+ /*
+ * The range of a packet pointer goes together with its id and with the offsets
+ * that it had in the compare with pkt_end. Both are replaced here. What holds for
+ * the new offsets is the range at the entry.
+ */
+ if (type_is_pkt_pointer(reg->type))
+ reg->range = max(entry_reg->range, 0);
err = bpf_set_reg_range(env, reg, bounds.r64, bounds.r32,
bounds.base, bounds.step);
if (err)