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
FIXTURE(so_peek_off)11 FIXTURE(so_peek_off)
12 {
13 int fd[2]; /* 0: sender, 1: receiver */
14 };
15
FIXTURE_VARIANT(so_peek_off)16 FIXTURE_VARIANT(so_peek_off)
17 {
18 int type;
19 };
20
FIXTURE_VARIANT_ADD(so_peek_off,stream)21 FIXTURE_VARIANT_ADD(so_peek_off, stream)
22 {
23 .type = SOCK_STREAM,
24 };
25
FIXTURE_VARIANT_ADD(so_peek_off,dgram)26 FIXTURE_VARIANT_ADD(so_peek_off, dgram)
27 {
28 .type = SOCK_DGRAM,
29 };
30
FIXTURE_VARIANT_ADD(so_peek_off,seqpacket)31 FIXTURE_VARIANT_ADD(so_peek_off, seqpacket)
32 {
33 .type = SOCK_SEQPACKET,
34 };
35
FIXTURE_SETUP(so_peek_off)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
FIXTURE_TEARDOWN(so_peek_off)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
TEST_F(so_peek_off,single_chunk)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
TEST_F(so_peek_off,two_chunks)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
TEST_F(so_peek_off,two_chunks_blocking)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
TEST_F(so_peek_off,two_chunks_overlap)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
TEST_F(so_peek_off,two_chunks_overlap_blocking)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
163