t_fork.c revision 1.2
1/* $NetBSD: t_fork.c,v 1.2 2018/05/19 02:42:58 kamil Exp $ */ 2 3/*- 4 * Copyright (c) 2018 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\ 31 The NetBSD Foundation, inc. All rights reserved."); 32__RCSID("$NetBSD: t_fork.c,v 1.2 2018/05/19 02:42:58 kamil Exp $"); 33 34#include <sys/param.h> 35#include <sys/types.h> 36#include <sys/sysctl.h> 37#include <sys/wait.h> 38#include <signal.h> 39#include <stdlib.h> 40#include <unistd.h> 41#include <err.h> 42#include <errno.h> 43 44#include <atf-c.h> 45 46#ifdef VFORK 47#define FORK vfork 48#else 49#define FORK fork 50#endif 51 52/* 53 * A child process cannot call atf functions and expect them to magically 54 * work like in the parent. 55 * The printf(3) messaging from a child will not work out of the box as well 56 * without estabilishing a communication protocol with its parent. To not 57 * overcomplicate the tests - do not log from a child and use err(3)/errx(3) 58 * wrapped with ASSERT_EQ()/ASSERT_NEQ() as that is guaranteed to work. 59 */ 60#define ASSERT_EQ(x, y) \ 61do { \ 62 uintmax_t vx = (x); \ 63 uintmax_t vy = (y); \ 64 int ret = vx == vy; \ 65 if (!ret) \ 66 errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \ 67 "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__, \ 68 #x, vx, #y, vy); \ 69} while (/*CONSTCOND*/0) 70 71#define ASSERT_NEQ(x, y) \ 72do { \ 73 uintmax_t vx = (x); \ 74 uintmax_t vy = (y); \ 75 int ret = vx != vy; \ 76 if (!ret) \ 77 errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \ 78 "%s(%ju) != %s(%ju)", __FILE__, __LINE__, __func__, \ 79 #x, vx, #y, vy); \ 80} while (/*CONSTCOND*/0) 81 82static pid_t 83await_stopped_child(pid_t process) 84{ 85 struct kinfo_proc2 *p = NULL; 86 size_t i, len; 87 pid_t child = -1; 88 89 int name[] = { 90 [0] = CTL_KERN, 91 [1] = KERN_PROC2, 92 [2] = KERN_PROC_ALL, 93 [3] = 0, 94 [4] = sizeof(struct kinfo_proc2), 95 [5] = 0 96 }; 97 98 const size_t namelen = __arraycount(name); 99 100 /* Await the process becoming a zombie */ 101 while(1) { 102 name[5] = 0; 103 104 ASSERT_EQ(sysctl(name, namelen, 0, &len, NULL, 0), 0); 105 106 ASSERT_EQ(reallocarr(&p, len, sizeof(struct kinfo_proc2)), 0); 107 108 name[5] = len; 109 110 ASSERT_EQ(sysctl(name, namelen, p, &len, NULL, 0), 0); 111 112 for (i = 0; i < len/sizeof(struct kinfo_proc2); i++) { 113 if (p[i].p_pid == getpid()) 114 continue; 115 if (p[i].p_ppid != process) 116 continue; 117 if (p[i].p_stat != LSSTOP) 118 continue; 119 child = p[i].p_pid; 120 break; 121 } 122 123 if (child != -1) 124 break; 125 126 ASSERT_EQ(usleep(1000), 0); 127 } 128 129 /* Free the buffer */ 130 ASSERT_EQ(reallocarr(&p, 0, sizeof(struct kinfo_proc2)), 0); 131 132 return child; 133} 134 135static void 136raise_raw(int sig) 137{ 138 int rv, status; 139 pid_t child, parent, watcher, wpid; 140 int expect_core = (sig == SIGABRT) ? 1 : 0; 141 142 /* 143 * Spawn a dedicated thread to watch for a stopped child and emit 144 * the SIGTERM signal to it. 145 * 146 * This is required in vfork(2)ing parent and optional in fork(2). 147 * 148 * vfork(2) might clobber watcher, this means that it's safer and 149 * simpler to reparent this process to initproc and forget about it. 150 */ 151 if (sig == SIGSTOP 152#ifndef VFORK 153 || (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) 154#endif 155 ) { 156 157 parent = getpid(); 158 159 watcher = fork(); 160 ATF_REQUIRE(watcher != 1); 161 if (watcher == 0) { 162 /* Double fork(2) trick to reparent to initproc */ 163 watcher = fork(); 164 ASSERT_NEQ(watcher, -1); 165 if (watcher != 0) 166 _exit(0); 167 168 child = await_stopped_child(parent); 169 170 errno = 0; 171 rv = kill(child, SIGKILL); 172 ASSERT_EQ(rv, 0); 173 ASSERT_EQ(errno, 0); 174 175 /* This exit value will be collected by initproc */ 176 _exit(0); 177 } 178 179 wpid = waitpid(watcher, &status, 0); 180 181 ATF_REQUIRE_EQ(wpid, watcher); 182 183 ATF_REQUIRE(WIFEXITED(status)); 184 ATF_REQUIRE(!WIFCONTINUED(status)); 185 ATF_REQUIRE(!WIFSIGNALED(status)); 186 ATF_REQUIRE(!WIFSTOPPED(status)); 187 ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); 188 } 189 190 child = FORK(); 191 ATF_REQUIRE(child != 1); 192 if (child == 0) { 193 rv = raise(sig); 194 ASSERT_EQ(rv, 0); 195 _exit(0); 196 } 197 wpid = waitpid(child, &status, 0); 198 199 ATF_REQUIRE_EQ(wpid, child); 200 201 switch (sig) { 202 case SIGKILL: 203 case SIGABRT: 204 case SIGHUP: 205 ATF_REQUIRE(!WIFEXITED(status)); 206 ATF_REQUIRE(!WIFCONTINUED(status)); 207 ATF_REQUIRE(WIFSIGNALED(status)); 208 ATF_REQUIRE(!WIFSTOPPED(status)); 209 ATF_REQUIRE_EQ(WTERMSIG(status), sig); 210 ATF_REQUIRE_EQ(!!WCOREDUMP(status), expect_core); 211 break; 212#ifdef VFORK 213 case SIGTSTP: 214 case SIGTTIN: 215 case SIGTTOU: 216#endif 217 case SIGCONT: 218 ATF_REQUIRE(WIFEXITED(status)); 219 ATF_REQUIRE(!WIFCONTINUED(status)); 220 ATF_REQUIRE(!WIFSIGNALED(status)); 221 ATF_REQUIRE(!WIFSTOPPED(status)); 222 ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); 223 break; 224#ifndef VFORK 225 case SIGTSTP: 226 case SIGTTIN: 227 case SIGTTOU: 228#endif 229 case SIGSTOP: 230 ATF_REQUIRE(!WIFEXITED(status)); 231 ATF_REQUIRE(!WIFCONTINUED(status)); 232 ATF_REQUIRE(WIFSIGNALED(status)); 233 ATF_REQUIRE(!WIFSTOPPED(status)); 234 ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL); 235 ATF_REQUIRE_EQ(!!WCOREDUMP(status), 0); 236 } 237} 238 239#define RAISE(test, sig) \ 240ATF_TC(test); \ 241ATF_TC_HEAD(test, tc) \ 242{ \ 243 \ 244 atf_tc_set_md_var(tc, "descr", \ 245 "raise " #sig " in vfork(2)ed child"); \ 246} \ 247 \ 248ATF_TC_BODY(test, tc) \ 249{ \ 250 \ 251 raise_raw(sig); \ 252} 253 254RAISE(raise1, SIGKILL) /* non-maskable */ 255RAISE(raise2, SIGSTOP) /* non-maskable */ 256RAISE(raise3, SIGTSTP) /* ignored in vfork(2) */ 257RAISE(raise4, SIGTTIN) /* ignored in vfork(2) */ 258RAISE(raise5, SIGTTOU) /* ignored in vfork(2) */ 259RAISE(raise6, SIGABRT) /* regular abort trap */ 260RAISE(raise7, SIGHUP) /* hangup */ 261RAISE(raise8, SIGCONT) /* continued? */ 262 263ATF_TP_ADD_TCS(tp) 264{ 265 ATF_TP_ADD_TC(tp, raise1); 266 ATF_TP_ADD_TC(tp, raise2); 267 ATF_TP_ADD_TC(tp, raise3); 268 ATF_TP_ADD_TC(tp, raise4); 269 ATF_TP_ADD_TC(tp, raise5); 270 ATF_TP_ADD_TC(tp, raise6); 271 ATF_TP_ADD_TC(tp, raise7); 272 ATF_TP_ADD_TC(tp, raise8); 273 274 return atf_no_error(); 275} 276