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