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: 6ccf49f793 ("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
This commit is contained in:
Alexei Starovoitov 2026-10-27 19:03:09 +00:00
parent 1536e9b437
commit 41b06cfd1c
No known key found for this signature in database

View File

@ -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)