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