BPF List
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "bo cao" <treebacker@foxmail.com>, bpf@vger.kernel.org
Subject: Re: [PATCH] bpf, sockmap: Fix ring capacity invariant break in bpf_msg_push_data()
Date: Thu, 12 Nov 2026 03:42:06 +0000	[thread overview]
Message-ID: <sashiko-outbox-210291@kernel.org> (raw)
In-Reply-To: <tencent_84A432FD1AE7054D384BEB36958789AAFA05@qq.com>

Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [Low] bpf: non-standard multi-line comment style in
  bpf_msg_push_data()
- [Low] bpf, sockmap: inaccurate -ENOSPC claim and dead check in
  bpf_msg_push_data()
- [Low] bpf, sockmap: inaccurate Fixes trailer in bpf_msg_push_data()
  fix
- [Low] bpf: unreachable sg.end check in bpf_msg_push_data() copy path
--

commit a346141a5e25b55660858c935ff1dea8fc5392a6
Author: Bo Cao <treebacker@foxmail.com>

> bpf, sockmap: Fix ring capacity invariant break in bpf_msg_push_data()
>
> A push at msg->size ends the element walk on the ring's free slot, so
> the "no space, absorb by copy" fallback reads the free slot's length
> instead of the last live element's. Nothing is absorbed and the split
> path runs anyway: with the ring already holding MAX_MSG_FRAGS live
> elements it advances sg.end onto sg.start, all NR_MSG_FRAG_IDS slots
> become live, and the occupancy (the distance from sg.start to
> sg.end) reads 0 -- "empty". Every capacity check in push/pop is then
> bypassed and later transforms corrupt the element list.
>
> Without the fix, a single send() with an SK_MSG verdict program on a
> full ring deterministically crashes the SK_DROP teardown (arm64,
> 7.4.0-rc2 + the bpf master sockmap series):
>
>   Unable to handle kernel NULL pointer dereference at virtual address 0000000000000008
>   ESR = 0x0000000096000004  FSC = 0x04: level 0 translation fault
>   Internal error: Oops: 0000000096000004 [#1]  SMP
>   CPU: 2 UID: 0 PID: 546 Comm: skmsg_panic Tainted: G        W           7.4.0-rc2 #1 PREEMPT(full)
>   pc : sk_msg_free_elem+0x90/0x148
>   lr : __sk_msg_free+0x8c/0x10c
>   x14: 4141414141414141
>   Call trace:
>    sk_msg_free_elem+0x90/0x148
>    __sk_msg_free+0x8c/0x10c
>    sk_msg_free+0x18/0x24
>    tcp_bpf_sendmsg+0x298/0xa54
>    inet_sendmsg+0x44/0x70
>    __sys_sendto+0x120/0x198
>
> put_page() on a zeroed scatterlist entry. With the fix the helper
> returns -ENOSPC, the message frees normally, and the crash is gone.
>
> Never read the free slot in the fallback: absorb into the last live
> element when the walk ended on it and the ring is full, or fail with
> -ENOSPC if there is nothing to absorb, so the guard slot is never
> consumed.
>
> The bug was found with LLM-assisted source auditing and verified end
> to end: it is reachable with CAP_BPF/CAP_NET_ADMIN and confirmed
> exploitable for local privilege escalation (details reported to the
> maintainers separately). Regression tested with
> tools/testing/selftests/bpf/test_sockmap.
>
> Tested on top of v7.4-rc2 plus the sockmap fix series currently in
> bpf/bpf.git master (b889ea1f6dbe..0048086bd361); neither that series
> nor the June sk_msg fixes (f3f34ca45b96 and friends) touch the ring
> capacity invariant, and the patch applies to mainline and bpf master
> alike.
>
> Fixes: 6fff607e2f14 ("bpf: sk_msg program helper bpf_msg_push_data")
> Signed-off-by: Bo Cao <treebacker@foxmail.com>

[Severity: Low]
Prior to commit 15ab0548e310 ("bpf, sockmap: Several fixes to
bpf_msg_push_data"), bpf_msg_push_data() checked start >= offset + l after
the scatterlist walk and returned -EINVAL whenever the loop ended at
i == msg->sg.end on a non-empty ring.  Commit 15ab0548e310 ("bpf, sockmap:
Several fixes to bpf_msg_push_data") relaxed that check to allow pushing at
the end of a message and added the sk_msg_iter_var_prev(i) adjustment inside
if (copy), overlooking that copy = msg->sg.data[i].length was read earlier.
Should the Fixes: tag point to that commit instead?

  Fixes: 15ab0548e310 ("bpf, sockmap: Several fixes to bpf_msg_push_data")

> diff --git a/net/core/filter.c b/net/core/filter.c
> index d68b521504050..9ecb94a56d548 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
> @@ -2892,8 +2892,29 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start,
>  	 * offset case because its the common case for inserting a
>  	 * header.
>  	 */
> -	if (!space || (space == 1 && start != offset))
> -		copy = msg->sg.data[i].length;
> +	if (!space || (space == 1 && start != offset)) {
> +		/* A push at msg->size leaves the walk on the ring's free
> +		 * slot, which is not a live element: its length reads 0
> +		 * (or stale data left by an earlier ring rotation), so
> +		 * the copy fallback would absorb nothing and the split
> +		 * path below would run. With the ring already full
> +		 * (space == 0) that path advances sg.end onto sg.start,
> +		 * leaving all NR_MSG_FRAG_IDS slots live -- the ring can
> +		 * no longer tell "full" from "empty" and later
> +		 * transforms corrupt state. Absorb into the last live
> +		 * element instead, or fail if there is nothing to absorb.
> +		 */

[Severity: Low]
This isn't a bug, but BPF coding style places the opening /* of a multi-line
comment on its own line with the comment text starting on the next line.

> +		if (i == msg->sg.end) {
> +			if (!space) {
> +				sk_msg_iter_var_prev(i);
> +				copy = msg->sg.data[i].length;
> +				if (!copy)
> +					return -ENOSPC;
> +			}
> +		} else {
> +			copy = msg->sg.data[i].length;
> +		}
> +	}
>  
>  	if (unlikely(copy + len < copy))
>  		return -EINVAL;

[Severity: Low]
Can copy ever be zero here when !space holds, or is the commit message
inaccurate in stating that the helper returns -ENOSPC on a full ring?

Every live scatterlist element in [msg->sg.start, msg->sg.end) has a
non-zero length.  When !space holds, the ring has MAX_MSG_FRAGS live
elements, so stepping i back from msg->sg.end selects the last live element
with msg->sg.data[i].length > 0; bpf_msg_push_data() then absorbs the push
into a newly allocated page and returns 0 (or -ENOMEM) rather than -ENOSPC.

[Severity: Low]
Does stepping i back from msg->sg.end here make the second msg->sg.end check
inside if (copy) unreachable dead code?

net/core/filter.c:bpf_msg_push_data() {
    ...
	if (copy) {
		int front, back;

		raw = page_address(page);

		if (i == msg->sg.end)
			sk_msg_iter_var_prev(i);
    ...
}

copy is initialized to 0 and is only set to a non-zero value when
i != msg->sg.end or after the !space branch above has already decremented i
away from msg->sg.end, so i can never equal msg->sg.end when entering
if (copy).

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/tencent_84A432FD1AE7054D384BEB36958789AAFA05@qq.com?part=1

  reply	other threads:[~2026-11-12  3:42 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-12  3:13 [PATCH] bpf, sockmap: Fix ring capacity invariant break in bpf_msg_push_data() bo cao
2026-11-12  3:42 ` sashiko-bot [this message]
2026-11-12  4:20 ` 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-210291@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=treebacker@foxmail.com \
    /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