Netdev List
 help / color / mirror / Atom feed
* [PATCH v1 net 0/3] af_unix: Minor fixes for MSG_OOB and MSG_PEEK.
@ 2026-09-02 20:21 Kuniyuki Iwashima
  2026-09-02 20:21 ` [PATCH v1 net 1/3] af_unix: Update last skb marker in manage_oob() Kuniyuki Iwashima
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-02 20:21 UTC (permalink / raw)
  To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Fahad Alharbi, Kuniyuki Iwashima, Kuniyuki Iwashima,
	netdev

Fahad Alharbi reported blocking recv(MSG_PEEK) could hog CPU
due to OOB skb.

Patch 1 and 2 fixes the issues and Patch 3 adds tests.


Kuniyuki Iwashima (3):
  af_unix: Update last skb marker in manage_oob().
  af_unix: Return immediately when manage_oob() returns NULL for
    0-length buffer.
  selftest: af_unix: Add zero-buffer test for msg_oob.c

 net/unix/af_unix.c                            | 12 ++--
 tools/testing/selftests/net/af_unix/msg_oob.c | 67 +++++++++++++++++++
 2 files changed, 75 insertions(+), 4 deletions(-)

-- 
2.55.0.970.g62bdec98f9-goog


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v1 net 1/3] af_unix: Update last skb marker in manage_oob().
  2026-09-02 20:21 [PATCH v1 net 0/3] af_unix: Minor fixes for MSG_OOB and MSG_PEEK Kuniyuki Iwashima
@ 2026-09-02 20:21 ` Kuniyuki Iwashima
  2026-09-02 20:21 ` [PATCH v1 net 2/3] af_unix: Return immediately when manage_oob() returns NULL for 0-length buffer Kuniyuki Iwashima
  2026-09-02 20:21 ` [PATCH v1 net 3/3] selftest: af_unix: Add zero-buffer test for msg_oob.c Kuniyuki Iwashima
  2 siblings, 0 replies; 4+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-02 20:21 UTC (permalink / raw)
  To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Fahad Alharbi, Kuniyuki Iwashima, Kuniyuki Iwashima,
	netdev

Fahad Alharbi reported that blocking recv(MSG_PEEK) could hog CPU
due to OOB skb.

In the following cases, manage_oob() skips OOB skb(s) and returns
NULL for the last recv(MSG_PEEK):

  socketpair(AF_UNIX, SOCK_STREAM, 0, sk);

  1) skb -> OOB skb -> NULL
     send(sk[0], "ab", 2, MSG_OOB);
     recv(sk[1], buf, 0, MSG_PEEK);

  2) skb -> consumed OOB skb -> NULL
     send(sk[0], "ab", 2, MSG_OOB);
     recv(sk[1], buf, 1, MSG_OOB);
     recv(sk[1], buf, 0, MSG_PEEK);

  3) consumed OOB skb -> OOB skb -> NULL
     send(sk[0], "a", 1, MSG_OOB);
     recv(sk[1], buf, 0, MSG_OOB);
     send(sk[0], "b", 1, MSG_OOB);
     recv(sk[1], buf, 1, MSG_PEEK);

Then, @copied is 0 in unix_stream_read_generic() (zero-length buffer,
or non-OOB skb is not yet consumed), and unix_stream_data_wait() is
called.

However, it returns immediately because @last is not updated in
unix_stream_read_generic(), and the thread busy-waits for a new skb.

Let's update @last in manage_oob().

For MSG_PEEK, @last is updated with the skipped OOB, and for the
non-peek case, @last matches the returned value (when !copied)
because OOB is unlinked.

Note that manage_oob() is inlined and no stack canary is added.

Fixes: 22dd70eb2c3d ("af_unix: Don't peek OOB data without MSG_OOB.")
Reported-by: Fahad Alharbi <fahad@codepure.com>
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 net/unix/af_unix.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 13f9926bf205..6861370062df 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -2812,8 +2812,8 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state)
 	return 1;
 }
 
