1*49a6e16fSderaadt /* $OpenBSD: t_fork.c,v 1.5 2021/12/13 16:56:48 deraadt Exp $ */
2abbaa274Smbuhl /* $NetBSD: t_fork.c,v 1.4 2019/04/06 15:41:54 kamil Exp $ */
3abbaa274Smbuhl
4abbaa274Smbuhl /*-
5abbaa274Smbuhl * Copyright (c) 2018, 2019 The NetBSD Foundation, Inc.
6abbaa274Smbuhl * All rights reserved.
7abbaa274Smbuhl *
8abbaa274Smbuhl * Redistribution and use in source and binary forms, with or without
9abbaa274Smbuhl * modification, are permitted provided that the following conditions
10abbaa274Smbuhl * are met:
11abbaa274Smbuhl * 1. Redistributions of source code must retain the above copyright
12abbaa274Smbuhl * notice, this list of conditions and the following disclaimer.
13abbaa274Smbuhl * 2. Redistributions in binary form must reproduce the above copyright
14abbaa274Smbuhl * notice, this list of conditions and the following disclaimer in the
15abbaa274Smbuhl * documentation and/or other materials provided with the distribution.
16abbaa274Smbuhl *
17abbaa274Smbuhl * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18abbaa274Smbuhl * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19abbaa274Smbuhl * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20abbaa274Smbuhl * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21abbaa274Smbuhl * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22abbaa274Smbuhl * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23abbaa274Smbuhl * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24abbaa274Smbuhl * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25abbaa274Smbuhl * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26abbaa274Smbuhl * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27abbaa274Smbuhl * POSSIBILITY OF SUCH DAMAGE.
28abbaa274Smbuhl */
29abbaa274Smbuhl #include "macros.h"
30abbaa274Smbuhl
31*49a6e16fSderaadt #include <sys/types.h>
32*49a6e16fSderaadt #include <sys/signal.h>
33c32913b7Smbuhl #ifdef __OpenBSD__
34c32913b7Smbuhl #include <sys/proc.h>
35c32913b7Smbuhl #endif
36abbaa274Smbuhl #include <sys/sysctl.h>
37abbaa274Smbuhl #include <sys/wait.h>
38abbaa274Smbuhl #include <sched.h>
39abbaa274Smbuhl #include <signal.h>
40abbaa274Smbuhl #include <stdbool.h>
41abbaa274Smbuhl #include <stdlib.h>
42abbaa274Smbuhl #include <unistd.h>
43abbaa274Smbuhl #include <err.h>
44abbaa274Smbuhl #include <errno.h>
45abbaa274Smbuhl
46abbaa274Smbuhl #include "atf-c.h"
47abbaa274Smbuhl
48abbaa274Smbuhl #ifdef VFORK
49abbaa274Smbuhl #define FORK vfork
50abbaa274Smbuhl #else
51abbaa274Smbuhl #define FORK fork
52abbaa274Smbuhl #endif
53abbaa274Smbuhl
54abbaa274Smbuhl /*
55abbaa274Smbuhl * A child process cannot call atf functions and expect them to magically
56abbaa274Smbuhl * work like in the parent.
57abbaa274Smbuhl * The printf(3) messaging from a child will not work out of the box as well
58abbaa274Smbuhl * without estabilishing a communication protocol with its parent. To not
59abbaa274Smbuhl * overcomplicate the tests - do not log from a child and use err(3)/errx(3)
60abbaa274Smbuhl * wrapped with ASSERT_EQ()/ASSERT_NEQ() as that is guaranteed to work.
61abbaa274Smbuhl */
62abbaa274Smbuhl #define ASSERT_EQ(x, y) \
63abbaa274Smbuhl do { \
64abbaa274Smbuhl uintmax_t vx = (x); \
65abbaa274Smbuhl uintmax_t vy = (y); \
66abbaa274Smbuhl int ret = vx == vy; \
67abbaa274Smbuhl if (!ret) \
68abbaa274Smbuhl errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \
69abbaa274Smbuhl "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__, \
70abbaa274Smbuhl #x, vx, #y, vy); \
71abbaa274Smbuhl } while (/*CONSTCOND*/0)
72abbaa274Smbuhl
73abbaa274Smbuhl #define ASSERT_NEQ(x, y) \
74abbaa274Smbuhl do { \
75abbaa274Smbuhl uintmax_t vx = (x); \
76abbaa274Smbuhl uintmax_t vy = (y); \
77abbaa274Smbuhl int ret = vx != vy; \
78abbaa274Smbuhl if (!ret) \
79abbaa274Smbuhl errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \
80abbaa274Smbuhl "%s(%ju) != %s(%ju)", __FILE__, __LINE__, __func__, \
81abbaa274Smbuhl #x, vx, #y, vy); \
82abbaa274Smbuhl } while (/*CONSTCOND*/0)
83abbaa274Smbuhl
84abbaa274Smbuhl static pid_t
await_stopped_child(pid_t process)85abbaa274Smbuhl await_stopped_child(pid_t process)
86abbaa274Smbuhl {
87abbaa274Smbuhl struct kinfo_proc2 *p = NULL;
88abbaa274Smbuhl size_t i, len;
89abbaa274Smbuhl pid_t child = -1;
90abbaa274Smbuhl
91abbaa274Smbuhl int name[] = {
92abbaa274Smbuhl [0] = CTL_KERN,
93abbaa274Smbuhl [1] = KERN_PROC2,
94abbaa274Smbuhl [2] = KERN_PROC_ALL,
95abbaa274Smbuhl [3] = 0,
96abbaa274Smbuhl [4] = sizeof(struct kinfo_proc2),
97abbaa274Smbuhl [5] = 0
98abbaa274Smbuhl };
99abbaa274Smbuhl
100abbaa274Smbuhl const size_t namelen = __arraycount(name);
101abbaa274Smbuhl
102abbaa274Smbuhl /* Await the process becoming a zombie */
103abbaa274Smbuhl while(1) {
104abbaa274Smbuhl name[5] = 0;
105abbaa274Smbuhl
106abbaa274Smbuhl ASSERT_EQ(sysctl(name, namelen, 0, &len, NULL, 0), 0);
107abbaa274Smbuhl
108abbaa274Smbuhl ASSERT_EQ(reallocarr(&p, len, sizeof(struct kinfo_proc2)), 0);
109abbaa274Smbuhl
110abbaa274Smbuhl name[5] = len;
111abbaa274Smbuhl
112abbaa274Smbuhl ASSERT_EQ(sysctl(name, namelen, p, &len, NULL, 0), 0);
113abbaa274Smbuhl
114abbaa274Smbuhl for (i = 0; i < len/sizeof(struct kinfo_proc2); i++) {
115abbaa274Smbuhl if (p[i].p_pid == getpid())
116abbaa274Smbuhl continue;
117abbaa274Smbuhl if (p[i].p_ppid != process)
118abbaa274Smbuhl continue;
119abbaa274Smbuhl if (p[i].p_stat != LSSTOP)
120abbaa274Smbuhl continue;
121abbaa274Smbuhl child = p[i].p_pid;
122abbaa274Smbuhl break;
123abbaa274Smbuhl }
124abbaa274Smbuhl
125abbaa274Smbuhl if (child != -1)
126abbaa274Smbuhl break;
127abbaa274Smbuhl
128abbaa274Smbuhl ASSERT_EQ(usleep(1000), 0);
129abbaa274Smbuhl }
130abbaa274Smbuhl
131abbaa274Smbuhl /* Free the buffer */
132abbaa274Smbuhl ASSERT_EQ(reallocarr(&p, 0, sizeof(struct kinfo_proc2)), 0);
133abbaa274Smbuhl
134abbaa274Smbuhl return child;
135abbaa274Smbuhl }
136abbaa274Smbuhl
137abbaa274Smbuhl static void
raise_raw(int sig)138abbaa274Smbuhl raise_raw(int sig)
139abbaa274Smbuhl {
140abbaa274Smbuhl int rv, status;
141abbaa274Smbuhl pid_t child, parent, watcher, wpid;
142abbaa274Smbuhl int expect_core = (sig == SIGABRT) ? 1 : 0;
143abbaa274Smbuhl
144abbaa274Smbuhl /*
145abbaa274Smbuhl * Spawn a dedicated thread to watch for a stopped child and emit
146abbaa274Smbuhl * the SIGKILL signal to it.
147abbaa274Smbuhl *
148abbaa274Smbuhl * This is required in vfork(2)ing parent and optional in fork(2).
149abbaa274Smbuhl *
150abbaa274Smbuhl * vfork(2) might clobber watcher, this means that it's safer and
151abbaa274Smbuhl * simpler to reparent this process to initproc and forget about it.
152abbaa274Smbuhl */
153abbaa274Smbuhl if (sig == SIGSTOP
154abbaa274Smbuhl #ifndef VFORK
155abbaa274Smbuhl || (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
156abbaa274Smbuhl #endif
157abbaa274Smbuhl ) {
158abbaa274Smbuhl
159abbaa274Smbuhl parent = getpid();
160abbaa274Smbuhl
161abbaa274Smbuhl watcher = fork();
162abbaa274Smbuhl ATF_REQUIRE(watcher != 1);
163abbaa274Smbuhl if (watcher == 0) {
164abbaa274Smbuhl /* Double fork(2) trick to reparent to initproc */
165abbaa274Smbuhl watcher = fork();
166abbaa274Smbuhl ASSERT_NEQ(watcher, -1);
167abbaa274Smbuhl if (watcher != 0)
168abbaa274Smbuhl _exit(0);
169abbaa274Smbuhl
170abbaa274Smbuhl child = await_stopped_child(parent);
171abbaa274Smbuhl
172abbaa274Smbuhl errno = 0;
173abbaa274Smbuhl rv = kill(child, SIGKILL);
174abbaa274Smbuhl ASSERT_EQ(rv, 0);
175abbaa274Smbuhl ASSERT_EQ(errno, 0);
176abbaa274Smbuhl
177abbaa274Smbuhl /* This exit value will be collected by initproc */
178abbaa274Smbuhl _exit(0);
179abbaa274Smbuhl }
180abbaa274Smbuhl
181abbaa274Smbuhl wpid = waitpid(watcher, &status, 0);
182abbaa274Smbuhl
183abbaa274Smbuhl ATF_REQUIRE_EQ(wpid, watcher);
184abbaa274Smbuhl
185abbaa274Smbuhl ATF_REQUIRE(WIFEXITED(status));
186abbaa274Smbuhl ATF_REQUIRE(!WIFCONTINUED(status));
187abbaa274Smbuhl ATF_REQUIRE(!WIFSIGNALED(status));
188abbaa274Smbuhl ATF_REQUIRE(!WIFSTOPPED(status));
189abbaa274Smbuhl ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
190abbaa274Smbuhl }
191abbaa274Smbuhl
192abbaa274Smbuhl child = FORK();
193abbaa274Smbuhl ATF_REQUIRE(child != 1);
194abbaa274Smbuhl if (child == 0) {
195abbaa274Smbuhl rv = raise(sig);
196abbaa274Smbuhl ASSERT_EQ(rv, 0);
197abbaa274Smbuhl _exit(0);
198abbaa274Smbuhl }
199abbaa274Smbuhl wpid = waitpid(child, &status, 0);
200abbaa274Smbuhl
201abbaa274Smbuhl ATF_REQUIRE_EQ(wpid, child);
202abbaa274Smbuhl
203abbaa274Smbuhl switch (sig) {
204abbaa274Smbuhl case SIGKILL:
205abbaa274Smbuhl case SIGABRT:
206abbaa274Smbuhl case SIGHUP:
207abbaa274Smbuhl ATF_REQUIRE(!WIFEXITED(status));
208abbaa274Smbuhl ATF_REQUIRE(!WIFCONTINUED(status));
209abbaa274Smbuhl ATF_REQUIRE(WIFSIGNALED(status));
210abbaa274Smbuhl ATF_REQUIRE(!WIFSTOPPED(status));
211abbaa274Smbuhl ATF_REQUIRE_EQ(WTERMSIG(status), sig);
212abbaa274Smbuhl ATF_REQUIRE_EQ(!!WCOREDUMP(status), expect_core);
213abbaa274Smbuhl break;
214abbaa274Smbuhl #ifdef VFORK
215abbaa274Smbuhl case SIGTSTP:
216abbaa274Smbuhl case SIGTTIN:
217abbaa274Smbuhl case SIGTTOU:
218abbaa274Smbuhl #endif
219abbaa274Smbuhl case SIGCONT:
220abbaa274Smbuhl ATF_REQUIRE(WIFEXITED(status));
221abbaa274Smbuhl ATF_REQUIRE(!WIFCONTINUED(status));
222abbaa274Smbuhl ATF_REQUIRE(!WIFSIGNALED(status));
223abbaa274Smbuhl ATF_REQUIRE(!WIFSTOPPED(status));
224abbaa274Smbuhl ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
225abbaa274Smbuhl break;
226abbaa274Smbuhl #ifndef VFORK
227abbaa274Smbuhl case SIGTSTP:
228abbaa274Smbuhl case SIGTTIN:
229abbaa274Smbuhl case SIGTTOU:
230abbaa274Smbuhl #endif
231abbaa274Smbuhl case SIGSTOP:
232abbaa274Smbuhl ATF_REQUIRE(!WIFEXITED(status));
233abbaa274Smbuhl ATF_REQUIRE(!WIFCONTINUED(status));
234abbaa274Smbuhl ATF_REQUIRE(WIFSIGNALED(status));
235abbaa274Smbuhl ATF_REQUIRE(!WIFSTOPPED(status));
236abbaa274Smbuhl ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL);
237abbaa274Smbuhl ATF_REQUIRE_EQ(!!WCOREDUMP(status), 0);
238abbaa274Smbuhl }
239abbaa274Smbuhl }
240abbaa274Smbuhl
241abbaa274Smbuhl #define RAISE(test, sig) \
242abbaa274Smbuhl ATF_TC(test); \
243abbaa274Smbuhl ATF_TC_HEAD(test, tc) \
244abbaa274Smbuhl { \
245abbaa274Smbuhl \
246abbaa274Smbuhl atf_tc_set_md_var(tc, "descr", \
247abbaa274Smbuhl "raise " #sig " in a child"); \
248abbaa274Smbuhl } \
249abbaa274Smbuhl \
250abbaa274Smbuhl ATF_TC_BODY(test, tc) \
251abbaa274Smbuhl { \
252abbaa274Smbuhl \
253abbaa274Smbuhl raise_raw(sig); \
254abbaa274Smbuhl }
255abbaa274Smbuhl
RAISE(raise1,SIGKILL)256abbaa274Smbuhl RAISE(raise1, SIGKILL) /* non-maskable */
257abbaa274Smbuhl RAISE(raise2, SIGSTOP) /* non-maskable */
258abbaa274Smbuhl RAISE(raise3, SIGTSTP) /* ignored in vfork(2) */
259abbaa274Smbuhl RAISE(raise4, SIGTTIN) /* ignored in vfork(2) */
260abbaa274Smbuhl RAISE(raise5, SIGTTOU) /* ignored in vfork(2) */
261abbaa274Smbuhl RAISE(raise6, SIGABRT) /* regular abort trap */
262abbaa274Smbuhl RAISE(raise7, SIGHUP) /* hangup */
263abbaa274Smbuhl RAISE(raise8, SIGCONT) /* continued? */
264abbaa274Smbuhl
265abbaa274Smbuhl /// ----------------------------------------------------------------------------
266abbaa274Smbuhl
267abbaa274Smbuhl static int
268abbaa274Smbuhl clone_func(void *arg __unused)
269abbaa274Smbuhl {
270abbaa274Smbuhl
271abbaa274Smbuhl return 0;
272abbaa274Smbuhl }
273abbaa274Smbuhl
274abbaa274Smbuhl static void
nested_raw(const char * fn,volatile int flags)275abbaa274Smbuhl nested_raw(const char *fn, volatile int flags)
276abbaa274Smbuhl {
277abbaa274Smbuhl int status;
278abbaa274Smbuhl pid_t child, child2, wpid;
279abbaa274Smbuhl const size_t stack_size = 1024 * 1024;
280abbaa274Smbuhl void *stack, *stack_base;
281abbaa274Smbuhl
282abbaa274Smbuhl stack = malloc(stack_size);
283abbaa274Smbuhl ATF_REQUIRE(stack != NULL);
284abbaa274Smbuhl
285abbaa274Smbuhl #ifdef __MACHINE_STACK_GROWS_UP
286abbaa274Smbuhl stack_base = stack;
287abbaa274Smbuhl #else
288abbaa274Smbuhl stack_base = (char *)stack + stack_size;
289abbaa274Smbuhl #endif
290abbaa274Smbuhl
291abbaa274Smbuhl flags |= SIGCHLD;
292abbaa274Smbuhl
293abbaa274Smbuhl child = FORK();
294abbaa274Smbuhl ATF_REQUIRE(child != 1);
295abbaa274Smbuhl if (child == 0) {
296abbaa274Smbuhl if (strcmp(fn, "fork") == 0)
297abbaa274Smbuhl child2 = fork();
298abbaa274Smbuhl else if (strcmp(fn, "vfork") == 0)
299abbaa274Smbuhl child2 = vfork();
300abbaa274Smbuhl #ifndef __OpenBSD__
301abbaa274Smbuhl else if (strcmp(fn, "clone") == 0)
302abbaa274Smbuhl child2 = __clone(clone_func, stack_base, flags, NULL);
303abbaa274Smbuhl #endif
304abbaa274Smbuhl else
305abbaa274Smbuhl __unreachable();
306abbaa274Smbuhl
307abbaa274Smbuhl ASSERT_NEQ(child2, -1);
308abbaa274Smbuhl
309abbaa274Smbuhl if ((strcmp(fn, "fork") == 0) || (strcmp(fn, "vfork") == 0)) {
310abbaa274Smbuhl if (child2 == 0)
311abbaa274Smbuhl _exit(0);
312abbaa274Smbuhl }
313abbaa274Smbuhl
314abbaa274Smbuhl wpid = waitpid(child2, &status, 0);
315abbaa274Smbuhl ASSERT_EQ(child2, wpid);
316abbaa274Smbuhl ASSERT_EQ(!!WIFEXITED(status), true);
317abbaa274Smbuhl ASSERT_EQ(!!WIFCONTINUED(status), false);
318abbaa274Smbuhl ASSERT_EQ(!!WIFSIGNALED(status), false);
319abbaa274Smbuhl ASSERT_EQ(!!WIFSTOPPED(status), false);
320abbaa274Smbuhl ASSERT_EQ(WEXITSTATUS(status), 0);
321abbaa274Smbuhl
322abbaa274Smbuhl _exit(0);
323abbaa274Smbuhl }
324abbaa274Smbuhl wpid = waitpid(child, &status, 0);
325abbaa274Smbuhl
326abbaa274Smbuhl ATF_REQUIRE_EQ(wpid, child);
327abbaa274Smbuhl ATF_REQUIRE_EQ(!!WIFEXITED(status), true);
328abbaa274Smbuhl ATF_REQUIRE_EQ(!!WIFCONTINUED(status), false);
329abbaa274Smbuhl ATF_REQUIRE_EQ(!!WIFSIGNALED(status), false);
330abbaa274Smbuhl ATF_REQUIRE_EQ(!!WIFSTOPPED(status), false);
331abbaa274Smbuhl ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
332abbaa274Smbuhl }
333abbaa274Smbuhl
334abbaa274Smbuhl #define NESTED(test, fn, flags) \
335abbaa274Smbuhl ATF_TC(test); \
336abbaa274Smbuhl ATF_TC_HEAD(test, tc) \
337abbaa274Smbuhl { \
338abbaa274Smbuhl \
339abbaa274Smbuhl atf_tc_set_md_var(tc, "descr", \
340abbaa274Smbuhl "Test nested " #fn " in a child"); \
341abbaa274Smbuhl } \
342abbaa274Smbuhl \
343abbaa274Smbuhl ATF_TC_BODY(test, tc) \
344abbaa274Smbuhl { \
345abbaa274Smbuhl \
346abbaa274Smbuhl nested_raw(#fn, flags); \
347abbaa274Smbuhl }
348abbaa274Smbuhl
349abbaa274Smbuhl NESTED(nested_fork, fork, 0)
350abbaa274Smbuhl NESTED(nested_vfork, vfork, 0)
351abbaa274Smbuhl #ifndef __OpenBSD__
352abbaa274Smbuhl NESTED(nested_clone, clone, 0)
NESTED(nested_clone_vm,clone,CLONE_VM)353abbaa274Smbuhl NESTED(nested_clone_vm, clone, CLONE_VM)
354abbaa274Smbuhl NESTED(nested_clone_fs, clone, CLONE_FS)
355abbaa274Smbuhl NESTED(nested_clone_files, clone, CLONE_FILES)
356abbaa274Smbuhl //NESTED(nested_clone_sighand, clone, CLONE_SIGHAND) // XXX
357abbaa274Smbuhl NESTED(nested_clone_vfork, clone, CLONE_VFORK)
358abbaa274Smbuhl #endif
359abbaa274Smbuhl
360abbaa274Smbuhl ATF_TP_ADD_TCS(tp)
361abbaa274Smbuhl {
362abbaa274Smbuhl ATF_TP_ADD_TC(tp, raise1);
363abbaa274Smbuhl ATF_TP_ADD_TC(tp, raise2);
364abbaa274Smbuhl ATF_TP_ADD_TC(tp, raise3);
365abbaa274Smbuhl ATF_TP_ADD_TC(tp, raise4);
366abbaa274Smbuhl ATF_TP_ADD_TC(tp, raise5);
367abbaa274Smbuhl ATF_TP_ADD_TC(tp, raise6);
368abbaa274Smbuhl ATF_TP_ADD_TC(tp, raise7);
369abbaa274Smbuhl ATF_TP_ADD_TC(tp, raise8);
370abbaa274Smbuhl
371abbaa274Smbuhl ATF_TP_ADD_TC(tp, nested_fork);
372abbaa274Smbuhl ATF_TP_ADD_TC(tp, nested_vfork);
373abbaa274Smbuhl #ifndef __OpenBSD__
374abbaa274Smbuhl ATF_TP_ADD_TC(tp, nested_clone);
375abbaa274Smbuhl ATF_TP_ADD_TC(tp, nested_clone_vm);
376abbaa274Smbuhl ATF_TP_ADD_TC(tp, nested_clone_fs);
377abbaa274Smbuhl ATF_TP_ADD_TC(tp, nested_clone_files);
378abbaa274Smbuhl // ATF_TP_ADD_TC(tp, nested_clone_sighand); // XXX
379abbaa274Smbuhl ATF_TP_ADD_TC(tp, nested_clone_vfork);
380abbaa274Smbuhl #endif
381abbaa274Smbuhl
382abbaa274Smbuhl return atf_no_error();
383abbaa274Smbuhl }
384