Skip to content

Commit

Permalink
Merge branch 'af_unix-fix-two-oob-issues'
Browse files Browse the repository at this point in the history
Kuniyuki Iwashima says:

====================
af_unix: Fix two OOB issues.

From: Kuniyuki Iwashima <kuniyu@google.com>

Recently, two issues are reported regarding MSG_OOB.

Patch 1 fixes issues that happen when multiple consumed OOB
skbs are placed consecutively in the recv queue.

Patch 2 fixes an inconsistent behaviour that close()ing a socket
with a consumed OOB skb at the head of the recv queue triggers
-ECONNRESET on the peer's recv().

v1: https://lore.kernel.org/netdev/20250618043453.281247-1-kuni1840@gmail.com/
====================

Link: https://patch.msgid.link/20250619041457.1132791-1-kuni1840@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
  • Loading branch information
Paolo Abeni committed Jun 24, 2025
2 parents 7544f3f + 632f55f commit c3f4293
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 12 deletions.
31 changes: 23 additions & 8 deletions net/unix/af_unix.c
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,11 @@ static void unix_sock_destructor(struct sock *sk)
#endif
}

static unsigned int unix_skb_len(const struct sk_buff *skb)
{
return skb->len - UNIXCB(skb).consumed;
}

static void unix_release_sock(struct sock *sk, int embrion)
{
struct unix_sock *u = unix_sk(sk);
Expand Down Expand Up @@ -694,10 +699,16 @@ static void unix_release_sock(struct sock *sk, int embrion)

if (skpair != NULL) {
if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_SEQPACKET) {
struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);

#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
if (skb && !unix_skb_len(skb))
skb = skb_peek_next(skb, &sk->sk_receive_queue);
#endif
unix_state_lock(skpair);
/* No more writes */
WRITE_ONCE(skpair->sk_shutdown, SHUTDOWN_MASK);
if (!skb_queue_empty_lockless(&sk->sk_receive_queue) || embrion)
if (skb || embrion)
WRITE_ONCE(skpair->sk_err, ECONNRESET);
unix_state_unlock(skpair);
skpair->sk_state_change(skpair);
Expand Down Expand Up @@ -2661,11 +2672,6 @@ static long unix_stream_data_wait(struct sock *sk, long timeo,
return timeo;
}

static unsigned int unix_skb_len(const struct sk_buff *skb)
{
return skb->len - UNIXCB(skb).consumed;
}

struct unix_stream_read_state {
int (*recv_actor)(struct sk_buff *, int, int,
struct unix_stream_read_state *);
Expand All @@ -2680,11 +2686,11 @@ struct unix_stream_read_state {
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
static int unix_stream_recv_urg(struct unix_stream_read_state *state)
{
struct sk_buff *oob_skb, *read_skb = NULL;
struct socket *sock = state->socket;
struct sock *sk = sock->sk;
struct unix_sock *u = unix_sk(sk);
int chunk = 1;
struct sk_buff *oob_skb;

mutex_lock(&u->iolock);
unix_state_lock(sk);
Expand All @@ -2699,9 +2705,16 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state)

oob_skb = u->oob_skb;

if (!(state->flags & MSG_PEEK))
if (!(state->flags & MSG_PEEK)) {
WRITE_ONCE(u->oob_skb, NULL);

if (oob_skb->prev != (struct sk_buff *)&sk->sk_receive_queue &&
!unix_skb_len(oob_skb->prev)) {
read_skb = oob_skb->prev;
__skb_unlink(read_skb, &sk->sk_receive_queue);
}
}

spin_unlock(&sk->sk_receive_queue.lock);
unix_state_unlock(sk);

Expand All @@ -2712,6 +2725,8 @@ static int unix_stream_recv_urg(struct unix_stream_read_state *state)

mutex_unlock(&u->iolock);

consume_skb(read_skb);

if (chunk < 0)
return -EFAULT;

Expand Down
142 changes: 138 additions & 4 deletions tools/testing/selftests/net/af_unix/msg_oob.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ static void __sendpair(struct __test_metadata *_metadata,
static void __recvpair(struct __test_metadata *_metadata,
FIXTURE_DATA(msg_oob) *self,
const char *expected_buf, int expected_len,
int buf_len, int flags)
int buf_len, int flags, bool is_sender)
{
int i, ret[2], recv_errno[2], expected_errno = 0;
char recv_buf[2][BUF_SZ] = {};
Expand All @@ -221,7 +221,9 @@ static void __recvpair(struct __test_metadata *_metadata,
errno = 0;

for (i = 0; i < 2; i++) {
ret[i] = recv(self->fd[i * 2 + 1], recv_buf[i], buf_len, flags);
int index = is_sender ? i * 2 : i * 2 + 1;

ret[i] = recv(self->fd[index], recv_buf[i], buf_len, flags);
recv_errno[i] = errno;
}

Expand Down Expand Up @@ -308,6 +310,20 @@ static void __siocatmarkpair(struct __test_metadata *_metadata,
ASSERT_EQ(answ[0], answ[1]);
}

static void __resetpair(struct __test_metadata *_metadata,
FIXTURE_DATA(msg_oob) *self,
const FIXTURE_VARIANT(msg_oob) *variant,
bool reset)
{
int i;

for (i = 0; i < 2; i++)
close(self->fd[i * 2 + 1]);

__recvpair(_metadata, self, "", reset ? -ECONNRESET : 0, 1,
variant->peek ? MSG_PEEK : 0, true);
}

#define sendpair(buf, len, flags) \
__sendpair(_metadata, self, buf, len, flags)

Expand All @@ -316,9 +332,10 @@ static void __siocatmarkpair(struct __test_metadata *_metadata,
if (variant->peek) \
__recvpair(_metadata, self, \
expected_buf, expected_len, \
buf_len, (flags) | MSG_PEEK); \
buf_len, (flags) | MSG_PEEK, false); \
__recvpair(_metadata, self, \
expected_buf, expected_len, buf_len, flags); \
expected_buf, expected_len, \
buf_len, flags, false); \
} while (0)

#define epollpair(oob_remaining) \
Expand All @@ -330,6 +347,9 @@ static void __siocatmarkpair(struct __test_metadata *_metadata,
#define setinlinepair() \
__setinlinepair(_metadata, self)

#define resetpair(reset) \
__resetpair(_metadata, self, variant, reset)

#define tcp_incompliant \
for (self->tcp_compliant = false; \
self->tcp_compliant == false; \
Expand All @@ -344,6 +364,21 @@ TEST_F(msg_oob, non_oob)
recvpair("", -EINVAL, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);

resetpair(true);
}

TEST_F(msg_oob, non_oob_no_reset)
{
sendpair("x", 1, 0);
epollpair(false);
siocatmarkpair(false);

recvpair("x", 1, 1, 0);
epollpair(false);
siocatmarkpair(false);

resetpair(false);
}

TEST_F(msg_oob, oob)
Expand All @@ -355,6 +390,19 @@ TEST_F(msg_oob, oob)
recvpair("x", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(true);

tcp_incompliant {
resetpair(false); /* TCP sets -ECONNRESET for ex-OOB. */
}
}

TEST_F(msg_oob, oob_reset)
{
sendpair("x", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);

resetpair(true);
}

TEST_F(msg_oob, oob_drop)
Expand All @@ -370,6 +418,8 @@ TEST_F(msg_oob, oob_drop)
recvpair("", -EINVAL, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);

resetpair(false);
}

TEST_F(msg_oob, oob_ahead)
Expand All @@ -385,6 +435,10 @@ TEST_F(msg_oob, oob_ahead)
recvpair("hell", 4, 4, 0);
epollpair(false);
siocatmarkpair(true);

tcp_incompliant {
resetpair(false); /* TCP sets -ECONNRESET for ex-OOB. */
}
}

TEST_F(msg_oob, oob_break)
Expand All @@ -403,6 +457,8 @@ TEST_F(msg_oob, oob_break)

recvpair("", -EAGAIN, 1, 0);
siocatmarkpair(false);

resetpair(false);
}

TEST_F(msg_oob, oob_ahead_break)
Expand All @@ -426,6 +482,8 @@ TEST_F(msg_oob, oob_ahead_break)
recvpair("world", 5, 5, 0);
epollpair(false);
siocatmarkpair(false);

resetpair(false);
}

TEST_F(msg_oob, oob_break_drop)
Expand All @@ -449,6 +507,8 @@ TEST_F(msg_oob, oob_break_drop)
recvpair("", -EINVAL, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);

resetpair(false);
}

TEST_F(msg_oob, ex_oob_break)
Expand Down Expand Up @@ -476,6 +536,8 @@ TEST_F(msg_oob, ex_oob_break)
recvpair("ld", 2, 2, 0);
epollpair(false);
siocatmarkpair(false);

resetpair(false);
}

TEST_F(msg_oob, ex_oob_drop)
Expand All @@ -498,6 +560,8 @@ TEST_F(msg_oob, ex_oob_drop)
epollpair(false);
siocatmarkpair(true);
}

resetpair(false);
}

TEST_F(msg_oob, ex_oob_drop_2)
Expand All @@ -523,6 +587,8 @@ TEST_F(msg_oob, ex_oob_drop_2)
epollpair(false);
siocatmarkpair(true);
}

resetpair(false);
}

TEST_F(msg_oob, ex_oob_oob)
Expand All @@ -546,6 +612,54 @@ TEST_F(msg_oob, ex_oob_oob)
recvpair("", -EINVAL, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(false);

resetpair(false);
}

TEST_F(msg_oob, ex_oob_ex_oob)
{
sendpair("x", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);

recvpair("x", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(true);

sendpair("y", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);

recvpair("y", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(true);

tcp_incompliant {
resetpair(false); /* TCP sets -ECONNRESET for ex-OOB. */
}
}

TEST_F(msg_oob, ex_oob_ex_oob_oob)
{
sendpair("x", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);

recvpair("x", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(true);

sendpair("y", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);

recvpair("y", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(true);

sendpair("z", 1, MSG_OOB);
epollpair(true);
siocatmarkpair(true);
}

TEST_F(msg_oob, ex_oob_ahead_break)
Expand Down Expand Up @@ -576,6 +690,10 @@ TEST_F(msg_oob, ex_oob_ahead_break)
recvpair("d", 1, 1, MSG_OOB);
epollpair(false);
siocatmarkpair(true);

tcp_incompliant {
resetpair(false); /* TCP sets -ECONNRESET for ex-OOB. */
}
}

TEST_F(msg_oob, ex_oob_siocatmark)
Expand All @@ -595,6 +713,8 @@ TEST_F(msg_oob, ex_oob_siocatmark)
recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */
epollpair(true);
siocatmarkpair(false);

resetpair(true);
}

TEST_F(msg_oob, inline_oob)
Expand All @@ -612,6 +732,8 @@ TEST_F(msg_oob, inline_oob)
recvpair("x", 1, 1, 0);
epollpair(false);
siocatmarkpair(false);

resetpair(false);
}

TEST_F(msg_oob, inline_oob_break)
Expand All @@ -633,6 +755,8 @@ TEST_F(msg_oob, inline_oob_break)
recvpair("o", 1, 1, 0);
epollpair(false);
siocatmarkpair(false);

resetpair(false);
}