-static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,
-				  int flags, int copied)
+static struct sk_buff *manage_oob(struct sk_buff *skb, struct sk_buff **last,
+				  struct sock *sk, int flags, int copied)
 {
 	struct sk_buff *read_skb = NULL, *unread_skb = NULL;
 	struct unix_sock *u = unix_sk(sk);
@@ -2827,11 +2827,13 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,
 		if (copied && (!u->oob_skb || skb == u->oob_skb)) {
 			skb = NULL;
 		} else if (flags & MSG_PEEK) {
+			*last = skb;
 			skb = skb_peek_next(skb, &sk->sk_receive_queue);
 		} else {
 			read_skb = skb;
 			skb = skb_peek_next(skb, &sk->sk_receive_queue);
 			__skb_unlink(read_skb, &sk->sk_receive_queue);
+			*last = skb;
 		}
 
 		if (!skb)
@@ -2850,8 +2852,10 @@ static struct sk_buff *manage_oob(struct sk_buff *skb, struct sock *sk,
 			__skb_unlink(skb, &sk->sk_receive_queue);
 			unread_skb = skb;
 			skb = skb_peek(&sk->sk_receive_queue);
+			*last = skb;
 		}
 	} else if (!sock_flag(sk, SOCK_URGINLINE)) {
+		*last = skb;
 		skb = skb_peek_next(skb, &sk->sk_receive_queue);
 	}
 
@@ -2971,7 +2975,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
 again:
 #if IS_ENABLED(CONFIG_AF_UNIX_OOB)
 		if (skb) {
-			skb = manage_oob(skb, sk, flags, copied);
+			skb = manage_oob(skb, &last, sk, flags, copied);
 			if (!skb && copied) {
 				unix_state_unlock(sk);
 				break;
-- 
2.55.0.970.g62bdec98f9-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v1 net 2/3] af_unix: Return immediately when manage_oob() returns NULL for 0-length buffer.
  2026-09-02 20:21 [PATCH v1 net 0/3] af_unix: Minor fixes for MSG_OOB and MSG_PEEK Kuniyuki Iwashima
  2026-09-02 20:21 ` [PATCH v1 net 1/3] af_unix: Update last skb marker in manage_oob() Kuniyuki Iwashima
@ 2026-09-02 20:21 ` Kuniyuki Iwashima
  2026-09-02 20:21 ` [PATCH v1 net 3/3] selftest: af_unix: Add zero-buffer test for msg_oob.c Kuniyuki Iwashima
  2 siblings, 0 replies; 4+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-02 20:21 UTC (permalink / raw)
  To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Fahad Alharbi, Kuniyuki Iwashima, Kuniyuki Iwashima,
	netdev

Fahad Alharbi reported that recv(0, MSG_PEEK) triggers busy-wait
in unix_stream_read_generic() if recv() is blocking and the last
skb in the queue is MSG_OOB skb.

In such a situation, TCP returns 0 immediately regardless of
blocking or non-blocking.

Let's follow the behaviour.

Fixes: 314001f0bf92 ("af_unix: Add OOB support")
Reported-by: Fahad Alharbi <fahad@codepure.com>
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 net/unix/af_unix.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 6861370062df..2da1017f8873 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -2976,7 +2976,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
 #if IS_ENABLED(CONFIG_AF_UNIX_OOB)
 		if (skb) {
 			skb = manage_oob(skb, &last, sk, flags, copied);
-			if (!skb && copied) {
+			if (!skb && (copied || !state->size)) {
 				unix_state_unlock(sk);
 				break;
 			}
-- 
2.55.0.970.g62bdec98f9-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v1 net 3/3] selftest: af_unix: Add zero-buffer test for msg_oob.c
  2026-09-02 20:21 [PATCH v1 net 0/3] af_unix: Minor fixes for MSG_OOB and MSG_PEEK Kuniyuki Iwashima
  2026-09-02 20:21 ` [PATCH v1 net 1/3] af_unix: Update last skb marker in manage_oob() Kuniyuki Iwashima
  2026-09-02 20:21 ` [PATCH v1 net 2/3] af_unix: Return immediately when manage_oob() returns NULL for 0-length buffer Kuniyuki Iwashima
@ 2026-09-02 20:21 ` Kuniyuki Iwashima
  2 siblings, 0 replies; 4+ messages in thread
From: Kuniyuki Iwashima @ 2026-09-02 20:21 UTC (permalink / raw)
  To: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Fahad Alharbi, Kuniyuki Iwashima, Kuniyuki Iwashima,
	netdev

The previous patches fixed two issues related to zero-length
buffer with MSG_PEEK for MSG_OOB skb.

Let's add corresponding tests in msg_oob.c.

Without this series:

  # FAILED: 50 / 60 tests passed.
  # Totals: pass:50 fail:10 xfail:0 xpass:0 skip:0 error:0

With this series:

  # PASSED: 60 / 60 tests passed.
  # Totals: pass:60 fail:0 xfail:0 xpass:0 skip:0 error:0

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 tools/testing/selftests/net/af_unix/msg_oob.c | 67 +++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/tools/testing/selftests/net/af_unix/msg_oob.c b/tools/testing/selftests/net/af_unix/msg_oob.c
index 1b499d56656c..f051d79f7a8e 100644
--- a/tools/testing/selftests/net/af_unix/msg_oob.c
+++ b/tools/testing/selftests/net/af_unix/msg_oob.c
@@ -290,6 +290,25 @@ static void __setinlinepair(struct __test_metadata *_metadata,
 	}
 }
 
