From: sashiko-bot@kernel.org
To: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>, bpf@vger.kernel.org
Subject: Re: [PATCH v2 bpf 14/19] bpf: Reset the range of a packet pointer that scalar evolution clamps
Date: Tue, 27 Oct 2026 19:30:46 +0000 [thread overview]
Message-ID: <sashiko-outbox-187068@kernel.org> (raw)
In-Reply-To: <20261027190314.1440199-15-alexei.starovoitov@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] bpf: out-of-bounds packet access via step tightening in bpf_clamp_scev_regs()
- [Medium] bpf: bpf_widen_scev_regs() does not reset negative packet pointer range
--
commit a6747880bf7fa92bdac4e2fa9d3223b8203c86f7
Author: Alexei Starovoitov <ast@kernel.org>
bpf: Reset the range of a packet pointer that scalar evolution clamps
This patch resets a clamped packet pointer's range to max(entry_reg->range, 0)
in bpf_clamp_scev_regs() so that packet bounds gained from pkt_end comparisons
inside a counted loop do not remain attached to the widened offset range across
all iterations.
> diff --git a/kernel/bpf/scev.c b/kernel/bpf/scev.c
> index 89f96682af3ff..7b82fd79f9cc9 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);
[Severity: Medium]
Should bpf_widen_scev_regs() also clamp reg->range with max(reg->range, 0)
when widening a packet pointer at loop entry?
When a packet pointer fails a pkt_end comparison before a counted loop,
mark_pkt_end() sets entry_reg->range to AT_PKT_END (-1) or BEYOND_PKT_END (-2).
At loop entry, bpf_widen_scev_regs() replaces the packet pointer's id and
offsets across all iterations without clearing a negative reg->range:
kernel/bpf/scev.c:bpf_widen_scev_regs() {
...
scratch_widened_reg_id(env, reg);
err = bpf_set_reg_range(env, reg, bounds.r64, bounds.r32,
bounds.base, bounds.step);
...
}
If the loop has a negative slope that moves the pointer backwards into the
packet on subsequent iterations, is_pkt_ptr_branch_taken() in
kernel/bpf/verifier.c will still fold pkt_end comparisons on the widened
register using the stale negative range marker.
In addition, when the backedge reaches the loop header, bpf_clamp_scev_regs()
sets rcur->range to 0 via max(entry_reg->range, 0) while the widened header
state still has rold->range < 0, causing regsafe() in kernel/bpf/states.c to
reject state pruning at the loop header:
kernel/bpf/states.c:regsafe() {
...
if (rold->range < 0 || rcur->range < 0) {
/* special case for [BEYOND|AT]_PKT_END */
if (rold->range != rcur->range)
return false;
}
...
}
> err = bpf_set_reg_range(env, reg, bounds.r64, bounds.r32,
> bounds.base, bounds.step);
[Severity: Critical]
Can preserving entry_reg->range across bpf_set_reg_range() here (and in
bpf_widen_scev_regs()) allow out-of-bounds packet reads and writes past
pkt_end when bounds.step tightens reg_umax(reg)?
In find_good_pkt_pointers(), comparing a derived packet pointer against
pkt_end sets reg->range = reg_umax(dst_reg), and check_packet_access() ->
check_mem_region_access() allows accesses of size bytes when
reg_umax(reg) + off + size <= reg->range. As seen in commit b88822d2584f
("bpf: Fix packet range of pointers sharing an id"), tightening the umax of a
packet pointer via var_off alignment while keeping its range is unsafe.
Before a counted loop, a packet pointer can have an unaligned umax paired with
an aligned var_off and step == 1:
r4 &= 0x38
if r4 > 50 goto exit ; r4: r64=[0, 50], var_off=(0x0; 0x38), step=1
r6 = pkt_data
r6 += r4 ; r6: pkt(id=1, r=0, r64=[0, 50], var_off=(0x0; 0x38), step=1)
r5 = r6
r5 += 8 ; r5: pkt(id=1, r=0, r64=[8, 58], var_off=(0x0; 0x78), step=1)
if r5 > pkt_end goto exit ; r6: pkt(id=1, r=58, r64=[0, 50], var_off=(0x0; 0x38), step=1)
When entering or clamping a 2-iteration loop (max_header_count = 2) that
increments r6 by 8 (slope = 8), linear_bounds() in kernel/bpf/scev.c computes
bounds.r64 = [0, 58] and promotes tnum_alignment(entry_reg->var_off) into
bounds.step:
kernel/bpf/scev.c:linear_bounds() {
...
if (cnum64_is_const(reg->r64) && !overflow) {
out->step = slope_abs;
out->base = imod(reg_smin(reg), out->step);
} else {
out->step = 1u << min_t(u32, tnum_alignment(reg->var_off), __ffs(slope_abs));
out->base = 0;
}
}
Here bounds.step becomes 8. Calling bpf_set_reg_range() -> reg_bounds_sync()
-> __update_bounds_from_step() -> cnum64_intersect_linear() then rounds
reg_umax(reg) down from 58 to 56 while reg->range remains 58 (and similarly
rounds [0, 50] down to [0, 48] with range = 50 for a negative slope r6 -= 8).
At runtime, if r4 == 48 and pkt_len == 56, on the second iteration r6 reaches
pkt_data + 56 == pkt_end, but check_packet_access() will permit a 2-byte
access at r6 + 0 because reg_umax(reg) + 2 <= 58.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261027190314.1440199-1-alexei.starovoitov@gmail.com?part=14
next prev parent reply other threads:[~2026-10-27 19:30 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 19:02 [PATCH v2 bpf 00/19] bpf: Remove BPF_F_ARENA_SCALAR, fix scalar evolution and scalar forks Alexei Starovoitov
2026-10-27 19:02 ` [PATCH v2 bpf 01/19] selftests/bpf: Remove tests for global data in arena Alexei Starovoitov
2026-10-27 19:02 ` [PATCH v2 bpf 02/19] libbpf: Remove .arena.data Alexei Starovoitov
2026-10-27 19:02 ` [PATCH v2 bpf 03/19] libbpf: Keep format strings of bpf_printk() in .rodata Alexei Starovoitov
2026-10-27 19:02 ` [PATCH v2 bpf 04/19] selftests/bpf: Remove tests for arena access through numbers Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 05/19] bpf: Remove BPF_F_ARENA_SCALAR Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 06/19] bpf, x86: Remove r0_on_stack from fetching atomics in arena Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 07/19] bpf: Clear delta of a register that scalar evolution widens Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 08/19] selftests/bpf: Test that a widened register is not linked Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 09/19] selftests/bpf: Wait for the last maps of map_kptr to be freed Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 10/19] selftests/bpf: Avoid a page fault in rcu_reentry while the handler is armed Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 11/19] bpf: Don't widen a pointer to a buffer in scalar evolution Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 12/19] selftests/bpf: Test a loop that moves a pointer to a buffer Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 13/19] bpf: Don't clamp a register whose type changed in the loop Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 14/19] bpf: Reset the range of a packet pointer that scalar evolution clamps Alexei Starovoitov
2026-10-27 19:30 ` sashiko-bot [this message]
2026-10-27 20:09 ` Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 15/19] selftests/bpf: Test the clamp of pointers in scalar evolution Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 16/19] bpf: Finish the insn in the state that maybe_fork_scalars() splits off Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 17/19] selftests/bpf: Test maybe_fork_scalars() at the head of a counted loop Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 18/19] bpf: Keep stack arguments in arch_bpf_timed_may_goto() Alexei Starovoitov
2026-10-27 19:48 ` bot+bpf-ci
2026-10-28 4:53 ` Alexei Starovoitov
2026-10-27 19:03 ` [PATCH v2 bpf 19/19] selftests/bpf: Test stack arguments across may_goto Alexei Starovoitov
2026-10-28 4:10 ` [PATCH v2 bpf 00/19] bpf: Remove BPF_F_ARENA_SCALAR, fix scalar evolution and scalar forks patchwork-bot+netdevbpf
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-187068@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=alexei.starovoitov@gmail.com \
--cc=bpf@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox