xref: /freebsd/tests/sys/kern/listener_wakeup.c (revision f126890a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2024 Gleb Smirnoff <glebius@FreeBSD.org>
5  * Copyright (c) 2018 Rozhuk Ivan <rozhuk.im@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
32 #include <netinet/in.h>
33 #include <netinet/tcp.h>
34 #include <sys/select.h>
35 #include <sys/event.h>
36 #include <poll.h>
37 
38 #include <stdbool.h>
39 #include <unistd.h>
40 #include <time.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <pthread.h>
44 #include <pthread_np.h>
45 
46 #include <atf-c.h>
47 
48 /*
49  * This test runs several scenarios when sleep(9) on a listen(2)ing socket is
50  * interrupted by shutdown(2) or by close(2).  What should happen in that case
51  * is not specified, neither is documented.  However, there is certain behavior
52  * that we have and this test makes sure it is preserved.  The known software
53  * to rely on the behavior is FreeSWITCH telephony software (see bug 227259).
54  * There might be more. This test is based on submission with the bug, bugzilla
55  * attachment 192260.
56  */
57 
58 static const struct test {
59 	enum {
60 		SLEEP_ACCEPT = 0,
61 		SLEEP_SELECT,
62 		SLEEP_POLL,
63 		SLEEP_KQUEUE,
64 		NSLEEP
65 	}		sleep;
66 	enum {
67 		WAKEUP_SHUTDOWN,
68 		WAKEUP_CLOSE,
69 	}		wakeup;
70 	enum {
71 		AFTER,
72 		BEFORE,
73 	}		when;
74 	bool		nonblock;
75 	int		result;
76 } tests[] = {
77 	{ SLEEP_ACCEPT,	WAKEUP_SHUTDOWN, AFTER, false,	ECONNABORTED },
78 	{ SLEEP_SELECT,	WAKEUP_SHUTDOWN, AFTER, false,	0 },
79 	{ SLEEP_POLL,	WAKEUP_SHUTDOWN, AFTER, false,	0 },
80 	{ SLEEP_KQUEUE,	WAKEUP_SHUTDOWN, AFTER, false,	0 },
81 	{ SLEEP_ACCEPT,	WAKEUP_CLOSE,	 AFTER, false,	ETIMEDOUT },
82 	{ SLEEP_SELECT,	WAKEUP_CLOSE,	 AFTER, false,	EBADF },
83 	{ SLEEP_POLL,	WAKEUP_CLOSE,	 AFTER, false,	0 },
84 	{ SLEEP_KQUEUE,	WAKEUP_CLOSE,	 AFTER, false,	0 },
85 	{ SLEEP_ACCEPT,	WAKEUP_SHUTDOWN, BEFORE, false,	ECONNABORTED },
86 	{ SLEEP_SELECT,	WAKEUP_SHUTDOWN, BEFORE, false,	0 },
87 	{ SLEEP_POLL,	WAKEUP_SHUTDOWN, BEFORE, false,	0 },
88 	{ SLEEP_KQUEUE,	WAKEUP_SHUTDOWN, BEFORE, false,	0 },
89 	{ SLEEP_SELECT,	WAKEUP_SHUTDOWN, AFTER, true,	0 },
90 	{ SLEEP_POLL,	WAKEUP_SHUTDOWN, AFTER, true,	0 },
91 	{ SLEEP_KQUEUE,	WAKEUP_SHUTDOWN, AFTER, true,	0 },
92 	{ SLEEP_SELECT,	WAKEUP_SHUTDOWN, BEFORE, true,	0 },
93 	{ SLEEP_POLL,	WAKEUP_SHUTDOWN, BEFORE, true,	0 },
94 	{ SLEEP_KQUEUE,	WAKEUP_SHUTDOWN, BEFORE, true,	0 },
95 };
96 
97 static int
98 tcp_listen(void)
99 {
100 	struct sockaddr_in sin = {
101 		.sin_family = PF_INET,
102 		.sin_len = sizeof(sin),
103 	};
104 	int s;
105 
106 	ATF_REQUIRE((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1);
107 	ATF_REQUIRE(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
108 	ATF_REQUIRE(listen(s, -1) == 0);
109 
110 	return (s);
111 }
112 
113 static int
114 unix_listen(void)
115 {
116 	struct sockaddr_un sun = {
117 		.sun_family = AF_UNIX,
118 		.sun_len = sizeof(sun),
119 		.sun_path = "listen-shutdown-test.sock",
120 	};
121 	int s;
122 
123 	ATF_REQUIRE((s = socket(PF_UNIX, SOCK_STREAM, 0)) != -1);
124 	(void)unlink(sun.sun_path);
125 	ATF_REQUIRE(bind(s, (struct sockaddr *)&sun, sizeof(sun)) == 0);
126 	ATF_REQUIRE(listen(s, -1) == 0);
127 
128 	return (s);
129 }
130 
131 static const struct proto {
132 	const char *name;
133 	int (*listen)(void);
134 } protos[] = {
135 	{ "PF_INET", tcp_listen },
136 	{ "PF_UNIX", unix_listen },
137 };
138 
139 static int
140 sleep_accept(int s)
141 {
142 	int rv;
143 
144 	rv = accept(s, NULL, NULL);
145 
146 	return (rv == -1 ? errno : 0);
147 }
148 
149 static int
150 sleep_select(int s)
151 {
152 	fd_set fds;
153 	int rv;
154 
155 	FD_ZERO(&fds);
156 	FD_SET(s, &fds);
157 	rv = select(s + 1, &fds, &fds, &fds, NULL);
158 
159 	return (rv == -1 ? errno : 0);
160 }
161 
162 static int
163 sleep_poll(int s)
164 {
165 	struct pollfd fds = {
166 		.fd = s,
167 		.events = (POLLIN | POLLPRI | POLLRDNORM | POLLWRNORM |
168 			   POLLRDBAND | POLLWRBAND),
169 		.revents = 0,
170 	};
171 	int rv;
172 
173 	rv = poll(&fds, 1, INFTIM);
174 
175 	return (rv == -1 ? errno : 0);
176 }
177 
178 static int
179 sleep_kqueue(int s)
180 {
181 	struct kevent kev;
182 	int kq, error;
183 
184 	ATF_REQUIRE((kq = kqueue()) != -1);
185 	EV_SET(&kev, s, EVFILT_READ, EV_ADD, 0, 0, NULL);
186 	if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1) {
187 		error = errno;
188 	} else {
189 		if (kev.flags & EV_ERROR)
190 			error = (int)kev.data;
191 		else
192 			error = 0;
193 	}
194 	ATF_REQUIRE(close(kq) == 0);
195 
196 	return (error);
197 }
198 
199 typedef int sleep_syscall_t(int);
200 static sleep_syscall_t *sleep_syscalls[NSLEEP] = {
201 	[SLEEP_ACCEPT] = sleep_accept,
202 	[SLEEP_SELECT] = sleep_select,
203 	[SLEEP_POLL] = sleep_poll,
204 	[SLEEP_KQUEUE] = sleep_kqueue,
205 };
206 
207 struct test_ctx {
208 	struct test const *test;
209 	int s;
210 	int result;
211 };
212 
213 static void *
214 sleep_syscall_thread(void *data) {
215 	struct test_ctx *ctx = data;
216 
217 	ctx->result = sleep_syscalls[ctx->test->sleep](ctx->s);
218 
219 	return (NULL);
220 }
221 
222 static void
223 run_tests(const struct proto *pr)
224 {
225 	pthread_t tid;
226 	struct timespec ts;
227 	int error;
228 
229 	for (u_int i = 0; i < nitems(tests); i ++) {
230 		struct test const *t = &tests[i];
231 		struct test_ctx ctx = {
232 			.test = t,
233 			/* Note: tested syscalls don't return this. */
234 			.result = ETIMEDOUT,
235 		};
236 
237 		ctx.s = pr->listen();
238 		if (t->nonblock)
239 			ATF_REQUIRE(fcntl(ctx.s, F_SETFL, O_NONBLOCK) != -1);
240 
241 		if (t->when == AFTER) {
242 			ATF_REQUIRE(pthread_create(&tid, NULL,
243 			    sleep_syscall_thread, &ctx) == 0);
244 			usleep(100000);
245 		}
246 
247 		switch (t->wakeup) {
248 		case WAKEUP_SHUTDOWN:
249 			ATF_REQUIRE(shutdown(ctx.s, SHUT_RDWR) == -1);
250 			ATF_REQUIRE(errno == ENOTCONN);
251 			break;
252 		case WAKEUP_CLOSE:
253 			ATF_REQUIRE(close(ctx.s) == 0);
254 			break;
255 		}
256 
257 		if (t->when == BEFORE) {
258 			ATF_REQUIRE(pthread_create(&tid, NULL,
259 			    sleep_syscall_thread, &ctx) == 0);
260 			usleep(100000);
261 		}
262 
263 		clock_gettime(CLOCK_REALTIME, &ts);
264 		ts.tv_sec++;
265 		if ((error = pthread_timedjoin_np(tid, NULL, &ts)) != 0) {
266 			ATF_REQUIRE(pthread_cancel(tid) == 0);
267 			ATF_REQUIRE(error == ETIMEDOUT);
268 			ATF_REQUIRE(ctx.result == ETIMEDOUT);
269 		}
270 
271 		ATF_REQUIRE_MSG(ctx.result == t->result,
272 		    "proto %s sleeping syscall #%d wakeup #%d nb %d, "
273 		    "expected %d, got %d", pr->name, t->sleep, t->wakeup,
274 		    t->nonblock, t->result, ctx.result);
275 
276 		if (t->wakeup == WAKEUP_SHUTDOWN)
277 			ATF_REQUIRE(close(ctx.s) == 0);
278 	}
279 }
280 
281 ATF_TC_WITHOUT_HEAD(all);
282 ATF_TC_BODY(all, tc)
283 {
284 	for (u_int f = 0; f < nitems(protos); f++)
285 		run_tests(&protos[f]);
286 }
287 
288 ATF_TP_ADD_TCS(tp)
289 {
290 	ATF_TP_ADD_TC(tp, all);
291 
292 	return (atf_no_error());
293 }
294