Merge branch 'af_unix-fix-two-oob-issues'
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>
This commit is contained in:
+23
-8
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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 *);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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] = {};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) \
|
||||
@@ -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; \
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user