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