1*115bc670Skamil /* $NetBSD: t_fork.c,v 1.4 2019/04/06 15:41:54 kamil Exp $ */ 2497013adSkamil 3497013adSkamil /*- 4*115bc670Skamil * 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> 30*115bc670Skamil __COPYRIGHT("@(#) Copyright (c) 2018, 2019\ 31497013adSkamil The NetBSD Foundation, inc. All rights reserved."); 32*115bc670Skamil __RCSID("$NetBSD: t_fork.c,v 1.4 2019/04/06 15:41:54 kamil Exp $"); 33497013adSkamil 34497013adSkamil #include <sys/param.h> 35497013adSkamil #include <sys/types.h> 36497013adSkamil #include <sys/sysctl.h> 37497013adSkamil #include <sys/wait.h> 38*115bc670Skamil #include <sched.h> 39497013adSkamil #include <signal.h> 40*115bc670Skamil #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 58497013adSkamil * without estabilishing 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 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 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", \ 247*115bc670Skamil "raise " #sig " in a child"); \ 248497013adSkamil } \ 249497013adSkamil \ 250497013adSkamil ATF_TC_BODY(test, tc) \ 251497013adSkamil { \ 252497013adSkamil \ 253497013adSkamil raise_raw(sig); \ 254497013adSkamil } 255497013adSkamil 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 265*115bc670Skamil /// ---------------------------------------------------------------------------- 266*115bc670Skamil 267*115bc670Skamil static int 268*115bc670Skamil clone_func(void *arg __unused) 269*115bc670Skamil { 270*115bc670Skamil 271*115bc670Skamil return 0; 272*115bc670Skamil } 273*115bc670Skamil 274*115bc670Skamil static void 275*115bc670Skamil nested_raw(const char *fn, volatile int flags) 276*115bc670Skamil { 277*115bc670Skamil int status; 278*115bc670Skamil pid_t child, child2, wpid; 279*115bc670Skamil const size_t stack_size = 1024 * 1024; 280*115bc670Skamil void *stack, *stack_base; 281*115bc670Skamil 282*115bc670Skamil stack = malloc(stack_size); 283*115bc670Skamil ATF_REQUIRE(stack != NULL); 284*115bc670Skamil 285*115bc670Skamil #ifdef __MACHINE_STACK_GROWS_UP 286*115bc670Skamil stack_base = stack; 287*115bc670Skamil #else 288*115bc670Skamil stack_base = (char *)stack + stack_size; 289*115bc670Skamil #endif 290*115bc670Skamil 291*115bc670Skamil flags |= SIGCHLD; 292*115bc670Skamil 293*115bc670Skamil child = FORK(); 294*115bc670Skamil ATF_REQUIRE(child != 1); 295*115bc670Skamil if (child == 0) { 296*115bc670Skamil if (strcmp(fn, "fork") == 0) 297*115bc670Skamil child2 = fork(); 298*115bc670Skamil else if (strcmp(fn, "vfork") == 0) 299*115bc670Skamil child2 = vfork(); 300*115bc670Skamil else if (strcmp(fn, "clone") == 0) 301*115bc670Skamil child2 = __clone(clone_func, stack_base, flags, NULL); 302*115bc670Skamil else 303*115bc670Skamil __unreachable(); 304*115bc670Skamil 305*115bc670Skamil ASSERT_NEQ(child2, -1); 306*115bc670Skamil 307*115bc670Skamil if ((strcmp(fn, "fork") == 0) || (strcmp(fn, "vfork") == 0)) { 308*115bc670Skamil if (child2 == 0) 309*115bc670Skamil _exit(0); 310*115bc670Skamil } 311*115bc670Skamil 312*115bc670Skamil wpid = waitpid(child2, &status, 0); 313*115bc670Skamil ASSERT_EQ(child2, wpid); 314*115bc670Skamil ASSERT_EQ(!!WIFEXITED(status), true); 315*115bc670Skamil ASSERT_EQ(!!WIFCONTINUED(status), false); 316*115bc670Skamil ASSERT_EQ(!!WIFSIGNALED(status), false); 317*115bc670Skamil ASSERT_EQ(!!WIFSTOPPED(status), false); 318*115bc670Skamil ASSERT_EQ(WEXITSTATUS(status), 0); 319*115bc670Skamil 320*115bc670Skamil _exit(0); 321*115bc670Skamil } 322*115bc670Skamil wpid = waitpid(child, &status, 0); 323*115bc670Skamil 324*115bc670Skamil ATF_REQUIRE_EQ(wpid, child); 325*115bc670Skamil ATF_REQUIRE_EQ(!!WIFEXITED(status), true); 326*115bc670Skamil ATF_REQUIRE_EQ(!!WIFCONTINUED(status), false); 327*115bc670Skamil ATF_REQUIRE_EQ(!!WIFSIGNALED(status), false); 328*115bc670Skamil ATF_REQUIRE_EQ(!!WIFSTOPPED(status), false); 329*115bc670Skamil ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); 330*115bc670Skamil } 331*115bc670Skamil 332*115bc670Skamil #define NESTED(test, fn, flags) \ 333*115bc670Skamil ATF_TC(test); \ 334*115bc670Skamil ATF_TC_HEAD(test, tc) \ 335*115bc670Skamil { \ 336*115bc670Skamil \ 337*115bc670Skamil atf_tc_set_md_var(tc, "descr", \ 338*115bc670Skamil "Test nested " #fn " in a child"); \ 339*115bc670Skamil } \ 340*115bc670Skamil \ 341*115bc670Skamil ATF_TC_BODY(test, tc) \ 342*115bc670Skamil { \ 343*115bc670Skamil \ 344*115bc670Skamil nested_raw(#fn, flags); \ 345*115bc670Skamil } 346*115bc670Skamil 347*115bc670Skamil NESTED(nested_fork, fork, 0) 348*115bc670Skamil NESTED(nested_vfork, vfork, 0) 349*115bc670Skamil NESTED(nested_clone, clone, 0) 350*115bc670Skamil NESTED(nested_clone_vm, clone, CLONE_VM) 351*115bc670Skamil NESTED(nested_clone_fs, clone, CLONE_FS) 352*115bc670Skamil NESTED(nested_clone_files, clone, CLONE_FILES) 353*115bc670Skamil //NESTED(nested_clone_sighand, clone, CLONE_SIGHAND) // XXX 354*115bc670Skamil NESTED(nested_clone_vfork, clone, CLONE_VFORK) 355*115bc670Skamil 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 367*115bc670Skamil ATF_TP_ADD_TC(tp, nested_fork); 368*115bc670Skamil ATF_TP_ADD_TC(tp, nested_vfork); 369*115bc670Skamil ATF_TP_ADD_TC(tp, nested_clone); 370*115bc670Skamil ATF_TP_ADD_TC(tp, nested_clone_vm); 371*115bc670Skamil ATF_TP_ADD_TC(tp, nested_clone_fs); 372*115bc670Skamil ATF_TP_ADD_TC(tp, nested_clone_files); 373*115bc670Skamil // ATF_TP_ADD_TC(tp, nested_clone_sighand); // XXX 374*115bc670Skamil ATF_TP_ADD_TC(tp, nested_clone_vfork); 375*115bc670Skamil 376497013adSkamil return atf_no_error(); 377497013adSkamil } 378