All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.