Skip to content

Commit 106a674

Browse files
committed
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>
2 parents d47515a + e1bb28b commit 106a674

4 files changed

Lines changed: 165 additions & 2 deletions

File tree

net/unix/af_unix.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2954,6 +2954,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
29542954

29552955
u = unix_sk(sk);
29562956

2957+
redo:
29572958
/* Lock the socket to prevent queue disordering
29582959
* while sleeps in memcpy_tomsg
29592960
*/
@@ -2965,7 +2966,6 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
29652966
struct sk_buff *skb, *last;
29662967
int chunk;
29672968

2968-
redo:
29692969
unix_state_lock(sk);
29702970
if (sock_flag(sk, SOCK_DEAD)) {
29712971
err = -ECONNRESET;
@@ -3015,7 +3015,6 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
30153015
goto out;
30163016
}
30173017

3018-
mutex_lock(&u->iolock);
30193018
goto redo;
30203019
unlock:
30213020
unix_state_unlock(sk);

tools/testing/selftests/net/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ skf_net_off
4545
socket
4646
so_incoming_cpu
4747
so_netns_cookie
48+
so_peek_off
4849
so_txtime
4950
so_rcv_listener
5051
stress_reuseport_listen

tools/testing/selftests/net/af_unix/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ TEST_GEN_PROGS := \
66
scm_inq \
77
scm_pidfd \
88
scm_rights \
9+
so_peek_off \
910
unix_connect \
1011
# end of TEST_GEN_PROGS
1112

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright 2025 Google LLC */
3+
4+
#include <stdlib.h>
5+
#include <unistd.h>
6+
7+
#include <sys/socket.h>
8+
9+
#include "../../kselftest_harness.h"
10+
11+
FIXTURE(so_peek_off)
12+
{
13+
int fd[2]; /* 0: sender, 1: receiver */
14+
};
15+
16+
FIXTURE_VARIANT(so_peek_off)
17+
{
18+
int type;
19+
};
20+
21+
FIXTURE_VARIANT_ADD(so_peek_off, stream)
22+
{
23+
.type = SOCK_STREAM,
24+
};
25+
26+
FIXTURE_VARIANT_ADD(so_peek_off, dgram)
27+
{
28+
.type = SOCK_DGRAM,
29+
};
30+
31+
FIXTURE_VARIANT_ADD(so_peek_off, seqpacket)
32+
{
33+
.type = SOCK_SEQPACKET,
34+
};
35+
36+
FIXTURE_SETUP(so_peek_off)
37+
{
38+
struct timeval timeout = {
39+
.tv_sec = 0,
40+
.tv_usec = 3000,
41+
};
42+
int ret;
43+
44+
ret = socketpair(AF_UNIX, variant->type, 0, self->fd);
45+
ASSERT_EQ(0, ret);
46+
47+
ret = setsockopt(self->fd[1], SOL_SOCKET, SO_RCVTIMEO_NEW,
48+
&timeout, sizeof(timeout));
49+
ASSERT_EQ(0, ret);
50+
51+
ret = setsockopt(self->fd[1], SOL_SOCKET, SO_PEEK_OFF,
52+
&(int){0}, sizeof(int));
53+
ASSERT_EQ(0, ret);
54+
}
55+
56+
FIXTURE_TEARDOWN(so_peek_off)
57+
{
58+
close_range(self->fd[0], self->fd[1], 0);
59+
}
60+
61+
#define sendeq(fd, str, flags) \
62+
do { \
63+
int bytes, len = strlen(str); \
64+
\
65+
bytes = send(fd, str, len, flags); \
66+
ASSERT_EQ(len, bytes); \
67+
} while (0)
68+
69+
#define recveq(fd, str, buflen, flags) \
70+
do { \
71+
char buf[(buflen) + 1] = {}; \
72+
int bytes; \
73+
\
74+
bytes = recv(fd, buf, buflen, flags); \
75+
ASSERT_NE(-1, bytes); \
76+
ASSERT_STREQ(str, buf); \
77+
} while (0)
78+
79+
#define async \
80+
for (pid_t pid = (pid = fork(), \
81+
pid < 0 ? \
82+
__TH_LOG("Failed to start async {}"), \
83+
_metadata->exit_code = KSFT_FAIL, \
84+
__bail(1, _metadata), \
85+
0xdead : \
86+
pid); \
87+
!pid; exit(0))
88+
89+
TEST_F(so_peek_off, single_chunk)
90+
{
91+
sendeq(self->fd[0], "aaaabbbb", 0);
92+
93+
recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
94+
recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
95+
}
96+
97+
TEST_F(so_peek_off, two_chunks)
98+
{
99+
sendeq(self->fd[0], "aaaa", 0);
100+
sendeq(self->fd[0], "bbbb", 0);
101+
102+
recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
103+
recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
104+
}
105+
106+
TEST_F(so_peek_off, two_chunks_blocking)
107+
{
108+
async {
109+
usleep(1000);
110+
sendeq(self->fd[0], "aaaa", 0);
111+
}
112+
113+
recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
114+
115+
async {
116+
usleep(1000);
117+
sendeq(self->fd[0], "bbbb", 0);
118+
}
119+
120+
/* goto again; -> goto redo; in unix_stream_read_generic(). */
121+
recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
122+
}
123+
124+
TEST_F(so_peek_off, two_chunks_overlap)
125+
{
126+
sendeq(self->fd[0], "aaaa", 0);
127+
recveq(self->fd[1], "aa", 2, MSG_PEEK);
128+
129+
sendeq(self->fd[0], "bbbb", 0);
130+
131+
if (variant->type == SOCK_STREAM) {
132+
/* SOCK_STREAM tries to fill the buffer. */
133+
recveq(self->fd[1], "aabb", 4, MSG_PEEK);
134+
recveq(self->fd[1], "bb", 100, MSG_PEEK);
135+
} else {
136+
/* SOCK_DGRAM and SOCK_SEQPACKET returns at the skb boundary. */
137+
recveq(self->fd[1], "aa", 100, MSG_PEEK);
138+
recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
139+
}
140+
}
141+
142+
TEST_F(so_peek_off, two_chunks_overlap_blocking)
143+
{
144+
async {
145+
usleep(1000);
146+
sendeq(self->fd[0], "aaaa", 0);
147+
}
148+
149+
recveq(self->fd[1], "aa", 2, MSG_PEEK);
150+
151+
async {
152+
usleep(1000);
153+
sendeq(self->fd[0], "bbbb", 0);
154+
}
155+
156+
/* Even SOCK_STREAM does not wait if at least one byte is read. */
157+
recveq(self->fd[1], "aa", 100, MSG_PEEK);
158+
159+
recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
160+
}
161+
162+
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)