1 /* $OpenBSD: kqueue-regress.c,v 1.4 2020/03/08 09:40:52 visa Exp $ */ 2 /* 3 * Written by Anton Lindqvist <anton@openbsd.org> 2018 Public Domain 4 */ 5 6 #include <sys/types.h> 7 #include <sys/event.h> 8 #include <sys/resource.h> 9 #include <sys/select.h> 10 #include <sys/time.h> 11 #include <sys/wait.h> 12 13 #include <assert.h> 14 #include <err.h> 15 #include <poll.h> 16 #include <signal.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <unistd.h> 20 21 #include "main.h" 22 23 static int do_regress1(void); 24 static int do_regress2(void); 25 static int do_regress3(void); 26 static int do_regress4(void); 27 static int do_regress5(void); 28 29 static void make_chain(int); 30 31 int 32 do_regress(int n) 33 { 34 switch (n) { 35 case 1: 36 return do_regress1(); 37 case 2: 38 return do_regress2(); 39 case 3: 40 return do_regress3(); 41 case 4: 42 return do_regress4(); 43 case 5: 44 return do_regress5(); 45 default: 46 errx(1, "unknown regress test number %d", n); 47 } 48 } 49 50 /* 51 * Regression test for NULL-deref in knote_processexit(). 52 */ 53 static int 54 do_regress1(void) 55 { 56 struct kevent kev[2]; 57 int kq; 58 59 ASS((kq = kqueue()) >= 0, 60 warn("kqueue")); 61 62 EV_SET(&kev[0], kq, EVFILT_READ, EV_ADD, 0, 0, NULL); 63 EV_SET(&kev[1], SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 64 ASS(kevent(kq, kev, 2, NULL, 0, NULL) == 0, 65 warn("can't register events on kqueue")); 66 67 /* kq intentionally left open */ 68 69 return 0; 70 } 71 72 /* 73 * Regression test for use-after-free in kqueue_close(). 74 */ 75 static int 76 do_regress2(void) 77 { 78 pid_t pid; 79 int i, status; 80 81 /* Run twice in order to trigger the panic faster, if still present. */ 82 for (i = 0; i < 2; i++) { 83 pid = fork(); 84 if (pid == -1) 85 err(1, "fork"); 86 87 if (pid == 0) { 88 struct kevent kev[1]; 89 int p0[2], p1[2]; 90 int kq; 91 92 if (pipe(p0) == -1) 93 err(1, "pipe"); 94 if (pipe(p1) == -1) 95 err(1, "pipe"); 96 97 kq = kqueue(); 98 if (kq == -1) 99 err(1, "kqueue"); 100 101 EV_SET(&kev[0], p0[0], EVFILT_READ, EV_ADD, 0, 0, NULL); 102 if (kevent(kq, kev, 1, NULL, 0, NULL) == -1) 103 err(1, "kevent"); 104 105 EV_SET(&kev[0], p1[1], EVFILT_READ, EV_ADD, 0, 0, NULL); 106 if (kevent(kq, kev, 1, NULL, 0, NULL) == -1) 107 err(1, "kevent"); 108 109 EV_SET(&kev[0], p1[0], EVFILT_READ, EV_ADD, 0, 0, NULL); 110 if (kevent(kq, kev, 1, NULL, 0, NULL) == -1) 111 err(1, "kevent"); 112 113 _exit(0); 114 } 115 116 if (waitpid(pid, &status, 0) == -1) 117 err(1, "waitpid"); 118 assert(WIFEXITED(status)); 119 assert(WEXITSTATUS(status) == 0); 120 } 121 122 return 0; 123 } 124 125 /* 126 * Regression test for kernel stack exhaustion. 127 */ 128 static int 129 do_regress3(void) 130 { 131 pid_t pid; 132 int dir, status; 133 134 for (dir = 0; dir < 2; dir++) { 135 pid = fork(); 136 if (pid == -1) 137 err(1, "fork"); 138 139 if (pid == 0) { 140 make_chain(dir); 141 _exit(0); 142 } 143 144 if (waitpid(pid, &status, 0) == -1) 145 err(1, "waitpid"); 146 assert(WIFEXITED(status)); 147 assert(WEXITSTATUS(status) == 0); 148 } 149 150 return 0; 151 } 152 153 static void 154 make_chain(int dir) 155 { 156 struct kevent kev[1]; 157 int i, kq, prev; 158 159 /* 160 * Build a chain of kqueues and leave the files open. 161 * If the chain is long enough and properly oriented, a broken kernel 162 * can exhaust the stack when this process exits. 163 */ 164 for (i = 0, prev = -1; i < 120; i++, prev = kq) { 165 kq = kqueue(); 166 if (kq == -1) 167 err(1, "kqueue"); 168 if (prev == -1) 169 continue; 170 171 if (dir == 0) { 172 EV_SET(&kev[0], prev, EVFILT_READ, EV_ADD, 0, 0, NULL); 173 if (kevent(kq, kev, 1, NULL, 0, NULL) == -1) 174 err(1, "kevent"); 175 } else { 176 EV_SET(&kev[0], kq, EVFILT_READ, EV_ADD, 0, 0, NULL); 177 if (kevent(prev, kev, 1, NULL, 0, NULL) == -1) 178 err(1, "kevent"); 179 } 180 } 181 } 182 183 /* 184 * Regression test for kernel stack exhaustion. 185 */ 186 static int 187 do_regress4(void) 188 { 189 static const int nkqueues = 500; 190 struct kevent kev[1]; 191 struct rlimit rlim; 192 struct timespec ts; 193 int fds[2], i, kq = -1, prev; 194 195 if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) 196 err(1, "getrlimit"); 197 if (rlim.rlim_cur < nkqueues + 8) { 198 rlim.rlim_cur = nkqueues + 8; 199 if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) { 200 printf("RLIMIT_NOFILE is too low and can't raise it\n"); 201 printf("SKIPPED\n"); 202 exit(0); 203 } 204 } 205 206 if (pipe(fds) == -1) 207 err(1, "pipe"); 208 209 /* Build a chain of kqueus. The first kqueue refers to the pipe. */ 210 for (i = 0, prev = fds[0]; i < nkqueues; i++, prev = kq) { 211 kq = kqueue(); 212 if (kq == -1) 213 err(1, "kqueue"); 214 215 EV_SET(&kev[0], prev, EVFILT_READ, EV_ADD, 0, 0, NULL); 216 if (kevent(kq, kev, 1, NULL, 0, NULL) == -1) 217 err(1, "kevent"); 218 } 219 220 /* 221 * Trigger a cascading event through the chain. 222 * If the chain is long enough, a broken kernel can run out 223 * of kernel stack space. 224 */ 225 write(fds[1], "x", 1); 226 227 /* 228 * Check that the event gets propagated. 229 * The propagation is not instantaneous, so allow a brief pause. 230 */ 231 ts.tv_sec = 5; 232 ts.tv_nsec = 0; 233 assert(kevent(kq, NULL, 0, kev, 1, NULL) == 1); 234 235 return 0; 236 } 237 238 /* 239 * Regression test for select and poll with kqueue. 240 */ 241 static int 242 do_regress5(void) 243 { 244 fd_set fdset; 245 struct kevent kev[1]; 246 struct pollfd pfd[1]; 247 struct timeval tv; 248 int fds[2], kq, ret; 249 250 if (pipe(fds) == -1) 251 err(1, "pipe"); 252 253 kq = kqueue(); 254 if (kq == -1) 255 err(1, "kqueue"); 256 EV_SET(&kev[0], fds[0], EVFILT_READ, EV_ADD, 0, 0, NULL); 257 if (kevent(kq, kev, 1, NULL, 0, NULL) == -1) 258 err(1, "kevent"); 259 260 /* Check that no event is reported. */ 261 262 FD_ZERO(&fdset); 263 FD_SET(kq, &fdset); 264 tv.tv_sec = 0; 265 tv.tv_usec = 0; 266 ret = select(kq + 1, &fdset, NULL, NULL, &tv); 267 if (ret == -1) 268 err(1, "select"); 269 assert(ret == 0); 270 271 pfd[0].fd = kq; 272 pfd[0].events = POLLIN; 273 pfd[0].revents = 0; 274 ret = poll(pfd, 1, 0); 275 if (ret == -1) 276 err(1, "poll"); 277 assert(ret == 0); 278 279 /* Trigger an event. */ 280 write(fds[1], "x", 1); 281 282 /* Check that the event gets reported. */ 283 284 FD_ZERO(&fdset); 285 FD_SET(kq, &fdset); 286 tv.tv_sec = 5; 287 tv.tv_usec = 0; 288 ret = select(kq + 1, &fdset, NULL, NULL, &tv); 289 if (ret == -1) 290 err(1, "select"); 291 assert(ret == 1); 292 assert(FD_ISSET(kq, &fdset)); 293 294 pfd[0].fd = kq; 295 pfd[0].events = POLLIN; 296 pfd[0].revents = 0; 297 ret = poll(pfd, 1, 5000); 298 if (ret == -1) 299 err(1, "poll"); 300 assert(ret == 1); 301 assert(pfd[0].revents & POLLIN); 302 303 return 0; 304 } 305