xref: /openbsd-src/regress/sys/kern/kqueue/kqueue-regress.c (revision a3c3d22fc5014d243e24ef846ab11e251994f624)
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