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