xref: /netbsd-src/tests/lib/libc/sys/t_fork.c (revision 00831369bc3c415b2bf2d57144b32e3e361fad38)
1*00831369Sandvar /*	$NetBSD: t_fork.c,v 1.5 2022/05/24 20:08:38 andvar Exp $	*/
2497013adSkamil 
3497013adSkamil /*-
4115bc670Skamil  * Copyright (c) 2018, 2019 The NetBSD Foundation, Inc.
5497013adSkamil  * All rights reserved.
6497013adSkamil  *
7497013adSkamil  * Redistribution and use in source and binary forms, with or without
8497013adSkamil  * modification, are permitted provided that the following conditions
9497013adSkamil  * are met:
10497013adSkamil  * 1. Redistributions of source code must retain the above copyright
11497013adSkamil  *    notice, this list of conditions and the following disclaimer.
12497013adSkamil  * 2. Redistributions in binary form must reproduce the above copyright
13497013adSkamil  *    notice, this list of conditions and the following disclaimer in the
14497013adSkamil  *    documentation and/or other materials provided with the distribution.
15497013adSkamil  *
16497013adSkamil  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17497013adSkamil  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18497013adSkamil  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19497013adSkamil  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20497013adSkamil  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21497013adSkamil  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22497013adSkamil  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23497013adSkamil  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24497013adSkamil  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25497013adSkamil  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26497013adSkamil  * POSSIBILITY OF SUCH DAMAGE.
27497013adSkamil  */
28497013adSkamil 
29497013adSkamil #include <sys/cdefs.h>
30115bc670Skamil __COPYRIGHT("@(#) Copyright (c) 2018, 2019\
31497013adSkamil  The NetBSD Foundation, inc. All rights reserved.");
32*00831369Sandvar __RCSID("$NetBSD: t_fork.c,v 1.5 2022/05/24 20:08:38 andvar Exp $");
33497013adSkamil 
34497013adSkamil #include <sys/param.h>
35497013adSkamil #include <sys/types.h>
36497013adSkamil #include <sys/sysctl.h>
37497013adSkamil #include <sys/wait.h>
38115bc670Skamil #include <sched.h>
39497013adSkamil #include <signal.h>
40115bc670Skamil #include <stdbool.h>
41497013adSkamil #include <stdlib.h>
42497013adSkamil #include <unistd.h>
43497013adSkamil #include <err.h>
44497013adSkamil #include <errno.h>
45497013adSkamil 
46497013adSkamil #include <atf-c.h>
47497013adSkamil 
48497013adSkamil #ifdef VFORK
49497013adSkamil #define FORK vfork
50497013adSkamil #else
51497013adSkamil #define FORK fork
52497013adSkamil #endif
53497013adSkamil 
54497013adSkamil /*
55497013adSkamil  * A child process cannot call atf functions and expect them to magically
56497013adSkamil  * work like in the parent.
57497013adSkamil  * The printf(3) messaging from a child will not work out of the box as well
58*00831369Sandvar  * without establishing a communication protocol with its parent. To not
59497013adSkamil  * overcomplicate the tests - do not log from a child and use err(3)/errx(3)
60497013adSkamil  * wrapped with ASSERT_EQ()/ASSERT_NEQ() as that is guaranteed to work.
61497013adSkamil  */
62497013adSkamil #define ASSERT_EQ(x, y)								\
63497013adSkamil do {										\
64497013adSkamil 	uintmax_t vx = (x);							\
65497013adSkamil 	uintmax_t vy = (y);							\
66497013adSkamil 	int ret = vx == vy;							\
67497013adSkamil 	if (!ret)								\
68497013adSkamil 		errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: "		\
69497013adSkamil 		    "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__,		\
70497013adSkamil 		    #x, vx, #y, vy);						\
71497013adSkamil } while (/*CONSTCOND*/0)
72497013adSkamil 
73497013adSkamil #define ASSERT_NEQ(x, y)							\
74497013adSkamil do {										\
75497013adSkamil 	uintmax_t vx = (x);							\
76497013adSkamil 	uintmax_t vy = (y);							\
77497013adSkamil 	int ret = vx != vy;							\
78497013adSkamil 	if (!ret)								\
79497013adSkamil 		errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: "		\
80497013adSkamil 		    "%s(%ju) != %s(%ju)", __FILE__, __LINE__, __func__,		\
81497013adSkamil 		    #x, vx, #y, vy);						\
82497013adSkamil } while (/*CONSTCOND*/0)
83497013adSkamil 
84497013adSkamil static pid_t
await_stopped_child(pid_t process)85497013adSkamil await_stopped_child(pid_t process)
86497013adSkamil {
87497013adSkamil 	struct kinfo_proc2 *p = NULL;
88497013adSkamil 	size_t i, len;
89497013adSkamil 	pid_t child = -1;
90497013adSkamil 
91497013adSkamil 	int name[] = {
92497013adSkamil 		[0] = CTL_KERN,
93497013adSkamil 		[1] = KERN_PROC2,
94497013adSkamil 		[2] = KERN_PROC_ALL,
95497013adSkamil 		[3] = 0,
96497013adSkamil 		[4] = sizeof(struct kinfo_proc2),
97497013adSkamil 		[5] = 0
98497013adSkamil 	};
99497013adSkamil 
100497013adSkamil 	const size_t namelen = __arraycount(name);
101497013adSkamil 
102497013adSkamil 	/* Await the process becoming a zombie */
103497013adSkamil 	while(1) {
104497013adSkamil 		name[5] = 0;
105497013adSkamil 
106497013adSkamil 		ASSERT_EQ(sysctl(name, namelen, 0, &len, NULL, 0), 0);
107497013adSkamil 
108497013adSkamil 		ASSERT_EQ(reallocarr(&p, len, sizeof(struct kinfo_proc2)), 0);
109497013adSkamil 
110497013adSkamil 		name[5] = len;
111497013adSkamil 
112497013adSkamil 		ASSERT_EQ(sysctl(name, namelen, p, &len, NULL, 0), 0);
113497013adSkamil 
114497013adSkamil 		for (i = 0; i < len/sizeof(struct kinfo_proc2); i++) {
115497013adSkamil 			if (p[i].p_pid == getpid())
116497013adSkamil 				continue;
117497013adSkamil 			if (p[i].p_ppid != process)
118497013adSkamil 				continue;
119497013adSkamil 			if (p[i].p_stat != LSSTOP)
120497013adSkamil 				continue;
121497013adSkamil 			child = p[i].p_pid;
122497013adSkamil 			break;
123497013adSkamil 		}
124497013adSkamil 
125497013adSkamil 		if (child != -1)
126497013adSkamil 			break;
127497013adSkamil 
128497013adSkamil 		ASSERT_EQ(usleep(1000), 0);
129497013adSkamil 	}
130497013adSkamil 
131497013adSkamil 	/* Free the buffer */
132497013adSkamil 	ASSERT_EQ(reallocarr(&p, 0, sizeof(struct kinfo_proc2)), 0);
133497013adSkamil 
134497013adSkamil 	return child;
135497013adSkamil }
136497013adSkamil 
137497013adSkamil static void
raise_raw(int sig)138497013adSkamil raise_raw(int sig)
139497013adSkamil {
140497013adSkamil 	int rv, status;
141497013adSkamil 	pid_t child, parent, watcher, wpid;
142497013adSkamil 	int expect_core = (sig == SIGABRT) ? 1 : 0;
143497013adSkamil 
144497013adSkamil 	/*
145497013adSkamil 	 * Spawn a dedicated thread to watch for a stopped child and emit
146aa6024afSkamil 	 * the SIGKILL signal to it.
147497013adSkamil 	 *
148497013adSkamil 	 * This is required in vfork(2)ing parent and optional in fork(2).
149497013adSkamil 	 *
150497013adSkamil 	 * vfork(2) might clobber watcher, this means that it's safer and
151497013adSkamil 	 * simpler to reparent this process to initproc and forget about it.
152497013adSkamil 	 */
153497013adSkamil 	if (sig == SIGSTOP
154497013adSkamil #ifndef VFORK
155497013adSkamil 	    || (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
156497013adSkamil #endif
157497013adSkamil 	    ) {
158497013adSkamil 
159497013adSkamil 		parent = getpid();
160497013adSkamil 
161497013adSkamil 		watcher = fork();
162497013adSkamil 		ATF_REQUIRE(watcher != 1);
163497013adSkamil 		if (watcher == 0) {
164497013adSkamil 			/* Double fork(2) trick to reparent to initproc */
165497013adSkamil 			watcher = fork();
166497013adSkamil 			ASSERT_NEQ(watcher, -1);
167497013adSkamil 			if (watcher != 0)
168497013adSkamil 				_exit(0);
169497013adSkamil 
170497013adSkamil 			child = await_stopped_child(parent);
171497013adSkamil 
172497013adSkamil 			errno = 0;
173497013adSkamil 			rv = kill(child, SIGKILL);
174497013adSkamil 			ASSERT_EQ(rv, 0);
175497013adSkamil 			ASSERT_EQ(errno, 0);
176497013adSkamil 
177497013adSkamil 			/* This exit value will be collected by initproc */
178497013adSkamil 			_exit(0);
179497013adSkamil 		}
180497013adSkamil 
181497013adSkamil 		wpid = waitpid(watcher, &status, 0);
182497013adSkamil 
183497013adSkamil 		ATF_REQUIRE_EQ(wpid, watcher);
184497013adSkamil 
185497013adSkamil 		ATF_REQUIRE(WIFEXITED(status));
186497013adSkamil 		ATF_REQUIRE(!WIFCONTINUED(status));
187497013adSkamil 		ATF_REQUIRE(!WIFSIGNALED(status));
188497013adSkamil 		ATF_REQUIRE(!WIFSTOPPED(status));
189497013adSkamil 		ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
190497013adSkamil 	}
191497013adSkamil 
192497013adSkamil 	child = FORK();
193497013adSkamil 	ATF_REQUIRE(child != 1);
194497013adSkamil 	if (child == 0) {
195497013adSkamil 		rv = raise(sig);
196497013adSkamil 		ASSERT_EQ(rv, 0);
197497013adSkamil 		_exit(0);
198497013adSkamil 	}
199497013adSkamil 	wpid = waitpid(child, &status, 0);
200497013adSkamil 
201497013adSkamil 	ATF_REQUIRE_EQ(wpid, child);
202497013adSkamil 
203497013adSkamil 	switch (sig) {
204497013adSkamil 	case SIGKILL:
205497013adSkamil 	case SIGABRT:
206497013adSkamil 	case SIGHUP:
207497013adSkamil 		ATF_REQUIRE(!WIFEXITED(status));
208497013adSkamil 		ATF_REQUIRE(!WIFCONTINUED(status));
209497013adSkamil 		ATF_REQUIRE(WIFSIGNALED(status));
210497013adSkamil 		ATF_REQUIRE(!WIFSTOPPED(status));
211497013adSkamil 		ATF_REQUIRE_EQ(WTERMSIG(status), sig);
212497013adSkamil 		ATF_REQUIRE_EQ(!!WCOREDUMP(status), expect_core);
213497013adSkamil 		break;
214497013adSkamil #ifdef VFORK
215497013adSkamil 	case SIGTSTP:
216497013adSkamil 	case SIGTTIN:
217497013adSkamil 	case SIGTTOU:
218497013adSkamil #endif
219497013adSkamil 	case SIGCONT:
220497013adSkamil 		ATF_REQUIRE(WIFEXITED(status));
221497013adSkamil 		ATF_REQUIRE(!WIFCONTINUED(status));
222497013adSkamil 		ATF_REQUIRE(!WIFSIGNALED(status));
223497013adSkamil 		ATF_REQUIRE(!WIFSTOPPED(status));
224497013adSkamil 		ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
225497013adSkamil 		break;
226497013adSkamil #ifndef VFORK
227497013adSkamil 	case SIGTSTP:
228497013adSkamil 	case SIGTTIN:
229497013adSkamil 	case SIGTTOU:
230497013adSkamil #endif
231497013adSkamil 	case SIGSTOP:
232497013adSkamil 		ATF_REQUIRE(!WIFEXITED(status));
233497013adSkamil 		ATF_REQUIRE(!WIFCONTINUED(status));
234497013adSkamil 		ATF_REQUIRE(WIFSIGNALED(status));
235497013adSkamil 		ATF_REQUIRE(!WIFSTOPPED(status));
236497013adSkamil 		ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL);
237497013adSkamil 		ATF_REQUIRE_EQ(!!WCOREDUMP(status), 0);
238497013adSkamil 	}
239497013adSkamil }
240497013adSkamil 
241497013adSkamil #define RAISE(test, sig)							\
242497013adSkamil ATF_TC(test);									\
243497013adSkamil ATF_TC_HEAD(test, tc)								\
244497013adSkamil {										\
245497013adSkamil 										\
246497013adSkamil 	atf_tc_set_md_var(tc, "descr",						\
247115bc670Skamil 	    "raise " #sig " in a child");					\
248497013adSkamil }										\
249497013adSkamil 										\
250497013adSkamil ATF_TC_BODY(test, tc)								\
251497013adSkamil {										\
252497013adSkamil 										\
253497013adSkamil 	raise_raw(sig);								\
254497013adSkamil }
255497013adSkamil 
RAISE(raise1,SIGKILL)256497013adSkamil RAISE(raise1, SIGKILL) /* non-maskable */
257497013adSkamil RAISE(raise2, SIGSTOP) /* non-maskable */
258497013adSkamil RAISE(raise3, SIGTSTP) /* ignored in vfork(2) */
259497013adSkamil RAISE(raise4, SIGTTIN) /* ignored in vfork(2) */
260497013adSkamil RAISE(raise5, SIGTTOU) /* ignored in vfork(2) */
261497013adSkamil RAISE(raise6, SIGABRT) /* regular abort trap */
262497013adSkamil RAISE(raise7, SIGHUP)  /* hangup */
263497013adSkamil RAISE(raise8, SIGCONT) /* continued? */
264497013adSkamil 
265115bc670Skamil /// ----------------------------------------------------------------------------
266115bc670Skamil 
267115bc670Skamil static int
268115bc670Skamil clone_func(void *arg __unused)
269115bc670Skamil {
270115bc670Skamil 
271115bc670Skamil 	return 0;
272115bc670Skamil }
273115bc670Skamil 
274115bc670Skamil static void
nested_raw(const char * fn,volatile int flags)275115bc670Skamil nested_raw(const char *fn, volatile int flags)
276115bc670Skamil {
277115bc670Skamil 	int status;
278115bc670Skamil 	pid_t child, child2, wpid;
279115bc670Skamil 	const size_t stack_size = 1024 * 1024;
280115bc670Skamil 	void *stack, *stack_base;
281115bc670Skamil 
282115bc670Skamil 	stack = malloc(stack_size);
283115bc670Skamil 	ATF_REQUIRE(stack != NULL);
284115bc670Skamil 
285115bc670Skamil #ifdef __MACHINE_STACK_GROWS_UP
286115bc670Skamil 	stack_base = stack;
287115bc670Skamil #else
288115bc670Skamil 	stack_base = (char *)stack + stack_size;
289115bc670Skamil #endif
290115bc670Skamil 
291115bc670Skamil 	flags |= SIGCHLD;
292115bc670Skamil 
293115bc670Skamil 	child = FORK();
294115bc670Skamil 	ATF_REQUIRE(child != 1);
295115bc670Skamil 	if (child == 0) {
296115bc670Skamil 		if (strcmp(fn, "fork") == 0)
297115bc670Skamil 			child2 = fork();
298115bc670Skamil 		else if (strcmp(fn, "vfork") == 0)
299115bc670Skamil 			child2 = vfork();
300115bc670Skamil 		else if (strcmp(fn, "clone") == 0)
301115bc670Skamil 			child2 = __clone(clone_func, stack_base, flags, NULL);
302115bc670Skamil 		else
303115bc670Skamil 			__unreachable();
304115bc670Skamil 
305115bc670Skamil 		ASSERT_NEQ(child2, -1);
306115bc670Skamil 
307115bc670Skamil 		if ((strcmp(fn, "fork") == 0) || (strcmp(fn, "vfork") == 0)) {
308115bc670Skamil 			if (child2 == 0)
309115bc670Skamil 				_exit(0);
310115bc670Skamil 		}
311115bc670Skamil 
312115bc670Skamil 		wpid = waitpid(child2, &status, 0);
313115bc670Skamil 		ASSERT_EQ(child2, wpid);
314115bc670Skamil 		ASSERT_EQ(!!WIFEXITED(status), true);
315115bc670Skamil 		ASSERT_EQ(!!WIFCONTINUED(status), false);
316115bc670Skamil 		ASSERT_EQ(!!WIFSIGNALED(status), false);
317115bc670Skamil 		ASSERT_EQ(!!WIFSTOPPED(status), false);
318115bc670Skamil 		ASSERT_EQ(WEXITSTATUS(status), 0);
319115bc670Skamil 
320115bc670Skamil 		_exit(0);
321115bc670Skamil 	}
322115bc670Skamil 	wpid = waitpid(child, &status, 0);
323115bc670Skamil 
324115bc670Skamil 	ATF_REQUIRE_EQ(wpid, child);
325115bc670Skamil 	ATF_REQUIRE_EQ(!!WIFEXITED(status), true);
326115bc670Skamil 	ATF_REQUIRE_EQ(!!WIFCONTINUED(status), false);
327115bc670Skamil 	ATF_REQUIRE_EQ(!!WIFSIGNALED(status), false);
328115bc670Skamil 	ATF_REQUIRE_EQ(!!WIFSTOPPED(status), false);
329115bc670Skamil 	ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
330115bc670Skamil }
331115bc670Skamil 
332115bc670Skamil #define NESTED(test, fn, flags)							\
333115bc670Skamil ATF_TC(test);									\
334115bc670Skamil ATF_TC_HEAD(test, tc)								\
335115bc670Skamil {										\
336115bc670Skamil 										\
337115bc670Skamil 	atf_tc_set_md_var(tc, "descr",						\
338115bc670Skamil 	    "Test nested " #fn " in a child");					\
339115bc670Skamil }										\
340115bc670Skamil 										\
341115bc670Skamil ATF_TC_BODY(test, tc)								\
342115bc670Skamil {										\
343115bc670Skamil 										\
344115bc670Skamil 	nested_raw(#fn, flags);							\
345115bc670Skamil }
346115bc670Skamil 
347115bc670Skamil NESTED(nested_fork, fork, 0)
348115bc670Skamil NESTED(nested_vfork, vfork, 0)
349115bc670Skamil NESTED(nested_clone, clone, 0)
NESTED(nested_clone_vm,clone,CLONE_VM)350115bc670Skamil NESTED(nested_clone_vm, clone, CLONE_VM)
351115bc670Skamil NESTED(nested_clone_fs, clone, CLONE_FS)
352115bc670Skamil NESTED(nested_clone_files, clone, CLONE_FILES)
353115bc670Skamil //NESTED(nested_clone_sighand, clone, CLONE_SIGHAND) // XXX
354115bc670Skamil NESTED(nested_clone_vfork, clone, CLONE_VFORK)
355115bc670Skamil 
356497013adSkamil ATF_TP_ADD_TCS(tp)
357497013adSkamil {
358497013adSkamil 	ATF_TP_ADD_TC(tp, raise1);
359497013adSkamil 	ATF_TP_ADD_TC(tp, raise2);
360497013adSkamil 	ATF_TP_ADD_TC(tp, raise3);
361497013adSkamil 	ATF_TP_ADD_TC(tp, raise4);
362497013adSkamil 	ATF_TP_ADD_TC(tp, raise5);
363497013adSkamil 	ATF_TP_ADD_TC(tp, raise6);
364497013adSkamil 	ATF_TP_ADD_TC(tp, raise7);
365497013adSkamil 	ATF_TP_ADD_TC(tp, raise8);
366497013adSkamil 
367115bc670Skamil 	ATF_TP_ADD_TC(tp, nested_fork);
368115bc670Skamil 	ATF_TP_ADD_TC(tp, nested_vfork);
369115bc670Skamil 	ATF_TP_ADD_TC(tp, nested_clone);
370115bc670Skamil 	ATF_TP_ADD_TC(tp, nested_clone_vm);
371115bc670Skamil 	ATF_TP_ADD_TC(tp, nested_clone_fs);
372115bc670Skamil 	ATF_TP_ADD_TC(tp, nested_clone_files);
373115bc670Skamil //	ATF_TP_ADD_TC(tp, nested_clone_sighand); // XXX
374115bc670Skamil 	ATF_TP_ADD_TC(tp, nested_clone_vfork);
375115bc670Skamil 
376497013adSkamil 	return atf_no_error();
377497013adSkamil }
378