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
58static 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
97static int
98tcp_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
113static int
114unix_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
131static 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
139static int
140sleep_accept(int s)
141{
142	int rv;
143
144	rv = accept(s, NULL, NULL);
145
146	return (rv == -1 ? errno : 0);
147}
148
149static int
150sleep_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
162static int
163sleep_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
178static int
179sleep_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
199typedef int sleep_syscall_t(int);
200static 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
207struct test_ctx {
208	struct test const *test;
209	int s;
210	int result;
211};
212
213static void *
214sleep_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
222static void
223run_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
281ATF_TC_WITHOUT_HEAD(all);
282ATF_TC_BODY(all, tc)
283{
284	for (u_int f = 0; f < nitems(protos); f++)
285		run_tests(&protos[f]);
286}
287
288ATF_TP_ADD_TCS(tp)
289{
290	ATF_TP_ADD_TC(tp, all);
291
292	return (atf_no_error());
293}
294