diff options
| author | Alexei Starovoitov <ast@kernel.org> | 2026-10-27 19:03:09 +0000 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2026-10-28 04:07:03 +0000 |
| commit | 41b06cfd1ceaf10e382b8f2d2d720e3e0abae10b (patch) | |
| tree | fe31c4a2f745d2b579c41cdb21b174459b2cad3a | |
| parent | 1536e9b4375f235596ab5b7d6aa0fc4573965c4e (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.c | 7 |
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) |
