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