Merge branch 'af_unix-minor-fixes-for-msg_oob-and-msg_peek'

Kuniyuki Iwashima says:

====================
af_unix: Minor fixes for MSG_OOB and MSG_PEEK.

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.
====================

Link: https://patch.msgid.link/20260902202202.892676-1-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-09-07 16:58:45 -07:00
commit dead0c41db
2 changed files with 75 additions and 4 deletions

View File

@ -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,8 +2975,8 @@ 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);
if (!skb && copied) {
skb = manage_oob(skb, &last, sk, flags, copied);
if (!skb && (copied || !state->size)) {
unix_state_unlock(sk);
break;
}

View File

@ -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