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 #define RECV_ALL (RECV_TIMER | RECV_VNODE) 96 97 static int 98 cponfork_notes_check(int kq, int clofd) 99 { 100 struct kevent ev; 101 int error, received = 0; 102 103 EV_SET(&ev, TIMER_TIMEOUT, EVFILT_TIMER, 104 EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_SECONDS, 4, NULL); 105 error = kevent(kq, &ev, 1, NULL, 0, NULL); 106 if (error == -1) 107 return (RECV_ERROR); 108 109 while ((received & RECV_ALL) != RECV_ALL) { 110 error = kevent(kq, NULL, 0, &ev, 1, NULL); 111 if (error < 0) 112 return (RECV_ERROR); 113 else if (error == 0) 114 break; 115 116 switch (ev.filter) { 117 case EVFILT_TIMER: 118 if (ev.ident == TIMER_TIMEOUT) 119 return (received | RECV_ERROR); 120 121 received |= RECV_TIMER; 122 break; 123 case EVFILT_VNODE: 124 received |= RECV_VNODE; 125 break; 126 case EVFILT_READ: 127 if ((int)ev.ident != clofd) 128 return (received | RECV_ERROR); 129 received |= RECV_CLOREAD; 130 break; 131 } 132 } 133 134 return (received); 135 } 136 137 ATF_TC_WITHOUT_HEAD(cponfork_notes); 138 ATF_TC_BODY(cponfork_notes, tc) 139 { 140 struct kevent ev[3]; 141 int clofd, dfd, error, kq, pdfd, pmask, status; 142 pid_t pid; 143 144 kq = kqueuex(KQUEUE_CPONFORK); 145 ATF_REQUIRE(kq >= 0); 146 147 dfd = open(".", O_DIRECTORY); 148 ATF_REQUIRE(dfd >= 0); 149 150 clofd = kqueue(); 151 ATF_REQUIRE(clofd >= 0); 152 153 /* 154 * Setup an event on clofd that we can trigger to make it readable, 155 * as we'll want this ready to go when we fork to be sure that if we 156 * *were* going to receive an event from it, it would have occurred 157 * before the three-second timer that would normally close out the child 158 * fires. 159 */ 160 EV_SET(&ev[0], 0, EVFILT_USER, EV_ADD | EV_ENABLE, 0, 0, NULL); 161 error = kevent(clofd, &ev[0], 1, NULL, 0, NULL); 162 ATF_REQUIRE(error != -1); 163 164 /* 165 * Every event we setup here we should expect to observe in both the 166 * child and the parent, with exception to the EVFILT_READ of clofd. We 167 * except that one to be dropped in the child when the kqueue it's 168 * attached to goes away, thus its exclusion from the RECV_ALL mask. 169 */ 170 EV_SET(&ev[0], dfd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, 171 NOTE_WRITE, 0, NULL); 172 EV_SET(&ev[1], TIMER_FORKED, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, 173 NOTE_SECONDS, 3, NULL); 174 EV_SET(&ev[2], clofd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 175 0, NULL); 176 error = kevent(kq, &ev[0], 3, NULL, 0, NULL); 177 ATF_REQUIRE(error != -1); 178 179 /* Fire off an event to make clofd readable. */ 180 EV_SET(&ev[0], 0, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); 181 error = kevent(clofd, &ev[0], 1, NULL, 0, NULL); 182 183 /* 184 * We're only using pdfork here for the kill-on-exit semantics, in case 185 * the parent fails to setup some context needed for one of our events 186 * to fire. 187 */ 188 pid = pdfork(&pdfd, 0); 189 ATF_REQUIRE(pid != -1); 190 if (pid == 0) { 191 struct kinfo_file kf = { .kf_structsize = sizeof(kf) }; 192 193 if (fcntl(kq, F_KINFO, &kf) != 0) 194 _exit(RECV_ERROR); 195 else if (kf.kf_type != KF_TYPE_KQUEUE) 196 _exit(RECV_ERROR); 197 198 _exit(cponfork_notes_check(kq, clofd)); 199 } 200 201 /* Setup anything we need to fire off any of our events above. */ 202 error = mkdir("canary", 0755); 203 ATF_REQUIRE(error == 0); 204 205 /* 206 * We'll simultaneously do the same exercise of polling the kqueue in 207 * the parent, to demonstrate that forking doesn't "steal" any of the 208 * knotes from us -- all of the events we've added are one-shot and 209 * still fire twice (once in parent, once in child). 210 */ 211 pmask = cponfork_notes_check(kq, clofd); 212 ATF_REQUIRE_EQ(pmask, RECV_ALL | RECV_CLOREAD); 213 214 /* Wait for the child to timeout or observe the timer. */ 215 _Static_assert(RECV_ALL <= UCHAR_MAX, 216 "Too many events to observe -- switch from waitpid -> waitid"); 217 error = waitpid(pid, &status, 0); 218 ATF_REQUIRE(error != -1); 219 ATF_REQUIRE(WIFEXITED(status)); 220 ATF_REQUIRE_EQ(WEXITSTATUS(status), RECV_ALL); 221 } 222 223 ATF_TP_ADD_TCS(tp) 224 { 225 ATF_TP_ADD_TC(tp, shared_table_filt_sig); 226 ATF_TP_ADD_TC(tp, cponfork_notes); 227 228 return (atf_no_error()); 229 } 230