From: Vineet Gupta <vineet.gupta@linux.dev>
To: sashiko-reviews@lists.linux.dev
Cc: bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next v2 09/13] bpf: track low-32 scalar equality across narrowing stack fills
Date: Fri, 11 Sep 2026 12:13:33 +0530 [thread overview]
Message-ID: <1a84bd17-f673-4230-811b-e417ca58d62e@linux.dev> (raw)
In-Reply-To: <3f3e5658-4393-4530-8b3b-987fe20ddc95@linux.dev>
On 9/11/26 11:37 AM, Vineet Gupta wrote:
> On 9/10/26 10:34 PM, sashiko-bot@kernel.org wrote:
>> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>> - [Critical] Sign-extending loads incorrectly preserve conflicting SUBREG_ZEXT metadata.
>> --
>>
>> commit f939550a777ae3d2a7392dec744bffdc9fabcdda
>> Author: Vineet Gupta <vineet.gupta@linux.dev>
>>
>> bpf: track low-32 scalar equality across narrowing stack fills
>>
>> This commit records a low-32 link for stack fills narrower than the wider
>> source, allowing the verifier to deduce relations between 32-bit narrows
>> and their wider origins. It also adjusts check_mem_access() to not clear
>> the id of a ->subreg register on a sign-extending load.
>>
>>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>>> index 308ff53232f06..89be1240c99ae 100644
>>> --- a/kernel/bpf/verifier.c
>>> +++ b/kernel/bpf/verifier.c
>> [ ... ]
>>
>>> @@ -3968,18 +3969,34 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
>>>
>>> if (size <= spill_size &&
>>> bpf_stack_narrow_access_ok(off, size, spill_size)) {
>>> - if (env->bpf_capable && size == 4 && spill_size == 4 &&
>>> - get_reg_width(reg) <= 32)
>>> + bool narrowing = get_reg_width(reg) > size * BITS_PER_BYTE;
>>> + /*
>>> + * A narrowing fill keeps only the slot's low 32 bits,
>>> + * so record a low-32 link rather than dropping the
>>> + * relation, as a 32-bit mov from a wide source does.
>>> + * Which kind depends on how the load fills the high
>>> + * half, hence is_ldsx.
>>> + */
>>> + bool subreg_link = narrowing && size == 4;
>>> +
>>> + if (env->bpf_capable && size == 4 &&
>>> + (subreg_link || (spill_size == 4 && !narrowing)))
>>> /* Ensure stack slot has an ID to build a relation
>>> * with the destination register on fill.
>>> */
>>> assign_scalar_id_before_mov(env, reg);
>>> state->regs[dst_regno] = *reg;
>>>
>>> - /* Break the relation on a narrowing fill.
>>> - * coerce_reg_to_size will adjust the boundaries.
>>> - */
>>> - if (get_reg_width(reg) > size * BITS_PER_BYTE)
>>> + if (subreg_link && reg->id)
>>> + state->regs[dst_regno].subreg =
>>> + is_ldsx ? SUBREG_SEXT : SUBREG_ZEXT;
>>> + else if (narrowing)
>>> + /*
>>> + * Nothing to relate: either the slot has
>>> + * no id to share, or the fill is narrower
>>> + * than the 32 bits a link can describe.
>>> + * coerce_reg_to_size adjusts the bounds.
>>> + */
>>> clear_scalar_id(&state->regs[dst_regno]);
>> [Severity: Critical]
>> When narrowing is false, does this preserve a stale SUBREG_ZEXT property for
>> a sign-extending load?
>>
>> If a register with SUBREG_ZEXT is spilled to the stack and then read back
>> via a 4-byte sign-extending load, narrowing evaluates to false. This allows
>> the destination register to inherit the SUBREG_ZEXT property directly from
>> the stack slot, even though it is being sign-extended.
>>
>> [ ... ]
> Indeed if narrowing is false, wrong metadata can potentially be
> introduced, a comment to that effect is added here.
>
> /*
> * A fill that is not narrowing copies the slot's
> * kind verbatim, which a sign-extending load can
> * contradict. check_mem_access() runs after this
> * and drops any kind but SUBREG_SEXT once it knows
> * the extension was not a no-op.
> */
>
>
> See the second site below for actual fix...
>
>>> @@ -6736,13 +6753,15 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
>>> * Sign-extension can change the register value relative
>>> * to a scalar it is linked with by id (e.g. a zero-
>>> * extending fill of the same spilled stack slot), thus
>>> - * drop the shared id in that case.
>>> + * drop the shared id in that case. A ->subreg link is
>>> + * the exception: it already records that only the low
>>> + * 32 bits are shared, and how the high half follows.
>>> */
>>> bool no_sext = reg_umax(®s[value_regno]) <
>>> (1ULL << (size * BITS_PER_BYTE - 1));
>>>
>>> coerce_reg_to_size_sx(®s[value_regno], size);
>>> - if (!no_sext)
>>> + if (!no_sext && !regs[value_regno].subreg)
>>> clear_scalar_id(®s[value_regno]);
>> [Severity: Critical]
>> Does this check wrongly preserve the ID for SUBREG_ZEXT registers that
>> have just been sign-extended?
>>
>> Because SUBREG_ZEXT is non-zero, !regs[value_regno].subreg evaluates to
>> false, skipping the clear_scalar_id() call for a register that might hold
>> a sign-extended negative value.
>>
>> Later, if a conditional branch triggers sync_linked_regs(), it will see
>> SUBREG_ZEXT and invoke reconstruct_zext32(). This would forcefully set the
>> bounds to a zero-extended positive value.
>>
>> At runtime, the discrepancy between the verifier's zero-extended positive
>> bounds and the actual sign-extended negative value could bypass bounds checks,
>> potentially leading to out-of-bounds memory reads or writes.
>>
>> Should this explicitly check for SUBREG_SEXT rather than just allowing any
>> non-zero ->subreg value?
> Exactly that. And the first path leads here so the comment above suffices.
What I meant was first site has comment, and this site has the exact fix
which sashiko identified.
They are both part of the same bug.
Thx
next prev parent reply other threads:[~2026-09-11 6:43 UTC|newest]
Thread overview: 39+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-10 16:46 [PATCH bpf-next v2 00/13] bpf: track scalar equality across the low 32 bits Vineet Gupta
2026-09-10 16:46 ` [PATCH bpf-next v2 01/13] bpf: move linked-scalar flags out of bpf_reg_state->id [NFC] Vineet Gupta
2026-09-10 17:00 ` sashiko-bot
2026-09-11 6:56 ` Vineet Gupta
2026-09-10 17:52 ` bot+bpf-ci
2026-09-12 18:50 ` Alexei Starovoitov
2026-09-10 16:46 ` [PATCH bpf-next v2 02/13] bpf: compare linked-scalar kinds in regs_exact() Vineet Gupta
2026-09-12 18:51 ` Alexei Starovoitov
2026-09-10 16:46 ` [PATCH bpf-next v2 03/13] bpf: track low-32 scalar equality across zero-extending movs Vineet Gupta
2026-09-10 17:52 ` bot+bpf-ci
2026-09-11 9:29 ` Vineet Gupta
2026-09-12 18:59 ` Alexei Starovoitov
2026-09-10 16:46 ` [PATCH bpf-next v2 04/13] selftests/bpf: cover the low-32 link for " Vineet Gupta
2026-09-10 16:46 ` [PATCH bpf-next v2 05/13] bpf: keep the range across a sign extension that cannot change it Vineet Gupta
2026-09-10 17:08 ` sashiko-bot
2026-09-10 17:52 ` bot+bpf-ci
2026-09-11 10:37 ` Vineet Gupta
2026-09-12 19:02 ` Alexei Starovoitov
2026-09-10 16:46 ` [PATCH bpf-next v2 06/13] selftests/bpf: cover sign extensions that cannot change the range Vineet Gupta
2026-09-10 16:46 ` [PATCH bpf-next v2 07/13] bpf: track low-32 scalar equality across sign-extending movs Vineet Gupta
2026-09-10 17:52 ` bot+bpf-ci
2026-09-11 10:00 ` Vineet Gupta
2026-09-12 19:09 ` Alexei Starovoitov
2026-09-10 16:46 ` [PATCH bpf-next v2 08/13] selftests/bpf: cover the low-32 link for " Vineet Gupta
2026-09-10 17:52 ` bot+bpf-ci
2026-09-11 8:00 ` Vineet Gupta
2026-09-10 16:46 ` [PATCH bpf-next v2 09/13] bpf: track low-32 scalar equality across narrowing stack fills Vineet Gupta
2026-09-10 17:04 ` sashiko-bot
2026-09-11 6:07 ` Vineet Gupta
2026-09-11 6:43 ` Vineet Gupta [this message]
2026-09-10 16:46 ` [PATCH bpf-next v2 10/13] selftests/bpf: cover the low-32 link for " Vineet Gupta
2026-09-10 17:00 ` sashiko-bot
2026-09-11 5:34 ` Vineet Gupta
2026-09-10 17:31 ` bot+bpf-ci
2026-09-11 5:07 ` Vineet Gupta
2026-09-10 16:46 ` [PATCH bpf-next v2 11/13] bpf: record what a narrowing spill actually stores Vineet Gupta
2026-09-10 16:46 ` [PATCH bpf-next v2 12/13] bpf: track low-32 scalar equality across narrowing stack spills Vineet Gupta
2026-09-10 17:05 ` sashiko-bot
2026-09-10 16:46 ` [PATCH bpf-next v2 13/13] selftests/bpf: cover the low-32 link for " Vineet Gupta
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=1a84bd17-f673-4230-811b-e417ca58d62e@linux.dev \
--to=vineet.gupta@linux.dev \
--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.