+static void __setblockingpair(struct __test_metadata *_metadata,
+			      FIXTURE_DATA(msg_oob) *self)
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		int ret, old_flags, flags;
+
+		old_flags = fcntl(self->fd[i * 2 + 1], F_GETFL, 0);
+		ASSERT_NE(-1, old_flags);
+
+		ret = fcntl(self->fd[i * 2 + 1], F_SETFL, old_flags & ~O_NONBLOCK);
+		ASSERT_EQ(0, ret);
+
+		flags = fcntl(self->fd[i * 2 + 1], F_GETFL, 0);
+		ASSERT_EQ(old_flags & ~O_NONBLOCK, flags);
+	}
+}
+
 static void __siocatmarkpair(struct __test_metadata *_metadata,
 			     FIXTURE_DATA(msg_oob) *self,
 			     bool oob_head)
@@ -347,6 +366,9 @@ static void __resetpair(struct __test_metadata *_metadata,
 #define setinlinepair()							\
 	__setinlinepair(_metadata, self)
 
+#define setblockingpair()						\
+	__setblockingpair(_metadata, self)
+
 #define resetpair(reset)						\
 	__resetpair(_metadata, self, variant, reset)
 
@@ -888,4 +910,49 @@ TEST_F(msg_oob, inline_ex_oob_siocatmark)
 	resetpair(true);
 }
 
+TEST_F(msg_oob, zero_buf_oob)
+{
+	sendpair("a", 1, MSG_OOB);
+	recvpair("", 0, 0, 0);
+}
+
+TEST_F(msg_oob, zero_buf_oob_blocking)
+{
+	sendpair("a", 1, MSG_OOB);
+	setblockingpair();
+	recvpair("", 0, 0, 0);
+}
+
+TEST_F(msg_oob, zero_buf_non_oob_oob)
+{
+	sendpair("ab", 2, MSG_OOB);
+	recvpair("", 0, 0, 0);
+}
+
+TEST_F(msg_oob, zero_buf_non_oob_oob_blocking)
+{
+	sendpair("ab", 2, MSG_OOB);
+	setblockingpair();
+	recvpair("", 0, 0, 0);
+}
+
+TEST_F(msg_oob, zero_buf_ex_oob_oob)
+{
+	sendpair("a", 1, MSG_OOB);
+	recvpair("a", 1, 1, MSG_OOB);
+
+	sendpair("b", 1, MSG_OOB);
+	recvpair("", 0, 0, 0);
+}
+
+TEST_F(msg_oob, zero_buf_ex_oob_oob_blocking)
+{
+	sendpair("a", 1, MSG_OOB);
+	recvpair("a", 1, 1, MSG_OOB);
+
+	sendpair("b", 1, MSG_OOB);
+	setblockingpair();
+	recvpair("", 0, 0, 0);
+}
+
 TEST_HARNESS_MAIN
-- 
2.55.0.970.g62bdec98f9-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-09-02 20:22 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-02 20:21 [PATCH v1 net 0/3] af_unix: Minor fixes for MSG_OOB and MSG_PEEK Kuniyuki Iwashima
2026-09-02 20:21 ` [PATCH v1 net 1/3] af_unix: Update last skb marker in manage_oob() Kuniyuki Iwashima
2026-09-02 20:21 ` [PATCH v1 net 2/3] af_unix: Return immediately when manage_oob() returns NULL for 0-length buffer Kuniyuki Iwashima
2026-09-02 20:21 ` [PATCH v1 net 3/3] selftest: af_unix: Add zero-buffer test for msg_oob.c Kuniyuki Iwashima

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox