xref: /linux/tools/testing/selftests/net/af_unix/so_peek_off.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
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 = 5,
40 		.tv_usec = 0,
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 peekoffeq(fd, expected)					\
80 	do {							\
81 		socklen_t optlen = sizeof(int);			\
82 		int off = -1;					\
83 		int ret;					\
84 								\
85 		ret = getsockopt(fd, SOL_SOCKET, SO_PEEK_OFF,	\
86 				 &off, &optlen);		\
87 		ASSERT_EQ(0, ret);				\
88 		ASSERT_EQ((socklen_t)sizeof(off), optlen);	\
89 		ASSERT_EQ(expected, off);			\
90 	} while (0)
91 
92 #define async							\
93 	for (pid_t pid = (pid = fork(),				\
94 			  pid < 0 ?				\
95 			  __TH_LOG("Failed to start async {}"),	\
96 			  _metadata->exit_code = KSFT_FAIL,	\
97 			  __bail(1, _metadata),			\
98 			  0xdead :				\
99 			  pid);					\
100 	     !pid; exit(0))
101 
102 TEST_F(so_peek_off, single_chunk)
103 {
104 	sendeq(self->fd[0], "aaaabbbb", 0);
105 
106 	recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
107 	peekoffeq(self->fd[1], 4);
108 	recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
109 	peekoffeq(self->fd[1], 8);
110 
111 	recveq(self->fd[1], "aaaabbbb", 8, 0);
112 	peekoffeq(self->fd[1], 0);
113 }
114 
115 TEST_F(so_peek_off, two_chunks)
116 {
117 	sendeq(self->fd[0], "aaaa", 0);
118 	sendeq(self->fd[0], "bbbb", 0);
119 
120 	recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
121 	peekoffeq(self->fd[1], 4);
122 	recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
123 	peekoffeq(self->fd[1], 8);
124 
125 	recveq(self->fd[1], "aaaa", 4, 0);
126 	recveq(self->fd[1], "bbbb", 4, 0);
127 	peekoffeq(self->fd[1], 0);
128 }
129 
130 TEST_F(so_peek_off, two_chunks_blocking)
131 {
132 	async {
133 		usleep(1000);
134 		sendeq(self->fd[0], "aaaa", 0);
135 	}
136 
137 	recveq(self->fd[1], "aaaa", 4, MSG_PEEK);
138 	peekoffeq(self->fd[1], 4);
139 
140 	async {
141 		usleep(1000);
142 		sendeq(self->fd[0], "bbbb", 0);
143 	}
144 
145 	/* goto again; -> goto redo; in unix_stream_read_generic(). */
146 	recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
147 	peekoffeq(self->fd[1], 8);
148 
149 	recveq(self->fd[1], "aaaa", 4, 0);
150 	recveq(self->fd[1], "bbbb", 4, 0);
151 	peekoffeq(self->fd[1], 0);
152 }
153 
154 TEST_F(so_peek_off, two_chunks_overlap)
155 {
156 	sendeq(self->fd[0], "aaaa", 0);
157 	recveq(self->fd[1], "aa", 2, MSG_PEEK);
158 	peekoffeq(self->fd[1], 2);
159 
160 	sendeq(self->fd[0], "bbbb", 0);
161 
162 	if (variant->type == SOCK_STREAM) {
163 		/* SOCK_STREAM tries to fill the buffer. */
164 		recveq(self->fd[1], "aabb", 4, MSG_PEEK);
165 		peekoffeq(self->fd[1], 6);
166 		recveq(self->fd[1], "bb", 100, MSG_PEEK);
167 		peekoffeq(self->fd[1], 8);
168 	} else {
169 		/* SOCK_DGRAM and SOCK_SEQPACKET returns at the skb boundary. */
170 		recveq(self->fd[1], "aa", 100, MSG_PEEK);
171 		peekoffeq(self->fd[1], 4);
172 		recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
173 		peekoffeq(self->fd[1], 8);
174 	}
175 
176 	recveq(self->fd[1], "aaaa", 4, 0);
177 	recveq(self->fd[1], "bbbb", 4, 0);
178 	peekoffeq(self->fd[1], 0);
179 }
180 
181 TEST_F(so_peek_off, two_chunks_overlap_blocking)
182 {
183 	async {
184 		usleep(1000);
185 		sendeq(self->fd[0], "aaaa", 0);
186 	}
187 
188 	recveq(self->fd[1], "aa", 2, MSG_PEEK);
189 	peekoffeq(self->fd[1], 2);
190 
191 	async {
192 		usleep(1000);
193 		sendeq(self->fd[0], "bbbb", 0);
194 	}
195 
196 	/* Even SOCK_STREAM does not wait if at least one byte is read. */
197 	recveq(self->fd[1], "aa", 100, MSG_PEEK);
198 	peekoffeq(self->fd[1], 4);
199 
200 	recveq(self->fd[1], "bbbb", 100, MSG_PEEK);
201 	peekoffeq(self->fd[1], 8);
202 
203 	recveq(self->fd[1], "aaaa", 4, 0);
204 	recveq(self->fd[1], "bbbb", 4, 0);
205 	peekoffeq(self->fd[1], 0);
206 }
207 
208 TEST_HARNESS_MAIN
209