TEST_F(msg_oob, inline_oob_ahead_break)
Expand Down Expand Up @@ -661,6 +785,8 @@ TEST_F(msg_oob, inline_oob_ahead_break)

epollpair(false);
siocatmarkpair(false);

resetpair(false);
}

TEST_F(msg_oob, inline_ex_oob_break)
Expand All @@ -686,6 +812,8 @@ TEST_F(msg_oob, inline_ex_oob_break)
recvpair("rld", 3, 3, 0);
epollpair(false);
siocatmarkpair(false);

resetpair(false);
}

TEST_F(msg_oob, inline_ex_oob_no_drop)
Expand All @@ -707,6 +835,8 @@ TEST_F(msg_oob, inline_ex_oob_no_drop)
recvpair("y", 1, 1, 0);
epollpair(false);
siocatmarkpair(false);

resetpair(false);
}

TEST_F(msg_oob, inline_ex_oob_drop)
Expand All @@ -731,6 +861,8 @@ TEST_F(msg_oob, inline_ex_oob_drop)
epollpair(false);
siocatmarkpair(false);
}

resetpair(false);
}

TEST_F(msg_oob, inline_ex_oob_siocatmark)
Expand All @@ -752,6 +884,8 @@ TEST_F(msg_oob, inline_ex_oob_siocatmark)
recvpair("hell", 4, 4, 0); /* Intentionally stop at ex-OOB. */
epollpair(true);
siocatmarkpair(false);

resetpair(true);
}

TEST_HARNESS_MAIN

0 comments on commit c3f4293

Please sign in to comment.