* [PATCH] af_unix: fix u->inq_len accounting in manage_oob() and unix_stream_read_skb()
@ 2026-09-19 22:17 Hui Peng
2026-09-20 19:27 ` Kuniyuki Iwashima
2026-09-20 23:19 ` [PATCH] af_unix: fix u->inq_len accounting in manage_oob() and unix_stream_read_skb() netdev-bot+sashiko
0 siblings, 2 replies; 5+ messages in thread
From: Hui Peng @ 2026-09-19 22:17 UTC (permalink / raw)
To: kuniyu, davem, edumazet, kuba, pabeni, horms; +Cc: netdev, linux-kernel
When an old OOB skb is replaced and discarded in manage_oob(), or when a
fully consumed OOB skb is skipped in unix_stream_read_skb(), the
discarded byte count is not subtracted from u->inq_len (or is subtracted
inconsistently), causing SIOCINQ / FIONREAD to report stale positive
byte counts on an empty AF_UNIX stream socket. Properly account
u->inq_len when dropping or skipping consumed OOB skbs.
Fixes: 314001f0bf92 ("af_unix: Add OOB support")
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 42cffeafc8c1..1770af3c2684 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -2849,6 +2849,7 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sk_buff **last,
WRITE_ONCE(u->oob_skb, NULL);
if (!sock_flag(sk, SOCK_URGINLINE)) {
+ WRITE_ONCE(u->inq_len, u->inq_len - 1);
__skb_unlink(skb, &sk->sk_receive_queue);
unread_skb = skb;
skb = skb_peek(&sk->sk_receive_queue);
@@ -2884,6 +2885,7 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor)
return err;
mutex_lock(&u->iolock);
+again:
spin_lock(&queue->lock);
skb = __skb_dequeue(queue);
@@ -2893,6 +2895,12 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor)
return -EAGAIN;
}
+ if (!unix_skb_len(skb)) {
+ spin_unlock(&queue->lock);
+ consume_skb(skb);
+ goto again;
+ }
+
WRITE_ONCE(u->inq_len, u->inq_len - unix_skb_len(skb));
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
@@ -2908,6 +2916,11 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor)
spin_unlock(&queue->lock);
+ if (UNIXCB(skb).consumed) {
+ skb_pull(skb, UNIXCB(skb).consumed);
+ UNIXCB(skb).consumed = 0;
+ }
+
unix_orphan_scm(sk, skb);
mutex_unlock(&u->iolock);
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH] af_unix: fix u->inq_len accounting in manage_oob() and unix_stream_read_skb() 2026-09-19 22:17 [PATCH] af_unix: fix u->inq_len accounting in manage_oob() and unix_stream_read_skb() Hui Peng @ 2026-09-20 19:27 ` Kuniyuki Iwashima 2026-09-21 5:48 ` [PATCH net v2 1/2] af_unix: decrement u->inq_len when skipping unread OOB skb in manage_oob() Hui Peng 2026-09-20 23:19 ` [PATCH] af_unix: fix u->inq_len accounting in manage_oob() and unix_stream_read_skb() netdev-bot+sashiko 1 sibling, 1 reply; 5+ messages in thread From: Kuniyuki Iwashima @ 2026-09-20 19:27 UTC (permalink / raw) To: Hui Peng; +Cc: davem, edumazet, kuba, pabeni, horms, netdev, linux-kernel On Sat, Sep 19, 2026 at 3:17 PM Hui Peng <benquike@gmail.com> wrote: > > When an old OOB skb is replaced and discarded in manage_oob(), or when a > fully consumed OOB skb is skipped in unix_stream_read_skb(), the > discarded byte count is not subtracted from u->inq_len (or is subtracted > inconsistently), causing SIOCINQ / FIONREAD to report stale positive > byte counts on an empty AF_UNIX stream socket. Properly account > u->inq_len when dropping or skipping consumed OOB skbs. > > Fixes: 314001f0bf92 ("af_unix: Add OOB support") > Assisted-by: LLM Please tell your LLM not to mix different fixes into one. btw I'm tempted to remove OOB support entirely. --- pw-bot: cr ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v2 1/2] af_unix: decrement u->inq_len when skipping unread OOB skb in manage_oob() 2026-09-20 19:27 ` Kuniyuki Iwashima @ 2026-09-21 5:48 ` Hui Peng 2026-09-21 5:48 ` [PATCH net v2 2/2] af_unix: skip consumed OOB skb and pull consumed bytes in unix_stream_read_skb() Hui Peng 0 siblings, 1 reply; 5+ messages in thread From: Hui Peng @ 2026-09-21 5:48 UTC (permalink / raw) To: kuniyu, davem, edumazet, kuba, pabeni Cc: horms, willemb, mhal, jakub, netdev, linux-kernel, stable, benquike When a normal in-band read on an AF_UNIX stream socket (without MSG_PEEK and without SO_OOBINLINE) encounters an unread u->oob_skb in manage_oob(), manage_oob() clears u->oob_skb, unlinks the 1-byte skb from sk->sk_receive_queue, and drops it with SKB_DROP_REASON_UNIX_SKIP_OOB without decrementing u->inq_len. Because queue_oob() incremented u->inq_len by 1 when queuing the OOB skb, u->inq_len remains permanently inflated by 1 byte for each skipped unread OOB skb, causing SIOCINQ / FIONREAD to report a stale positive byte count on an empty socket. Decrement u->inq_len by 1 when unlinking the unread OOB skb in manage_oob(). Tested in QEMU against Linux 7.3.0-rc3 by sending three 1-byte MSG_OOB packets interleaved with normal stream data on an AF_UNIX SOCK_STREAM socketpair and draining all in-band data via recv(): on the unfixed kernel, ioctl(SIOCINQ) reports 3 on the empty socket; with this patch applied, ioctl(SIOCINQ) reports 0. Fixes: f4e1fb04c123 ("af_unix: Use cached value for SOCK_STREAM in unix_inq_len().") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Hui Peng <benquike@gmail.com> --- Changes in v2: - Split the manage_oob() and unix_stream_read_skb() fixes into a 2-patch series as requested by Kuniyuki Iwashima. - Update Fixes: tag to f4e1fb04c123 ("af_unix: Use cached value for SOCK_STREAM in unix_inq_len().") and clarify the commit message as noted by Sashiko. net/unix/af_unix.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 42cffeafc8c1..1770af3c2684 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2849,6 +2849,7 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sk_buff **last, WRITE_ONCE(u->oob_skb, NULL); if (!sock_flag(sk, SOCK_URGINLINE)) { + WRITE_ONCE(u->inq_len, u->inq_len - 1); __skb_unlink(skb, &sk->sk_receive_queue); unread_skb = skb; skb = skb_peek(&sk->sk_receive_queue); -- 2.49.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v2 2/2] af_unix: skip consumed OOB skb and pull consumed bytes in unix_stream_read_skb() 2026-09-21 5:48 ` [PATCH net v2 1/2] af_unix: decrement u->inq_len when skipping unread OOB skb in manage_oob() Hui Peng @ 2026-09-21 5:48 ` Hui Peng 0 siblings, 0 replies; 5+ messages in thread From: Hui Peng @ 2026-09-21 5:48 UTC (permalink / raw) To: kuniyu, davem, edumazet, kuba, pabeni Cc: horms, willemb, mhal, jakub, netdev, linux-kernel, stable, benquike When an OOB byte is consumed via recv(MSG_OOB) in unix_stream_recv_urg(), u->oob_skb is cleared to NULL and UNIXCB(oob_skb).consumed is incremented to 1, but oob_skb remains on sk->sk_receive_queue (with unix_skb_len(skb) == 0) to preserve the OOB mark until normal reads advance past it. Similarly, a partial recv() in unix_stream_read_generic() advances UNIXCB(skb).consumed without pulling the skb header and leaves the partially consumed skb at the head of sk->sk_receive_queue. If the socket is subsequently read via unix_stream_read_skb() (used by BPF sockmap), unix_stream_read_skb() only checks skb == u->oob_skb (which is only true for an unconsumed OOB skb) and ignores UNIXCB(skb).consumed. As a result, a consumed OOB skb (unix_skb_len(skb) == 0) is handed to recv_actor() and re-delivers the already consumed OOB byte, and a partially consumed skb re-delivers its already consumed prefix. In unix_stream_read_skb(), skip and free zero-length consumed skbs (after calling unix_orphan_scm() so SCM_RIGHTS fd accounting remains balanced) and pull UNIXCB(skb).consumed bytes via pskb_pull() (which safely handles both linear and non-linear paged skbs) before invoking recv_actor(). Tested in QEMU against Linux 7.3.0-rc3 with BPF_MAP_TYPE_SOCKMAP and a BPF_SK_SKB_STREAM_VERDICT program: 1. Consuming a 1-byte MSG_OOB packet ("Z") before inserting the socket into sockmap and sending "HELLO" re-delivers "ZHELLO" on the unfixed kernel, whereas with this patch applied recv() receives "HELLO!". 2. Consuming a 3-byte prefix ("123") of "12345678" before inserting the socket into sockmap re-delivers "12345678" on the unfixed kernel, whereas with this patch applied recv() receives "45678". Fixes: 77462de14a43 ("af_unix: Add read_sock for stream socket types") Fixes: 314001f0bf92 ("af_unix: Add OOB support") Fixes: 638f32604385 ("af_unix: Disable MSG_OOB handling for sockets in sockmap/sockhash") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Hui Peng <benquike@gmail.com> --- Changes in v2: - Split out from the manage_oob() fix into patch 2/2 as requested by Kuniyuki Iwashima. - Call unix_orphan_scm(sk, skb) before consume_skb(skb) when dropping a zero-length consumed skb so u->scm_stat.nr_fds is decremented, and use pskb_pull() instead of skb_pull() to safely handle non-linear paged skbs without hitting BUG() in __skb_pull(), as noted by Sashiko. net/unix/af_unix.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 1770af3c2684..f5d64e7b5c1c 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2885,6 +2885,7 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor) return err; mutex_lock(&u->iolock); +again: spin_lock(&queue->lock); skb = __skb_dequeue(queue); @@ -2894,6 +2895,13 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor) return -EAGAIN; } + if (!unix_skb_len(skb)) { + spin_unlock(&queue->lock); + unix_orphan_scm(sk, skb); + consume_skb(skb); + goto again; + } + WRITE_ONCE(u->inq_len, u->inq_len - unix_skb_len(skb)); #if IS_ENABLED(CONFIG_AF_UNIX_OOB) @@ -2913,6 +2921,14 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor) mutex_unlock(&u->iolock); + if (UNIXCB(skb).consumed) { + if (!pskb_pull(skb, UNIXCB(skb).consumed)) { + kfree_skb(skb); + return -ENOMEM; + } + UNIXCB(skb).consumed = 0; + } + return recv_actor(sk, skb); } -- 2.49.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] af_unix: fix u->inq_len accounting in manage_oob() and unix_stream_read_skb() 2026-09-19 22:17 [PATCH] af_unix: fix u->inq_len accounting in manage_oob() and unix_stream_read_skb() Hui Peng 2026-09-20 19:27 ` Kuniyuki Iwashima @ 2026-09-20 23:19 ` netdev-bot+sashiko 1 sibling, 0 replies; 5+ messages in thread From: netdev-bot+sashiko @ 2026-09-20 23:19 UTC (permalink / raw) To: benquike Cc: kuniyu, davem, edumazet, kuba, pabeni, horms, netdev, linux-kernel Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider. Critical: 1 · High: 0 · Medium: 3 · Low: 0 If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough). - [Critical] Kernel BUG() (oops/panic) in the sockmap read path: the newly added `skb_pull(skb, UNIXCB(skb).consumed)` in… - [Medium] Commit message does not match the code, and two of the three hunks are unrelated to inq_len accounting (scope creep). - [Medium] Incorrect Fixes tag / wrong bug attribution. - [Medium] Unbalanced SCM_RIGHTS accounting on the new early-drop path in `unix_stream_read_skb()`: the added block frees the dequeued skb with… -- af_unix: fix u->inq_len accounting in manage_oob() and unix_stream_read_skb() This adds a u->inq_len decrement when manage_oob() discards a replaced OOB skb, makes unix_stream_read_skb() skip and free fully consumed skbs, and pulls the already consumed prefix out of the skb before handing it to the recv_actor. No Link: tags are present in the commit header. A few questions on the changelog and on the two new blocks in unix_stream_read_skb(). > When an old OOB skb is replaced and discarded in manage_oob(), or when a > fully consumed OOB skb is skipped in unix_stream_read_skb(), the > discarded byte count is not subtracted from u->inq_len (or is subtracted > inconsistently), causing SIOCINQ / FIONREAD to report stale positive > byte counts on an empty AF_UNIX stream socket. [Severity: Medium] Is the unix_stream_read_skb() half of this description accurate? unix_skb_len() is: static unsigned int unix_skb_len(const struct sk_buff *skb) { return skb->len - UNIXCB(skb).consumed; } so a fully consumed skb contributes 0 bytes, and the pre-existing subtraction already in that function: WRITE_ONCE(u->inq_len, u->inq_len - unix_skb_len(skb)); subtracts exactly 0 for it. The bytes were already removed either by the partial read in unix_stream_read_generic(): WRITE_ONCE(u->inq_len, u->inq_len - chunk); or by unix_stream_recv_urg(): WRITE_ONCE(u->inq_len, u->inq_len - 1); Can a fully consumed skb still hold un-accounted bytes in this path? The new !unix_skb_len(skb) block contains no inq_len update at all, and the new skb_pull() block is not mentioned in the changelog. Both change what the ->read_skb() (BPF sockmap) consumer sees: one stops delivery of a zero-payload skb, the other stops re-delivering already consumed bytes. Could those two changes be split out with their own justification, so that this patch only carries the manage_oob() accounting fix? > Fixes: 314001f0bf92 ("af_unix: Add OOB support") [Severity: Medium] Is this the right Fixes target? u->inq_len does not exist at that commit; blame on include/net/af_unix.h attributes int inq_len; to f4e1fb04c1238 ("af_unix: Use cached value for SOCK_STREAM in unix_inq_len()."). Before that, unix_inq_len() walked the receive queue: skb_queue_walk(&sk->sk_receive_queue, skb) amount += unix_skb_len(skb); so a discarded OOB skb could not leave a stale count behind. f4e1fb04c1238 added the inq_len increment in queue_oob() and the decrement in unix_stream_recv_urg(), but not the manage_oob() discard decrement this patch adds. Should the tag point at f4e1fb04c1238 instead, and should the patch carry Cc: stable@vger.kernel.org given it claims a user-visible SIOCINQ/FIONREAD fix? > diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c > index 42cffeafc8c1..1770af3c2684 100644 > --- a/net/unix/af_unix.c > +++ b/net/unix/af_unix.c > @@ -2849,6 +2849,7 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sk_buff **last, > WRITE_ONCE(u->oob_skb, NULL); > > if (!sock_flag(sk, SOCK_URGINLINE)) { > + WRITE_ONCE(u->inq_len, u->inq_len - 1); > __skb_unlink(skb, &sk->sk_receive_queue); > unread_skb = skb; > skb = skb_peek(&sk->sk_receive_queue); > @@ -2884,6 +2885,7 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor) > return err; > > mutex_lock(&u->iolock); > +again: > spin_lock(&queue->lock); > > skb = __skb_dequeue(queue); > @@ -2893,6 +2895,12 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor) > return -EAGAIN; > } > > + if (!unix_skb_len(skb)) { > + spin_unlock(&queue->lock); > + consume_skb(skb); > + goto again; > + } > + [Severity: Medium] Does this new discard path leave u->scm_stat.nr_fds inflated? The normal exit of unix_stream_read_skb() goes through: static void unix_orphan_scm(struct sock *sk, struct sk_buff *skb) { scm_stat_del(sk, skb); unix_destruct_scm(skb); skb->destructor = sock_wfree; } and scm_stat_del() is the only place that decrements nr_fds: if (unlikely(fp && fp->count)) { atomic_sub(fp->count, &u->scm_stat.nr_fds); unix_del_edges(fp); } The bare consume_skb() here goes through the destructor chain unix_wfree() -> unix_destruct_scm() -> unix_detach_fds(), which releases the files but never touches scm_stat.nr_fds. A reachable input looks like: sendmsg(MSG_OOB) with SCM_RIGHTS, where queue_oob() attaches the fds and calls scm_stat_add(other, skb); then a recvmsg(MSG_OOB) without MSG_PEEK in unix_stream_recv_urg() does if (!(state->flags & MSG_PEEK)) UNIXCB(oob_skb).consumed += 1; leaving a queued zero-payload skb that still carries UNIXCB(skb).fp. When that skb reaches the head of the queue on a sockmap'd socket, the new branch frees it without scm_stat_del(), so unix_show_fdinfo() keeps reporting an inflated scm_fds for the life of the socket. Would calling unix_orphan_scm(sk, skb) (or at least scm_stat_del()) before freeing keep the accounting balanced? > WRITE_ONCE(u->inq_len, u->inq_len - unix_skb_len(skb)); > > #if IS_ENABLED(CONFIG_AF_UNIX_OOB) > @@ -2908,6 +2916,11 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor) > > spin_unlock(&queue->lock); > > + if (UNIXCB(skb).consumed) { > + skb_pull(skb, UNIXCB(skb).consumed); > + UNIXCB(skb).consumed = 0; > + } > + [Severity: Critical] Can this skb_pull() hit the BUG() in __skb_pull() on a paged skb? UNIXCB(skb).consumed is a logical stream offset, not bounded by skb_headlen(skb). unix_stream_sendmsg() routinely builds non-linear skbs: data_len = max_t(int, 0, size - SKB_MAX_HEAD(0)); ... skb = sock_alloc_send_pskb(sk, size - data_len, data_len, ...); and on the MSG_SPLICE_PAGES branch the head length is 0. unix_stream_read_generic() then advances consumed and deliberately leaves the skb queued while any payload remains: UNIXCB(skb).consumed += chunk; ... if (unix_skb_len(skb)) { spin_unlock(&sk->sk_receive_queue.lock); break; } skb_pull() only rejects len > skb->len; __skb_pull() then does: skb->len -= len; if (unlikely(skb->len < skb->data_len)) { ... BUG(); } So a peer writing more than SKB_MAX_HEAD(0) bytes (or splicing pages, where a single consumed byte is enough), a reader consuming a prefix past the linear head with a normal recvmsg(), and then further data arriving on a socket with a sockmap verdict program gives sk_psock_verdict_data_ready() -> ops->read_skb() -> unix_stream_read_skb() -> skb_pull() -> BUG(). The BUG() fires after spin_unlock(&queue->lock) but before mutex_unlock(&u->iolock), so the task dies holding u->iolock and further readers and MSG_OOB users of that socket block forever; the skb is already dequeued and u->inq_len already decremented. If already consumed bytes need to be hidden from the recv_actor, would a paged-safe form such as pskb_pull()/__pskb_pull_tail() with failure handling, or passing the offset to the actor, be more appropriate here? > unix_orphan_scm(sk, skb); > > mutex_unlock(&u->iolock); -- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260919221735.3708237-1-benquike%40gmail.com ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-21 5:48 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-19 22:17 [PATCH] af_unix: fix u->inq_len accounting in manage_oob() and unix_stream_read_skb() Hui Peng 2026-09-20 19:27 ` Kuniyuki Iwashima 2026-09-21 5:48 ` [PATCH net v2 1/2] af_unix: decrement u->inq_len when skipping unread OOB skb in manage_oob() Hui Peng 2026-09-21 5:48 ` [PATCH net v2 2/2] af_unix: skip consumed OOB skb and pull consumed bytes in unix_stream_read_skb() Hui Peng 2026-09-20 23:19 ` [PATCH] af_unix: fix u->inq_len accounting in manage_oob() and unix_stream_read_skb() netdev-bot+sashiko
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®