BPF List
 help / color / mirror / Atom feed
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

  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