From: sashiko-bot@kernel.org
To: "Vineet Gupta" <vineet.gupta@linux.dev>
Cc: bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next v3 04/12] bpf: Track low-32 scalar equality across zero-extending movs
Date: Tue, 20 Oct 2026 16:39:57 +0000 [thread overview]
Message-ID: <sashiko-outbox-179367@kernel.org> (raw)
In-Reply-To: <20261020161907.2115379-5-vineet.gupta@linux.dev>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] bpf: empty or corrupted range after intersection in sync_linked_regs()
--
commit 05d23d7192de1c6009703f8fbe1537ad78dda89e
Author: Vineet Gupta <vineet.gupta@linux.dev>
bpf: Track low-32 scalar equality across zero-extending movs
This commit extends linked-scalar tracking across zero-extending 32-bit moves
from wide 64-bit source registers using BPF_REG_LINK_ZEXT. Because a wide base
cannot be inverted across all 64 bits, sync_linked_regs() propagates bounds
forward only and intersects the derived bounds with the destination register's
existing state.
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index b9c20a5cc6d6f..6afd718ede647 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
[ ... ]
> @@ -18749,6 +18798,27 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s
> if (reg->link_kind == BPF_REG_LINK_ZEXT ||
> known_reg->link_kind == BPF_REG_LINK_ZEXT)
> zext_32_to_64(reg);
> + /*
> + * Intersect rather than overwrite. A BPF_REG_LINK_ZEXT
> + * link over a wide base does not propagate back, so the
> + * base can be wider than what @reg has already proved
> + * about itself; replacing it would lose that.
> + */
> + cnum64_intersect_with(®->r64, prev.r64);
> + cnum32_intersect_with(®->r32, prev.r32);
> + reg->var_off = tnum_intersect(reg->var_off, prev.var_off);
> + /*
> + * An empty intersection means the two sides contradict,
> + * so this path is unreachable. Say nothing rather than
> + * say it with an empty range: reg_bounds_sync() below
> + * refuses to touch one, and the first later
> + * reg_bounds_sanity_check() would report it as a
> + * verifier bug, which it is not. Keeping the wider
> + * pre-intersect state only costs precision on a path
> + * that cannot be taken.
> + */
> + if (range_bounds_violation(reg))
> + *reg = prev;
> reg_bounds_sync(reg);
> }
> if (e->is_reg)
[Severity: Medium]
Can a contradiction in var_off or linear step still leave an empty range on
reg, or corrupt reg into a bogus constant, after sync_linked_regs() finishes?
First, tnum_intersect() in kernel/bpf/tnum.c assumes its arguments overlap:
/* Note that if a and b disagree - i.e. one has a 'known 1' where the other has
* a 'known 0' - this will return a 'known 1' for that bit.
*/
struct tnum tnum_intersect(struct tnum a, struct tnum b)
{
u64 v, mu;
v = a.value | b.value;
mu = a.mask & b.mask;
return TNUM(v & ~mu, mu);
}
When reg->var_off and prev.var_off disagree on a known bit without
tnum_overlap(reg->var_off, prev.var_off) being checked first, tnum_intersect()
turns the conflicting bit into a known 1.
Second, range_bounds_violation(reg) only checks cnum32_is_empty(reg->r32) ||
cnum64_is_empty(reg->r64), and is only called before reg_bounds_sync(reg). Any
contradiction between the intersected r64/r32 ranges and var_off (via
__update_reg_bounds()) or base/step (via __update_bounds_from_step()) happens
inside reg_bounds_sync(reg), after the range_bounds_violation(reg) check has
already passed.
For example, in a socket filter:
w7 = w6; /* r6 is unknown 64-bit; r7 gets BPF_REG_LINK_ZEXT */
if w7 < 4 goto out;
if w7 > 5 goto out;
if w7 & 2 goto out; /* r7: r64=[4, 5], var_off=(0x4; 0x1) */
if r6 & 2 goto l1;
At l1, r6 has var_off=(0x2; ~2ULL), so after zext_32_to_64(reg), reg has
r64=[2, U32_MAX] and var_off=(0x2; 0xfffffffd). Intersecting r64 and r32 with
prev produces [4, 5], which is non-empty, so range_bounds_violation(reg) is
false. However, tnum_intersect() produces (0x6; 0x1) ([6, 7]), and
reg_bounds_sync(reg) then intersects [4, 5] with [6, 7], leaving reg->r64 and
reg->r32 empty and triggering a verifier warning on a subsequent r7 %= r9 (with
r9 = 0).
Worse, if w7 was narrowed to a single constant 4 (r64=[4, 4],
var_off=(0x4; 0x0)) before "if r6 & 2 goto l1", tnum_intersect() fabricates
the constant var_off=(0x6; 0x0). Inside
reg_bounds_sync()->__update_reg_bounds()->__update_reg64_bounds():
cnum64_intersect_with(®->r64, cnum64_from_tnum(reg->var_off));
/* Check if u64 and tnum overlap in a single value */
tnum_next = tnum_step(reg->var_off, reg_umin(reg));
umin_in_tnum = (reg_umin(reg) & ~reg->var_off.mask) == reg->var_off.value;
tmax = reg->var_off.value | reg->var_off.mask;
if (umin_in_tnum && tnum_next > reg_umax(reg)) {
___mark_reg_known(reg, reg_umin(reg));
} else if (!umin_in_tnum && tnum_next == tmax) {
___mark_reg_known(reg, tmax);
}
cnum64_intersect_with() sets reg->r64 to CNUM64_EMPTY, where reg_umin(reg)
returns 0 and reg_umax(reg) returns U64_MAX. That satisfies !umin_in_tnum &&
tnum_next == tmax (6 == 6) and calls ___mark_reg_known(reg, 6), overwriting
r7 == 4 with the contradicted constant r7 == 6.
Even when tnum_overlap(reg->var_off, prev.var_off) holds, reg_bounds_sync(reg)
can still empty r64/r32 if the intersected cnum range and intersected tnum (or
base/step in __update_bounds_from_step()) have no common points.
Should sync_linked_regs() verify tnum_overlap(reg->var_off, prev.var_off)
before calling tnum_intersect(), and also check range_bounds_violation(reg)
after reg_bounds_sync(reg) to restore *reg = prev?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261020161907.2115379-1-vineet.gupta@linux.dev?part=4
next prev parent reply other threads:[~2026-10-20 16:40 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-20 16:18 [PATCH bpf-next v3 00/12] bpf: track scalar equality across the low 32 bits Vineet Gupta
2026-10-20 16:18 ` [PATCH bpf-next v3 01/12] bpf: Keep the 32-bit range across a sign extension that cannot change it Vineet Gupta
2026-10-20 16:18 ` [PATCH bpf-next v3 02/12] selftests/bpf: Cover sign extensions that cannot change the range Vineet Gupta
2026-10-20 16:18 ` [PATCH bpf-next v3 03/12] bpf: Prepare linked scalars for relations narrower than 64 bits Vineet Gupta
2026-10-20 16:18 ` [PATCH bpf-next v3 04/12] bpf: Track low-32 scalar equality across zero-extending movs Vineet Gupta
2026-10-20 16:39 ` sashiko-bot [this message]
2026-10-20 16:19 ` [PATCH bpf-next v3 05/12] selftests/bpf: Cover the low-32 link for " Vineet Gupta
2026-10-20 16:53 ` bot+bpf-ci
2026-10-20 16:19 ` [PATCH bpf-next v3 06/12] bpf: Track low-32 scalar equality across sign-extending movs Vineet Gupta
2026-10-20 16:19 ` [PATCH bpf-next v3 07/12] selftests/bpf: Cover the low-32 link for " Vineet Gupta
2026-10-20 16:19 ` [PATCH bpf-next v3 08/12] bpf: Recover the low 32 bits on the reverse link direction Vineet Gupta
2026-10-20 16:19 ` [PATCH bpf-next v3 09/12] bpf: Track low-32 scalar equality across narrowing stack fills Vineet Gupta
2026-10-20 16:19 ` [PATCH bpf-next v3 10/12] selftests/bpf: Cover the low-32 link for " Vineet Gupta
2026-10-20 16:29 ` sashiko-bot
2026-10-20 16:52 ` bot+bpf-ci
2026-10-20 16:19 ` [PATCH bpf-next v3 11/12] [do not merge] bpf: Track low-32 scalar equality across narrowing stack spills Vineet Gupta
2026-10-20 16:42 ` sashiko-bot
2026-10-20 16:19 ` [PATCH bpf-next v3 12/12] [do not merge] selftests/bpf: Cover the low-32 link for " Vineet Gupta
2026-10-20 16:32 ` sashiko-bot
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-179367@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=vineet.gupta@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