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 bpf-next 15/26] bpf: Recompute a pointer after the jump that bounds its index
Date: Mon, 09 Nov 2026 07:32:45 +0000	[thread overview]
Message-ID: <sashiko-outbox-204545@kernel.org> (raw)
In-Reply-To: <20261109064715.2382452-16-alexei.starovoitov@gmail.com>

Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] bpf: empty range accepted in recompute_ptr() and
  refine_linked_regs()
--

commit 409be5926da88fb4098b077eaa16b9ed30f13887
Author: Alexei Starovoitov <ast@kernel.org>

bpf: Recompute a pointer after the jump that bounds its index

This commit extends scalar evolution linked-register refinement at
conditional jumps to recompute pointers of the form base + index + delta
when the jump bounds the index register.

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index d980e2c608050..970211dfe38c1 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -19774,6 +19774,39 @@ static struct bpf_reg_state *scev_reg_for_fill(struct bpf_func_state *frame, u32
>  	return &ss->spilled_ptr;
>  }
>  
> +/*
> + *	p = buf + i; if (i > 100) return 0; *p = 0;
> + * p is @dst_reg, x is @ptr_reg, i is @off_reg. Scalar evolution says that p == x + i. So the insns
> + * 'p = x; p += i' are a no-op at run time, and a state that is sound before them is sound after
> + * them: apply their transfer function. Type, object and offset of p are taken from x as it is now;
> + * how p was computed does not matter.
> + * The old p is replaced, not intersected: a state does not distinguish two values of one hash map,
> + * and the intersection of an offset into the one with an offset into the other holds for neither.
> + * It is replaced only if the new range is a subset of the old one.
> + */
> +static bool recompute_ptr(struct bpf_verifier_env *env, struct bpf_reg_state *dst_reg,
> +			  const struct bpf_reg_state *ptr_reg, const struct bpf_reg_state *off_reg)
> +{
> +	struct bpf_reg_state sum = *ptr_reg;
> +
> +	/* the cpu can speculate past the jump with an out-of-bounds index */
> +	if (!env->bypass_spec_v1)
> +		return false;
> +	/* 'p += i' assigns a new id to a packet pointer */
> +	if (ptr_reg->type == SCALAR_VALUE || type_is_pkt_pointer(ptr_reg->type) ||
> +	    !bpf_is_widenable_reg_type(ptr_reg))
> +		return false;
> +	__mark_reg32_unbounded(&sum);
> +	scalar_step_add(&sum, ptr_reg, off_reg);
> +	sum.r64 = cnum64_add(ptr_reg->r64, off_reg->r64);
> +	sum.var_off = tnum_add(ptr_reg->var_off, off_reg->var_off);
> +	reg_bounds_sync(&sum);
> +	if (!bpf_reg_union_compatible(dst_reg, &sum) || !cnum64_is_subset(dst_reg->r64, sum.r64))
> +		return false;
> +	*dst_reg = sum;
> +	return true;
> +}
> +
>  /*
>   * The jump at @idx refined @regno. A register that scalar evolution links to it holds
>   * @regno + delta on every path to the jump: intersect the two. The intersection is empty: no
> @@ -19783,22 +19816,29 @@ static void refine_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier
>  			       int idx, u32 regno)
>  {
>  	struct bpf_func_state *frame = vstate->frame[vstate->curframe];
> -	struct bpf_reg_state *reg, implied, saved;
> +	struct bpf_reg_state *reg, *base_reg, implied, saved;
>  	u32 pos, linked_regno, fill_off, bits;
> +	int base_regno;
>  	s64 delta;
>  
>  	if (frame->regs[regno].type != SCALAR_VALUE)
>  		return;
>  	for (pos = 0;
> -	     bpf_scev_next_linked_reg(env, idx, regno, &pos, &linked_regno, &fill_off, &delta,
> -				      &bits);
> +	     bpf_scev_next_linked_reg(env, idx, regno, &pos, &linked_regno, &fill_off, &base_regno,
> +				      &delta, &bits);
>  	     pos++) {
>  		reg = scev_reg_for_fill(frame, linked_regno, fill_off, bits);
> -		if (!reg || reg->type != SCALAR_VALUE)
> +		if (!reg || (base_regno < 0 && reg->type != SCALAR_VALUE))
>  			continue;
>  
>  		implied = frame->regs[regno];
>  		add_scalar_to_reg(&implied, delta);
> +		if (base_regno >= 0) {
> +			base_reg = scev_reg_for_fill(frame, base_regno, 0, 64);
> +			if (base_reg && bits == 64 && recompute_ptr(env, reg, base_reg, &implied))
> +				goto scratch;
> +			continue;
> +		}
>  		if (regs_fit_in_bits(reg, &implied, bits))
>  			bits = 64;
>  		if (bits < 32)
> @@ -19811,6 +19851,7 @@ static void refine_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier
>  			*reg = saved;
>  			continue;
>  		}
> +scratch:
>  		if (linked_regno < MAX_BPF_REG)
>  			mark_reg_scratched(env, linked_regno);
>  		else

[Severity: Medium]
Can recompute_ptr() overwrite dst_reg with an empty or inconsistent range?

Unlike the scalar path in refine_linked_regs(), the base_regno >= 0 path
jumps to scratch without checking reg_is_empty(),
const_tnum_range_mismatch() or const_tnum_range_mismatch_32() on the new
state.  In recompute_ptr(), cnum64_is_subset(dst_reg->r64, sum.r64) does
not reject an empty range because cnum64_is_subset() returns true when the
smaller range is CNUM64_EMPTY:

kernel/bpf/cnum_defs.h:cnum64_is_subset() {
	if (FN(is_empty(smaller)))
		return true;
    ...
}

If the tested scalar register has a signed range crossing 0, known bits in
var_off, and a linear step > 1, adding delta in add_scalar_to_reg(&implied,
delta) or adding ptr_reg's offset in recompute_ptr() can shift the range so
that it no longer crosses 0, allowing __reg_bound_offset() and
__update_bounds_from_step() inside reg_bounds_sync() to narrow r64 to
CNUM64_EMPTY.  recompute_ptr() then writes that empty range into *dst_reg,
and any later ALU or jump instruction on dst_reg triggers verifier_bug() in
reg_bounds_sanity_check().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261109064715.2382452-1-alexei.starovoitov@gmail.com?part=15

  reply	other threads:[~2026-11-09  7:32 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-09  6:46 [PATCH bpf-next 00/26] bpf: Find linked registers by scalar evolution, remove scalar ids Alexei Starovoitov
2026-11-09  6:46 ` [PATCH bpf-next 01/26] bpf: Keep the 32-bit range across a no-op sign extension Alexei Starovoitov
2026-11-09  6:46 ` [PATCH bpf-next 02/26] bpf: Model 32-bit shifts and divisions exactly in scalar evolution Alexei Starovoitov
2026-11-09  6:46 ` [PATCH bpf-next 03/26] bpf: Construct scalar evolution expressions without allocations Alexei Starovoitov
2026-11-09  6:46 ` [PATCH bpf-next 04/26] bpf: Don't join dead registers in scalar evolution Alexei Starovoitov
2026-11-09  6:46 ` [PATCH bpf-next 05/26] bpf: Simplify scalar evolution expressions at construction Alexei Starovoitov
2026-11-09  6:46 ` [PATCH bpf-next 06/26] bpf: Give SSA names to the values that scalar evolution does not track Alexei Starovoitov
2026-11-09  7:45   ` bot+bpf-ci
2026-11-09  6:46 ` [PATCH bpf-next 07/26] bpf: Compute scalar evolution expressions for whole subprogs Alexei Starovoitov
2026-11-09  6:46 ` [PATCH bpf-next 08/26] bpf: Mark the attr argument of bpf_sys_bpf() as MEM_WRITE Alexei Starovoitov
2026-11-09  7:17   ` bot+bpf-ci
2026-11-09  6:46 ` [PATCH bpf-next 09/26] bpf: Check writes to the stack against bpf_may_write_mask() Alexei Starovoitov
2026-11-09  7:42   ` sashiko-bot
2026-11-09  6:46 ` [PATCH bpf-next 10/26] bpf: Detect a dead branch when a range and var_off don't overlap Alexei Starovoitov
2026-11-09  6:46 ` [PATCH bpf-next 11/26] bpf: Don't treat half of a 64-bit spill as a 32-bit spill on big endian Alexei Starovoitov
2026-11-09  6:46 ` [PATCH bpf-next 12/26] bpf: Refine registers linked by scalar evolution at conditional jumps Alexei Starovoitov
2026-11-09  7:41   ` sashiko-bot
2026-11-09  7:45   ` bot+bpf-ci
2026-11-09  6:46 ` [PATCH bpf-next 13/26] selftests/bpf: Test registers linked by scalar evolution Alexei Starovoitov
2026-11-09  6:46 ` [PATCH bpf-next 14/26] selftests/bpf: Test sign extensions of a range and of a linked register Alexei Starovoitov
2026-11-09  6:46 ` [PATCH bpf-next 15/26] bpf: Recompute a pointer after the jump that bounds its index Alexei Starovoitov
2026-11-09  7:32   ` sashiko-bot [this message]
2026-11-09  6:47 ` [PATCH bpf-next 16/26] selftests/bpf: Test a pointer computed before its index is bounded Alexei Starovoitov
2026-11-09  6:47 ` [PATCH bpf-next 17/26] bpf: Find a copy of an induction variable without scalar ids Alexei Starovoitov
2026-11-09  7:30   ` bot+bpf-ci
2026-11-09  6:47 ` [PATCH bpf-next 18/26] bpf: Find linked registers in the basic blocks of irreducible loops Alexei Starovoitov
2026-11-09  6:47 ` [PATCH bpf-next 19/26] bpf: Remove sync_linked_regs(), refine by scalar evolution only Alexei Starovoitov
2026-11-09  6:47 ` [PATCH bpf-next 20/26] bpf: Recognize copies of a scalar by their ranges in flow_join_makes_up() Alexei Starovoitov
2026-11-09  6:47 ` [PATCH bpf-next 21/26] bpf: Stop assigning ids to scalars Alexei Starovoitov
2026-11-09  7:08   ` sashiko-bot
2026-11-09  6:47 ` [PATCH bpf-next 22/26] bpf: Remove scalar ids from regsafe() Alexei Starovoitov
2026-11-09  7:06   ` sashiko-bot
2026-11-09  6:47 ` [PATCH bpf-next 23/26] bpf: Remove scalar ids from the join of states Alexei Starovoitov
2026-11-09  6:47 ` [PATCH bpf-next 24/26] bpf: Remove scalar ids from the log and from bpf_verifier.h Alexei Starovoitov
2026-11-09  6:47 ` [PATCH bpf-next 25/26] bpf: Support sign-extended 32-bit linear scalar evolution Alexei Starovoitov
2026-11-09  6:47 ` [PATCH bpf-next 26/26] selftests/bpf: Test loops with a sign-extended 32-bit induction variable Alexei Starovoitov
2026-11-09 18:16 ` [PATCH bpf-next 00/26] bpf: Find linked registers by scalar evolution, remove scalar ids Alexei Starovoitov

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