mirror of
https://github.com/torvalds/linux.git
synced 2026-05-23 06:31:58 +02:00
Merge branch 'af_unix-fix-so_peek_off-bug-in-unix_stream_read_generic'
Kuniyuki Iwashima says: ==================== af_unix: Fix SO_PEEK_OFF bug in unix_stream_read_generic(). Miao Wang reported a bug of SO_PEEK_OFF on AF_UNIX SOCK_STREAM socket. Patch 1 fixes the bug and Patch 2 adds a new selftest to cover the case. ==================== Link: https://patch.msgid.link/20251117174740.3684604-1-kuniyu@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
106a67494c
|
|
@ -2954,6 +2954,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
|
|||
|
||||
u = unix_sk(sk);
|
||||
|
||||
redo:
|
||||
/* Lock the socket to prevent queue disordering
|
||||
* while sleeps in memcpy_tomsg
|
||||
*/
|
||||
|
|
@ -2965,7 +2966,6 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
|
|||
struct sk_buff *skb, *last;
|
||||
int chunk;
|
||||
|
||||
redo:
|
||||
unix_state_lock(sk);
|
||||
if (sock_flag(sk, SOCK_DEAD)) {
|
||||
err = -ECONNRESET;
|
||||
|
|
@ -3015,7 +3015,6 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
|
|||
goto out;
|
||||
}
|
||||
|
||||
mutex_lock(&u->iolock);
|
||||
goto redo;
|
||||
unlock:
|
||||
unix_state_unlock(sk);
|
||||
|
|
|
|||
1
tools/testing/selftests/net/.gitignore
vendored
1
tools/testing/selftests/net/.gitignore
vendored
|
|
@ -45,6 +45,7 @@ skf_net_off
|
|||
socket
|
||||
so_incoming_cpu
|
||||
so_netns_cookie
|
||||
so_peek_off
|
||||
so_txtime
|
||||
so_rcv_listener
|
||||
stress_reuseport_listen
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ TEST_GEN_PROGS := \
|
|||
scm_inq \
|
||||
scm_pidfd \
|
||||
scm_rights \
|
||||
so_peek_off \
|
||||
unix_connect \
|
||||
# end of TEST_GEN_PROGS
|
||||
|
||||
|
|
|
|||
162
tools/testing/selftests/net/af_unix/so_peek_off.c
Normal file
162
tools/testing/selftests/net/af_unix/so_peek_off.c
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright 2025 Google LLC */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "../../kselftest_harness.h"
|
||||
|
||||
FIXTURE(so_peek_off)
|
||||
{
|
||||
int fd[2]; /* 0: sender, 1: receiver */
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT(so_peek_off)
|
||||
{
|
||||
int type;
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(so_peek_off, stream)
|
||||
{
|
||||
.type = SOCK_STREAM,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(so_peek_off, dgram)
|
||||
{
|
||||
.type = SOCK_DGRAM,
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT_ADD(so_peek_off, seqpacket)
|
||||
{
|
||||
.type = SOCK_SEQPACKET,
|
||||
};
|
||||
|
||||
FIXTURE_SETUP(so_peek_off)
|
||||
{
|
||||
struct timeval timeout = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = 3000,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = socketpair(AF_UNIX, variant->type, 0, self->fd);
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
ret = setsockopt(self->fd[1], SOL_SOCKET, SO_RCVTIMEO_NEW,
|
||||
&timeout, sizeof(timeout));
|
||||
ASSERT_EQ(0, ret);
|
||||
|
||||
ret = setsockopt(self->fd[1], SOL_SOCKET, SO_PEEK_OFF,
|
||||
&(int){0}, sizeof(int));
|
||||
ASSERT_EQ(0, ret);
|
||||
}
|
||||
|
||||
FIXTURE_TEARDOWN(so_peek_off)
|
||||
{
|
||||
close_range(self->fd[0], self->fd[1], 0);
|
||||
}
|
||||
|
||||
#define sendeq(fd, str, flags) \
|
||||
do { \
|
||||
int bytes, len = strlen(str); \
|
||||
\
|
||||
bytes = send(fd, str, len, flags); \
|
||||
ASSERT_EQ(len, bytes); \
|
||||
} while (0)
|
||||
|
||||
#define recveq(fd, str, buflen, flags) \
|
||||
do { \
|
||||
char buf[(buflen) + 1] = {}; \
|
||||
int bytes; \
|
||||
\
|
||||
bytes = recv(fd, buf, buflen, flags); \
|
||||
ASSERT_NE(-1, bytes); \
|
||||
ASSERT_STREQ(str, buf); \
|
||||
} while (0)
|
||||
|
||||
#define async \
|
||||
for (pid_t pid = (pid = fork(), \
|
||||
pid < 0 ? \
|
||||
__TH_LOG("Failed to start async {}"), \
|
||||
_metadata->exit_code = KSFT_FAIL, \
|
||||
__bail(1, _metadata), \
|
||||
0xdead : \
|
||||
pid); \
|
||||
!pid; exit(0))
|
||||
|
||||
TEST_F(so_peek_off, single_chunk)
|
||||
{
|
||||
sendeq(self->fd[0], "aaaabbbb", 0);
|
||||
|
||||
recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
|
||||
recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
|
||||
}
|
||||
|
||||
TEST_F(so_peek_off, two_chunks)
|
||||
{
|
||||
sendeq(self->fd[0], "aaaa", 0);
|
||||
sendeq(self->fd[0], "bbbb", 0);
|
||||
|
||||
recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
|
||||
recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
|
||||
}
|
||||
|
||||
TEST_F(so_peek_off, two_chunks_blocking)
|
||||
{
|
||||
async {
|
||||
usleep(1000);
|
||||
sendeq(self->fd[0], "aaaa", 0);
|
||||
}
|
||||
|
||||
recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
|
||||
|
||||
async {
|
||||
usleep(1000);
|
||||
sendeq(self->fd[0], "bbbb", 0);
|
||||
}
|
||||
|
||||
/* goto again; -> goto redo; in unix_stream_read_generic(). */
|
||||
recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
|
||||
}
|
||||
|
||||
TEST_F(so_peek_off, two_chunks_overlap)
|
||||
{
|
||||
sendeq(self->fd[0], "aaaa", 0);
|
||||
recveq(self->fd[1], "aa", 2, MSG_PEEK);
|
||||
|
||||
sendeq(self->fd[0], "bbbb", 0);
|
||||
|
||||
if (variant->type == SOCK_STREAM) {
|
||||
/* SOCK_STREAM tries to fill the buffer. */
|
||||
recveq(self->fd[1], "aabb", 4, MSG_PEEK);
|
||||
recveq(self->fd[1], "bb", 100, MSG_PEEK);
|
||||
} else {
|
||||
/* SOCK_DGRAM and SOCK_SEQPACKET returns at the skb boundary. */
|
||||
recveq(self->fd[1], "aa", 100, MSG_PEEK);
|
||||
recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(so_peek_off, two_chunks_overlap_blocking)
|
||||
{
|
||||
async {
|
||||
usleep(1000);
|
||||
sendeq(self->fd[0], "aaaa", 0);
|
||||
}
|
||||
|
||||
recveq(self->fd[1], "aa", 2, MSG_PEEK);
|
||||
|
||||
async {
|
||||
usleep(1000);
|
||||
sendeq(self->fd[0], "bbbb", 0);
|
||||
}
|
||||
|
||||
/* Even SOCK_STREAM does not wait if at least one byte is read. */
|
||||
recveq(self->fd[1], "aa", 100, MSG_PEEK);
|
||||
|
||||
recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
Loading…
Reference in New Issue
Block a user