1*a3c3d22fSmillert /* $OpenBSD: kqueue-regress.c,v 1.5 2022/03/29 19:04:19 millert Exp $ */
2e7479016Santon /*
3e7479016Santon * Written by Anton Lindqvist <anton@openbsd.org> 2018 Public Domain
4e7479016Santon */
5e7479016Santon
6e7479016Santon #include <sys/types.h>
7e7479016Santon #include <sys/event.h>
8*a3c3d22fSmillert #include <sys/mman.h>
9247280a7Svisa #include <sys/resource.h>
10247280a7Svisa #include <sys/select.h>
11e7479016Santon #include <sys/time.h>
12e03a59bcSanton #include <sys/wait.h>
13e7479016Santon
14e03a59bcSanton #include <assert.h>
15e7479016Santon #include <err.h>
16247280a7Svisa #include <poll.h>
17e7479016Santon #include <signal.h>
18247280a7Svisa #include <stdio.h>
19e7479016Santon #include <stdlib.h>
20*a3c3d22fSmillert #include <string.h>
21e03a59bcSanton #include <unistd.h>
22e7479016Santon
23e7479016Santon #include "main.h"
24e7479016Santon
25e03a59bcSanton static int do_regress1(void);
26e03a59bcSanton static int do_regress2(void);
27a98ae779Svisa static int do_regress3(void);
28247280a7Svisa static int do_regress4(void);
29247280a7Svisa static int do_regress5(void);
30*a3c3d22fSmillert static int do_regress6(void);
31a98ae779Svisa
32a98ae779Svisa static void make_chain(int);
33e03a59bcSanton
34e7479016Santon int
do_regress(int n)35e03a59bcSanton do_regress(int n)
36e03a59bcSanton {
37e03a59bcSanton switch (n) {
38e03a59bcSanton case 1:
39e03a59bcSanton return do_regress1();
40e03a59bcSanton case 2:
41e03a59bcSanton return do_regress2();
42a98ae779Svisa case 3:
43a98ae779Svisa return do_regress3();
44247280a7Svisa case 4:
45247280a7Svisa return do_regress4();
46247280a7Svisa case 5:
47247280a7Svisa return do_regress5();
48*a3c3d22fSmillert case 6:
49*a3c3d22fSmillert return do_regress6();
50e03a59bcSanton default:
51e03a59bcSanton errx(1, "unknown regress test number %d", n);
52e03a59bcSanton }
53e03a59bcSanton }
54e03a59bcSanton
55e03a59bcSanton /*
56e03a59bcSanton * Regression test for NULL-deref in knote_processexit().
57e03a59bcSanton */
58e03a59bcSanton static int
do_regress1(void)59e03a59bcSanton do_regress1(void)
60e7479016Santon {
61e7479016Santon struct kevent kev[2];
62e7479016Santon int kq;
63e7479016Santon
64e7479016Santon ASS((kq = kqueue()) >= 0,
65e7479016Santon warn("kqueue"));
66e7479016Santon
67e7479016Santon EV_SET(&kev[0], kq, EVFILT_READ, EV_ADD, 0, 0, NULL);
68e7479016Santon EV_SET(&kev[1], SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
69e7479016Santon ASS(kevent(kq, kev, 2, NULL, 0, NULL) == 0,
70e7479016Santon warn("can't register events on kqueue"));
71e7479016Santon
72e7479016Santon /* kq intentionally left open */
73e7479016Santon
74e7479016Santon return 0;
75e7479016Santon }
76e03a59bcSanton
77e03a59bcSanton /*
78e03a59bcSanton * Regression test for use-after-free in kqueue_close().
79e03a59bcSanton */
80e03a59bcSanton static int
do_regress2(void)81e03a59bcSanton do_regress2(void)
82e03a59bcSanton {
83e03a59bcSanton pid_t pid;
84e03a59bcSanton int i, status;
85e03a59bcSanton
86e03a59bcSanton /* Run twice in order to trigger the panic faster, if still present. */
87e03a59bcSanton for (i = 0; i < 2; i++) {
88e03a59bcSanton pid = fork();
89e03a59bcSanton if (pid == -1)
90e03a59bcSanton err(1, "fork");
91e03a59bcSanton
92e03a59bcSanton if (pid == 0) {
93e03a59bcSanton struct kevent kev[1];
94e03a59bcSanton int p0[2], p1[2];
95e03a59bcSanton int kq;
96e03a59bcSanton
97e03a59bcSanton if (pipe(p0) == -1)
98e03a59bcSanton err(1, "pipe");
99e03a59bcSanton if (pipe(p1) == -1)
100e03a59bcSanton err(1, "pipe");
101e03a59bcSanton
102e03a59bcSanton kq = kqueue();
103e03a59bcSanton if (kq == -1)
104e03a59bcSanton err(1, "kqueue");
105e03a59bcSanton
106e03a59bcSanton EV_SET(&kev[0], p0[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
107e03a59bcSanton if (kevent(kq, kev, 1, NULL, 0, NULL) == -1)
108e03a59bcSanton err(1, "kevent");
109e03a59bcSanton
110e03a59bcSanton EV_SET(&kev[0], p1[1], EVFILT_READ, EV_ADD, 0, 0, NULL);
111e03a59bcSanton if (kevent(kq, kev, 1, NULL, 0, NULL) == -1)
112e03a59bcSanton err(1, "kevent");
113e03a59bcSanton
114e03a59bcSanton EV_SET(&kev[0], p1[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
115e03a59bcSanton if (kevent(kq, kev, 1, NULL, 0, NULL) == -1)
116e03a59bcSanton err(1, "kevent");
117e03a59bcSanton
118e03a59bcSanton _exit(0);
119e03a59bcSanton }
120e03a59bcSanton
121e03a59bcSanton if (waitpid(pid, &status, 0) == -1)
122e03a59bcSanton err(1, "waitpid");
123e03a59bcSanton assert(WIFEXITED(status));
124e03a59bcSanton assert(WEXITSTATUS(status) == 0);
125e03a59bcSanton }
126e03a59bcSanton
127e03a59bcSanton return 0;
128e03a59bcSanton }
129a98ae779Svisa
130a98ae779Svisa /*
131a98ae779Svisa * Regression test for kernel stack exhaustion.
132a98ae779Svisa */
133a98ae779Svisa static int
do_regress3(void)134a98ae779Svisa do_regress3(void)
135a98ae779Svisa {
136a98ae779Svisa pid_t pid;
137a98ae779Svisa int dir, status;
138a98ae779Svisa
139a98ae779Svisa for (dir = 0; dir < 2; dir++) {
140a98ae779Svisa pid = fork();
141a98ae779Svisa if (pid == -1)
142a98ae779Svisa err(1, "fork");
143a98ae779Svisa
144a98ae779Svisa if (pid == 0) {
145a98ae779Svisa make_chain(dir);
146a98ae779Svisa _exit(0);
147a98ae779Svisa }
148a98ae779Svisa
149a98ae779Svisa if (waitpid(pid, &status, 0) == -1)
150a98ae779Svisa err(1, "waitpid");
151a98ae779Svisa assert(WIFEXITED(status));
152a98ae779Svisa assert(WEXITSTATUS(status) == 0);
153a98ae779Svisa }
154a98ae779Svisa
155a98ae779Svisa return 0;
156a98ae779Svisa }
157a98ae779Svisa
158a98ae779Svisa static void
make_chain(int dir)159a98ae779Svisa make_chain(int dir)
160a98ae779Svisa {
161a98ae779Svisa struct kevent kev[1];
162a98ae779Svisa int i, kq, prev;
163a98ae779Svisa
164a98ae779Svisa /*
165a98ae779Svisa * Build a chain of kqueues and leave the files open.
166a98ae779Svisa * If the chain is long enough and properly oriented, a broken kernel
167a98ae779Svisa * can exhaust the stack when this process exits.
168a98ae779Svisa */
169a98ae779Svisa for (i = 0, prev = -1; i < 120; i++, prev = kq) {
170a98ae779Svisa kq = kqueue();
171a98ae779Svisa if (kq == -1)
172a98ae779Svisa err(1, "kqueue");
173a98ae779Svisa if (prev == -1)
174a98ae779Svisa continue;
175a98ae779Svisa
176a98ae779Svisa if (dir == 0) {
177a98ae779Svisa EV_SET(&kev[0], prev, EVFILT_READ, EV_ADD, 0, 0, NULL);
178a98ae779Svisa if (kevent(kq, kev, 1, NULL, 0, NULL) == -1)
179a98ae779Svisa err(1, "kevent");
180a98ae779Svisa } else {
181a98ae779Svisa EV_SET(&kev[0], kq, EVFILT_READ, EV_ADD, 0, 0, NULL);
182a98ae779Svisa if (kevent(prev, kev, 1, NULL, 0, NULL) == -1)
183a98ae779Svisa err(1, "kevent");
184a98ae779Svisa }
185a98ae779Svisa }
186a98ae779Svisa }
187247280a7Svisa
188247280a7Svisa /*
189247280a7Svisa * Regression test for kernel stack exhaustion.
190247280a7Svisa */
191247280a7Svisa static int
do_regress4(void)192247280a7Svisa do_regress4(void)
193247280a7Svisa {
194247280a7Svisa static const int nkqueues = 500;
195247280a7Svisa struct kevent kev[1];
196247280a7Svisa struct rlimit rlim;
197247280a7Svisa struct timespec ts;
198247280a7Svisa int fds[2], i, kq = -1, prev;
199247280a7Svisa
200247280a7Svisa if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
201247280a7Svisa err(1, "getrlimit");
202247280a7Svisa if (rlim.rlim_cur < nkqueues + 8) {
203247280a7Svisa rlim.rlim_cur = nkqueues + 8;
204247280a7Svisa if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
205247280a7Svisa printf("RLIMIT_NOFILE is too low and can't raise it\n");
206247280a7Svisa printf("SKIPPED\n");
207247280a7Svisa exit(0);
208247280a7Svisa }
209247280a7Svisa }
210247280a7Svisa
211247280a7Svisa if (pipe(fds) == -1)
212247280a7Svisa err(1, "pipe");
213247280a7Svisa
214247280a7Svisa /* Build a chain of kqueus. The first kqueue refers to the pipe. */
215247280a7Svisa for (i = 0, prev = fds[0]; i < nkqueues; i++, prev = kq) {
216247280a7Svisa kq = kqueue();
217247280a7Svisa if (kq == -1)
218247280a7Svisa err(1, "kqueue");
219247280a7Svisa
220247280a7Svisa EV_SET(&kev[0], prev, EVFILT_READ, EV_ADD, 0, 0, NULL);
221247280a7Svisa if (kevent(kq, kev, 1, NULL, 0, NULL) == -1)
222247280a7Svisa err(1, "kevent");
223247280a7Svisa }
224247280a7Svisa
225247280a7Svisa /*
226247280a7Svisa * Trigger a cascading event through the chain.
227247280a7Svisa * If the chain is long enough, a broken kernel can run out
228247280a7Svisa * of kernel stack space.
229247280a7Svisa */
230247280a7Svisa write(fds[1], "x", 1);
231247280a7Svisa
232247280a7Svisa /*
233247280a7Svisa * Check that the event gets propagated.
234247280a7Svisa * The propagation is not instantaneous, so allow a brief pause.
235247280a7Svisa */
236247280a7Svisa ts.tv_sec = 5;
237247280a7Svisa ts.tv_nsec = 0;
238247280a7Svisa assert(kevent(kq, NULL, 0, kev, 1, NULL) == 1);
239247280a7Svisa
240247280a7Svisa return 0;
241247280a7Svisa }
242247280a7Svisa
243247280a7Svisa /*
244247280a7Svisa * Regression test for select and poll with kqueue.
245247280a7Svisa */
246247280a7Svisa static int
do_regress5(void)247247280a7Svisa do_regress5(void)
248247280a7Svisa {
249247280a7Svisa fd_set fdset;
250247280a7Svisa struct kevent kev[1];
251247280a7Svisa struct pollfd pfd[1];
252247280a7Svisa struct timeval tv;
253247280a7Svisa int fds[2], kq, ret;
254247280a7Svisa
255247280a7Svisa if (pipe(fds) == -1)
256247280a7Svisa err(1, "pipe");
257247280a7Svisa
258247280a7Svisa kq = kqueue();
259247280a7Svisa if (kq == -1)
260247280a7Svisa err(1, "kqueue");
261247280a7Svisa EV_SET(&kev[0], fds[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
262247280a7Svisa if (kevent(kq, kev, 1, NULL, 0, NULL) == -1)
263247280a7Svisa err(1, "kevent");
264247280a7Svisa
265247280a7Svisa /* Check that no event is reported. */
266247280a7Svisa
267247280a7Svisa FD_ZERO(&fdset);
268247280a7Svisa FD_SET(kq, &fdset);
269247280a7Svisa tv.tv_sec = 0;
270247280a7Svisa tv.tv_usec = 0;
271247280a7Svisa ret = select(kq + 1, &fdset, NULL, NULL, &tv);
272247280a7Svisa if (ret == -1)
273247280a7Svisa err(1, "select");
274247280a7Svisa assert(ret == 0);
275247280a7Svisa
276247280a7Svisa pfd[0].fd = kq;
277247280a7Svisa pfd[0].events = POLLIN;
278247280a7Svisa pfd[0].revents = 0;
279247280a7Svisa ret = poll(pfd, 1, 0);
280247280a7Svisa if (ret == -1)
281247280a7Svisa err(1, "poll");
282247280a7Svisa assert(ret == 0);
283247280a7Svisa
284247280a7Svisa /* Trigger an event. */
285247280a7Svisa write(fds[1], "x", 1);
286247280a7Svisa
287247280a7Svisa /* Check that the event gets reported. */
288247280a7Svisa
289247280a7Svisa FD_ZERO(&fdset);
290247280a7Svisa FD_SET(kq, &fdset);
291247280a7Svisa tv.tv_sec = 5;
292247280a7Svisa tv.tv_usec = 0;
293247280a7Svisa ret = select(kq + 1, &fdset, NULL, NULL, &tv);
294247280a7Svisa if (ret == -1)
295247280a7Svisa err(1, "select");
296247280a7Svisa assert(ret == 1);
297247280a7Svisa assert(FD_ISSET(kq, &fdset));
298247280a7Svisa
299247280a7Svisa pfd[0].fd = kq;
300247280a7Svisa pfd[0].events = POLLIN;
301247280a7Svisa pfd[0].revents = 0;
302247280a7Svisa ret = poll(pfd, 1, 5000);
303247280a7Svisa if (ret == -1)
304247280a7Svisa err(1, "poll");
305247280a7Svisa assert(ret == 1);
306247280a7Svisa assert(pfd[0].revents & POLLIN);
307247280a7Svisa
308247280a7Svisa return 0;
309247280a7Svisa }
310*a3c3d22fSmillert
311*a3c3d22fSmillert int
test_regress6(int kq,size_t len)312*a3c3d22fSmillert test_regress6(int kq, size_t len)
313*a3c3d22fSmillert {
314*a3c3d22fSmillert const struct timespec nap_time = { 0, 1 };
315*a3c3d22fSmillert int i, kstatus, wstatus;
316*a3c3d22fSmillert struct kevent event;
317*a3c3d22fSmillert pid_t child, pid;
318*a3c3d22fSmillert void *addr;
319*a3c3d22fSmillert
320*a3c3d22fSmillert child = fork();
321*a3c3d22fSmillert switch (child) {
322*a3c3d22fSmillert case -1:
323*a3c3d22fSmillert warn("fork");
324*a3c3d22fSmillert return -1;
325*a3c3d22fSmillert case 0:
326*a3c3d22fSmillert /* fork a bunch of zombies to keep the reaper busy, then exit */
327*a3c3d22fSmillert signal(SIGCHLD, SIG_IGN);
328*a3c3d22fSmillert for (i = 0; i < 1000; i++) {
329*a3c3d22fSmillert if (fork() == 0) {
330*a3c3d22fSmillert /* Dirty some memory so uvm_exit has work. */
331*a3c3d22fSmillert addr = mmap(NULL, len, PROT_READ|PROT_WRITE,
332*a3c3d22fSmillert MAP_ANON, -1, 0);
333*a3c3d22fSmillert if (addr == MAP_FAILED)
334*a3c3d22fSmillert err(1, "mmap");
335*a3c3d22fSmillert memset(addr, 'A', len);
336*a3c3d22fSmillert nanosleep(&nap_time, NULL);
337*a3c3d22fSmillert _exit(2);
338*a3c3d22fSmillert }
339*a3c3d22fSmillert }
340*a3c3d22fSmillert nanosleep(&nap_time, NULL);
341*a3c3d22fSmillert _exit(1);
342*a3c3d22fSmillert default:
343*a3c3d22fSmillert /* parent */
344*a3c3d22fSmillert break;
345*a3c3d22fSmillert }
346*a3c3d22fSmillert
347*a3c3d22fSmillert /* Register NOTE_EXIT and wait for child. */
348*a3c3d22fSmillert EV_SET(&event, child, EVFILT_PROC, EV_ADD|EV_ONESHOT, NOTE_EXIT, 0,
349*a3c3d22fSmillert NULL);
350*a3c3d22fSmillert if (kevent(kq, &event, 1, &event, 1, NULL) != 1)
351*a3c3d22fSmillert err(1, "kevent");
352*a3c3d22fSmillert if (event.flags & EV_ERROR)
353*a3c3d22fSmillert errx(1, "kevent: %s", strerror(event.data));
354*a3c3d22fSmillert if (event.ident != child)
355*a3c3d22fSmillert errx(1, "expected child %d, got %lu", child, event.ident);
356*a3c3d22fSmillert kstatus = event.data;
357*a3c3d22fSmillert if (!WIFEXITED(kstatus))
358*a3c3d22fSmillert errx(1, "child did not exit?");
359*a3c3d22fSmillert
360*a3c3d22fSmillert pid = waitpid(child, &wstatus, WNOHANG);
361*a3c3d22fSmillert switch (pid) {
362*a3c3d22fSmillert case -1:
363*a3c3d22fSmillert err(1, "waitpid %d", child);
364*a3c3d22fSmillert case 0:
365*a3c3d22fSmillert printf("kevent: child %d exited %d\n", child,
366*a3c3d22fSmillert WEXITSTATUS(kstatus));
367*a3c3d22fSmillert printf("waitpid: child %d not ready\n", child);
368*a3c3d22fSmillert break;
369*a3c3d22fSmillert default:
370*a3c3d22fSmillert if (wstatus != kstatus) {
371*a3c3d22fSmillert /* macOS has a bug where kstatus is 0 */
372*a3c3d22fSmillert warnx("kevent status 0x%x != waitpid status 0x%x",
373*a3c3d22fSmillert kstatus, wstatus);
374*a3c3d22fSmillert }
375*a3c3d22fSmillert break;
376*a3c3d22fSmillert }
377*a3c3d22fSmillert
378*a3c3d22fSmillert return pid;
379*a3c3d22fSmillert }
380*a3c3d22fSmillert
381*a3c3d22fSmillert /*
382*a3c3d22fSmillert * Regression test for NOTE_EXIT waitability.
383*a3c3d22fSmillert */
384*a3c3d22fSmillert static int
do_regress6(void)385*a3c3d22fSmillert do_regress6(void)
386*a3c3d22fSmillert {
387*a3c3d22fSmillert int i, kq, page_size, rc;
388*a3c3d22fSmillert struct rlimit rlim;
389*a3c3d22fSmillert
390*a3c3d22fSmillert /* Bump process limits since we fork a lot. */
391*a3c3d22fSmillert if (getrlimit(RLIMIT_NPROC, &rlim) == -1)
392*a3c3d22fSmillert err(1, "getrlimit(RLIMIT_NPROC)");
393*a3c3d22fSmillert rlim.rlim_cur = rlim.rlim_max;
394*a3c3d22fSmillert if (setrlimit(RLIMIT_NPROC, &rlim) == -1)
395*a3c3d22fSmillert err(1, "setrlimit(RLIMIT_NPROC)");
396*a3c3d22fSmillert
397*a3c3d22fSmillert kq = kqueue();
398*a3c3d22fSmillert if (kq == -1)
399*a3c3d22fSmillert err(1, "kqueue");
400*a3c3d22fSmillert
401*a3c3d22fSmillert page_size = getpagesize();
402*a3c3d22fSmillert
403*a3c3d22fSmillert /* This test is inherently racey but fails within a few iterations. */
404*a3c3d22fSmillert for (i = 0; i < 25; i++) {
405*a3c3d22fSmillert rc = test_regress6(kq, page_size);
406*a3c3d22fSmillert switch (rc) {
407*a3c3d22fSmillert case -1:
408*a3c3d22fSmillert goto done;
409*a3c3d22fSmillert case 0:
410*a3c3d22fSmillert printf("child not ready when NOTE_EXIT received");
411*a3c3d22fSmillert if (i != 0)
412*a3c3d22fSmillert printf(" (%d iterations)", i + 1);
413*a3c3d22fSmillert putchar('\n');
414*a3c3d22fSmillert goto done;
415*a3c3d22fSmillert default:
416*a3c3d22fSmillert /* keep trying */
417*a3c3d22fSmillert continue;
418*a3c3d22fSmillert }
419*a3c3d22fSmillert }
420*a3c3d22fSmillert printf("child exited as expected when NOTE_EXIT received\n");
421*a3c3d22fSmillert
422*a3c3d22fSmillert done:
423*a3c3d22fSmillert close(kq);
424*a3c3d22fSmillert return rc <= 0;
425*a3c3d22fSmillert }
426