1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023 Andreas Bock <andreas.bock@virtual-arts-software.de> 5 * Copyright (c) 2023 Mark Johnston <markj@FreeBSD.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are 9 * 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 14 * the 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/event.h> 30 #include <sys/procdesc.h> 31 #include <sys/stat.h> 32 #include <sys/user.h> 33 #include <sys/wait.h> 34 35 #include <err.h> 36 #include <fcntl.h> 37 #include <signal.h> 38 #include <unistd.h> 39 40 #include <atf-c.h> 41 42 /* 43 * A regression test for bugzilla 275286. 44 */ 45 ATF_TC_WITHOUT_HEAD(shared_table_filt_sig); 46 ATF_TC_BODY(shared_table_filt_sig, tc) 47 { 48 struct sigaction sa; 49 pid_t pid; 50 int error, status; 51 52 sa.sa_handler = SIG_IGN; 53 sigemptyset(&sa.sa_mask); 54 sa.sa_flags = 0; 55 error = sigaction(SIGINT, &sa, NULL); 56 ATF_REQUIRE(error == 0); 57 58 pid = rfork(RFPROC); 59 ATF_REQUIRE(pid != -1); 60 if (pid == 0) { 61 struct kevent ev; 62 int kq; 63 64 kq = kqueue(); 65 if (kq < 0) 66 err(1, "kqueue"); 67 EV_SET(&ev, SIGINT, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0, 68 NULL); 69 if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0) 70 err(2, "kevent"); 71 if (kevent(kq, NULL, 0, &ev, 1, NULL) < 0) 72 err(3, "kevent"); 73 _exit(0); 74 } 75 76 /* Wait for the child to block in kevent(). */ 77 usleep(100000); 78 79 error = kill(pid, SIGINT); 80 ATF_REQUIRE(error == 0); 81 82 error = waitpid(pid, &status, 0); 83 ATF_REQUIRE(error != -1); 84 ATF_REQUIRE(WIFEXITED(status)); 85 ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); 86 } 87 88 #define TIMER_FORKED 0 89 #define TIMER_TIMEOUT 1 90 91 #define RECV_TIMER 0x01 92 #define RECV_VNODE 0x02 93 #define RECV_CLOREAD 0x04 94 #define RECV_ERROR 0x80 95 96 static const struct cponfork_recv { 97 const char *recv_error_desc; 98 unsigned int recv_bit; 99 bool recv_parent_only; 100 } cponfork_recv[] = { 101 { "EVFILT_TIMER did not fire", RECV_TIMER, false }, 102 { "EVFILT_VNODE expected with creation of canary", RECV_VNODE, false }, 103 { "EVFILT_READ received for fd closed on fork", RECV_CLOREAD, true }, 104 }; 105 106 static void 107 cponfork_notes_mask_check(unsigned int mask, bool childmask) 108 { 109 const struct cponfork_recv *rcv; 110 unsigned int expect; 111 112 ATF_REQUIRE(mask != RECV_ERROR); 113 for (size_t i = 0; i < nitems(cponfork_recv); i++) { 114 rcv = &cponfork_recv[i]; 115 116 expect = childmask && rcv->recv_parent_only ? 0 : rcv->recv_bit; 117 ATF_REQUIRE_EQ_MSG(expect, mask & rcv->recv_bit, 118 "%s (%s, mask %x)", rcv->recv_error_desc, 119 childmask ? "child" : "parent", 120 mask); 121 } 122 } 123 124 static unsigned int 125 cponfork_notes_mask(bool inchild) 126 { 127 const struct cponfork_recv *rcv; 128 unsigned int mask = 0; 129 130 for (size_t i = 0; i < nitems(cponfork_recv); i++) { 131 rcv = &cponfork_recv[i]; 132 133 if (!inchild || !rcv->recv_parent_only) 134 mask |= rcv->recv_bit; 135 } 136 137 ATF_REQUIRE(mask != 0); 138 return (mask); 139 } 140 141 static int 142 cponfork_notes_check(int kq, int clofd) 143 { 144 struct kevent ev; 145 unsigned int mask; 146 int error, received = 0; 147 148 mask = cponfork_notes_mask(true); 149 150 EV_SET(&ev, TIMER_TIMEOUT, EVFILT_TIMER, 151 EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_SECONDS, 4, NULL); 152 error = kevent(kq, &ev, 1, NULL, 0, NULL); 153 if (error == -1) 154 return (RECV_ERROR); 155 156 while ((received & mask) != mask) { 157 error = kevent(kq, NULL, 0, &ev, 1, NULL); 158 if (error < 0) 159 return (RECV_ERROR); 160 else if (error == 0) 161 break; 162 163 switch (ev.filter) { 164 case EVFILT_TIMER: 165 if (ev.ident == TIMER_TIMEOUT) 166 return (received | RECV_ERROR); 167 168 received |= RECV_TIMER; 169 break; 170 case EVFILT_VNODE: 171 received |= RECV_VNODE; 172 break; 173 case EVFILT_READ: 174 if ((int)ev.ident != clofd) 175 return (received | RECV_ERROR); 176 received |= RECV_CLOREAD; 177 break; 178 } 179 } 180 181 return (received); 182 } 183 184 ATF_TC_WITHOUT_HEAD(cponfork_notes); 185 ATF_TC_BODY(cponfork_notes, tc) 186 { 187 struct kevent ev[3]; 188 siginfo_t info; 189 int clofd, dfd, error, kq, pdfd, pmask; 190 pid_t pid; 191 192 kq = kqueuex(KQUEUE_CPONFORK); 193 ATF_REQUIRE(kq >= 0); 194 195 dfd = open(".", O_DIRECTORY); 196 ATF_REQUIRE(dfd >= 0); 197 198 clofd = kqueue(); 199 ATF_REQUIRE(clofd >= 0); 200 201 /* 202 * Setup an event on clofd that we can trigger to make it readable, 203 * as we'll want this ready to go when we fork to be sure that if we 204 * *were* going to receive an event from it, it would have occurred 205 * before the three-second timer that would normally close out the child 206 * fires. 207 */ 208 EV_SET(&ev[0], 0, EVFILT_USER, EV_ADD | EV_ENABLE, 0, 0, NULL); 209 error = kevent(clofd, &ev[0], 1, NULL, 0, NULL); 210 ATF_REQUIRE(error != -1); 211 212 /* 213 * Every event we setup here we should expect to observe in both the 214 * child and the parent, with exception to the EVFILT_READ of clofd. We 215 * expect that one to be dropped in the child when the kqueue it's 216 * attached to goes away, thus its exclusion from the child mask. 217 */ 218 EV_SET(&ev[0], dfd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, 219 NOTE_WRITE, 0, NULL); 220 EV_SET(&ev[1], TIMER_FORKED, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 221 NOTE_SECONDS, 3, NULL); 222 EV_SET(&ev[2], clofd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 223 0, NULL); 224 error = kevent(kq, &ev[0], 3, NULL, 0, NULL); 225 ATF_REQUIRE(error != -1); 226 227 /* Fire off an event to make clofd readable. */ 228 EV_SET(&ev[0], 0, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); 229 error = kevent(clofd, &ev[0], 1, NULL, 0, NULL); 230 231 /* 232 * We're only using pdfork here for the kill-on-exit semantics, in case 233 * the parent fails to setup some context needed for one of our events 234 * to fire. 235 */ 236 pid = pdfork(&pdfd, 0); 237 ATF_REQUIRE(pid != -1); 238 if (pid == 0) { 239 struct kinfo_file kf = { .kf_structsize = sizeof(kf) }; 240 241 if (fcntl(kq, F_KINFO, &kf) != 0) 242 _exit(RECV_ERROR); 243 else if (kf.kf_type != KF_TYPE_KQUEUE) 244 _exit(RECV_ERROR); 245 246 if (fcntl(clofd, F_KINFO, &kf) != -1 || errno != EBADF) 247 _exit(RECV_ERROR); 248 249 _exit(cponfork_notes_check(kq, clofd)); 250 } 251 252 /* Setup anything we need to fire off any of our events above. */ 253 error = mkdir("canary", 0755); 254 ATF_REQUIRE(error == 0); 255 256 /* 257 * We'll simultaneously do the same exercise of polling the kqueue in 258 * the parent, to demonstrate that forking doesn't "steal" any of the 259 * knotes from us -- all of the events we've added are one-shot and 260 * still fire twice (once in parent, once in child). 261 */ 262 pmask = cponfork_notes_check(kq, clofd); 263 cponfork_notes_mask_check(pmask, false); 264 265 /* Wait for the child to timeout or observe the timer. */ 266 error = waitid(P_PID, pid, &info, WEXITED); 267 ATF_REQUIRE(error != -1); 268 ATF_REQUIRE_EQ(CLD_EXITED, info.si_code); 269 cponfork_notes_mask_check(info.si_status, true); 270 } 271 272 ATF_TP_ADD_TCS(tp) 273 { 274 ATF_TP_ADD_TC(tp, shared_table_filt_sig); 275 ATF_TP_ADD_TC(tp, cponfork_notes); 276 277 return (atf_no_error()); 278 } 279