diff options
Diffstat (limited to '')
| -rw-r--r-- | src/regress/lib/libc/sys/t_fork.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/src/regress/lib/libc/sys/t_fork.c b/src/regress/lib/libc/sys/t_fork.c new file mode 100644 index 0000000000..76d66ce9d4 --- /dev/null +++ b/src/regress/lib/libc/sys/t_fork.c | |||
| @@ -0,0 +1,387 @@ | |||
| 1 | /* $OpenBSD: t_fork.c,v 1.1 2021/09/02 12:40:44 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 | #include <sys/types.h> | ||
| 38 | #include <sys/sysctl.h> | ||
| 39 | #include <sys/wait.h> | ||
| 40 | #include <sched.h> | ||
| 41 | #include <signal.h> | ||
| 42 | #include <stdbool.h> | ||
| 43 | #include <stdlib.h> | ||
| 44 | #include <unistd.h> | ||
| 45 | #include <err.h> | ||
| 46 | #include <errno.h> | ||
| 47 | |||
| 48 | #include "atf-c.h" | ||
| 49 | |||
| 50 | #ifdef VFORK | ||
| 51 | #define FORK vfork | ||
| 52 | #else | ||
| 53 | #define FORK fork | ||
| 54 | #endif | ||
| 55 | |||
| 56 | /* | ||
| 57 | * A child process cannot call atf functions and expect them to magically | ||
| 58 | * work like in the parent. | ||
| 59 | * The printf(3) messaging from a child will not work out of the box as well | ||
| 60 | * without estabilishing a communication protocol with its parent. To not | ||
| 61 | * overcomplicate the tests - do not log from a child and use err(3)/errx(3) | ||
| 62 | * wrapped with ASSERT_EQ()/ASSERT_NEQ() as that is guaranteed to work. | ||
| 63 | */ | ||
| 64 | #define ASSERT_EQ(x, y) \ | ||
| 65 | do { \ | ||
| 66 | uintmax_t vx = (x); \ | ||
| 67 | uintmax_t vy = (y); \ | ||
| 68 | int ret = vx == vy; \ | ||
| 69 | if (!ret) \ | ||
| 70 | errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \ | ||
| 71 | "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__, \ | ||
| 72 | #x, vx, #y, vy); \ | ||
| 73 | } while (/*CONSTCOND*/0) | ||
| 74 | |||
| 75 | #define ASSERT_NEQ(x, y) \ | ||
| 76 | do { \ | ||
| 77 | uintmax_t vx = (x); \ | ||
| 78 | uintmax_t vy = (y); \ | ||
| 79 | int ret = vx != vy; \ | ||
| 80 | if (!ret) \ | ||
| 81 | errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \ | ||
| 82 | "%s(%ju) != %s(%ju)", __FILE__, __LINE__, __func__, \ | ||
| 83 | #x, vx, #y, vy); \ | ||
| 84 | } while (/*CONSTCOND*/0) | ||
| 85 | |||
| 86 | static pid_t | ||
| 87 | await_stopped_child(pid_t process) | ||
| 88 | { | ||
| 89 | struct kinfo_proc2 *p = NULL; | ||
| 90 | size_t i, len; | ||
| 91 | pid_t child = -1; | ||
| 92 | |||
| 93 | int name[] = { | ||
| 94 | [0] = CTL_KERN, | ||
| 95 | [1] = KERN_PROC2, | ||
| 96 | [2] = KERN_PROC_ALL, | ||
| 97 | [3] = 0, | ||
| 98 | [4] = sizeof(struct kinfo_proc2), | ||
| 99 | [5] = 0 | ||
| 100 | }; | ||
| 101 | |||
| 102 | const size_t namelen = __arraycount(name); | ||
| 103 | |||
| 104 | /* Await the process becoming a zombie */ | ||
| 105 | while(1) { | ||
| 106 | name[5] = 0; | ||
| 107 | |||
| 108 | ASSERT_EQ(sysctl(name, namelen, 0, &len, NULL, 0), 0); | ||
| 109 | |||
| 110 | ASSERT_EQ(reallocarr(&p, len, sizeof(struct kinfo_proc2)), 0); | ||
| 111 | |||
| 112 | name[5] = len; | ||
| 113 | |||
| 114 | ASSERT_EQ(sysctl(name, namelen, p, &len, NULL, 0), 0); | ||
| 115 | |||
| 116 | for (i = 0; i < len/sizeof(struct kinfo_proc2); i++) { | ||
| 117 | if (p[i].p_pid == getpid()) | ||
| 118 | continue; | ||
| 119 | if (p[i].p_ppid != process) | ||
| 120 | continue; | ||
| 121 | #ifndef __OpenBSD__ | ||
| 122 | if (p[i].p_stat != LSSTOP) | ||
| 123 | continue; | ||
| 124 | #endif | ||
| 125 | child = p[i].p_pid; | ||
| 126 | break; | ||
| 127 | } | ||
| 128 | |||
| 129 | if (child != -1) | ||
| 130 | break; | ||
| 131 | |||
| 132 | ASSERT_EQ(usleep(1000), 0); | ||
| 133 | } | ||
| 134 | |||
| 135 | /* Free the buffer */ | ||
| 136 | ASSERT_EQ(reallocarr(&p, 0, sizeof(struct kinfo_proc2)), 0); | ||
| 137 | |||
| 138 | return child; | ||
| 139 | } | ||
| 140 | |||
| 141 | static void | ||
| 142 | raise_raw(int sig) | ||
| 143 | { | ||
| 144 | int rv, status; | ||
| 145 | pid_t child, parent, watcher, wpid; | ||
| 146 | int expect_core = (sig == SIGABRT) ? 1 : 0; | ||
| 147 | |||
| 148 | /* | ||
| 149 | * Spawn a dedicated thread to watch for a stopped child and emit | ||
| 150 | * the SIGKILL signal to it. | ||
| 151 | * | ||
| 152 | * This is required in vfork(2)ing parent and optional in fork(2). | ||
| 153 | * | ||
| 154 | * vfork(2) might clobber watcher, this means that it's safer and | ||
| 155 | * simpler to reparent this process to initproc and forget about it. | ||
| 156 | */ | ||
| 157 | if (sig == SIGSTOP | ||
| 158 | #ifndef VFORK | ||
| 159 | || (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) | ||
| 160 | #endif | ||
| 161 | ) { | ||
| 162 | |||
| 163 | parent = getpid(); | ||
| 164 | |||
| 165 | watcher = fork(); | ||
| 166 | ATF_REQUIRE(watcher != 1); | ||
| 167 | if (watcher == 0) { | ||
| 168 | /* Double fork(2) trick to reparent to initproc */ | ||
| 169 | watcher = fork(); | ||
| 170 | ASSERT_NEQ(watcher, -1); | ||
| 171 | if (watcher != 0) | ||
| 172 | _exit(0); | ||
| 173 | |||
| 174 | child = await_stopped_child(parent); | ||
| 175 | |||
| 176 | errno = 0; | ||
| 177 | rv = kill(child, SIGKILL); | ||
| 178 | ASSERT_EQ(rv, 0); | ||
| 179 | ASSERT_EQ(errno, 0); | ||
| 180 | |||
| 181 | /* This exit value will be collected by initproc */ | ||
| 182 | _exit(0); | ||
| 183 | } | ||
| 184 | |||
| 185 | wpid = waitpid(watcher, &status, 0); | ||
| 186 | |||
| 187 | ATF_REQUIRE_EQ(wpid, watcher); | ||
| 188 | |||
| 189 | ATF_REQUIRE(WIFEXITED(status)); | ||
| 190 | ATF_REQUIRE(!WIFCONTINUED(status)); | ||
| 191 | ATF_REQUIRE(!WIFSIGNALED(status)); | ||
| 192 | ATF_REQUIRE(!WIFSTOPPED(status)); | ||
| 193 | ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); | ||
| 194 | } | ||
| 195 | |||
| 196 | child = FORK(); | ||
| 197 | ATF_REQUIRE(child != 1); | ||
| 198 | if (child == 0) { | ||
| 199 | rv = raise(sig); | ||
| 200 | ASSERT_EQ(rv, 0); | ||
| 201 | _exit(0); | ||
| 202 | } | ||
| 203 | wpid = waitpid(child, &status, 0); | ||
| 204 | |||
| 205 | ATF_REQUIRE_EQ(wpid, child); | ||
| 206 | |||
| 207 | switch (sig) { | ||
| 208 | case SIGKILL: | ||
| 209 | case SIGABRT: | ||
| 210 | case SIGHUP: | ||
| 211 | ATF_REQUIRE(!WIFEXITED(status)); | ||
| 212 | ATF_REQUIRE(!WIFCONTINUED(status)); | ||
| 213 | ATF_REQUIRE(WIFSIGNALED(status)); | ||
| 214 | ATF_REQUIRE(!WIFSTOPPED(status)); | ||
| 215 | ATF_REQUIRE_EQ(WTERMSIG(status), sig); | ||
| 216 | ATF_REQUIRE_EQ(!!WCOREDUMP(status), expect_core); | ||
| 217 | break; | ||
| 218 | #ifdef VFORK | ||
| 219 | case SIGTSTP: | ||
| 220 | case SIGTTIN: | ||
| 221 | case SIGTTOU: | ||
| 222 | #endif | ||
| 223 | case SIGCONT: | ||
| 224 | ATF_REQUIRE(WIFEXITED(status)); | ||
| 225 | ATF_REQUIRE(!WIFCONTINUED(status)); | ||
| 226 | ATF_REQUIRE(!WIFSIGNALED(status)); | ||
| 227 | ATF_REQUIRE(!WIFSTOPPED(status)); | ||
| 228 | ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); | ||
| 229 | break; | ||
| 230 | #ifndef VFORK | ||
| 231 | case SIGTSTP: | ||
| 232 | case SIGTTIN: | ||
| 233 | case SIGTTOU: | ||
| 234 | #endif | ||
| 235 | case SIGSTOP: | ||
| 236 | ATF_REQUIRE(!WIFEXITED(status)); | ||
| 237 | ATF_REQUIRE(!WIFCONTINUED(status)); | ||
| 238 | ATF_REQUIRE(WIFSIGNALED(status)); | ||
| 239 | ATF_REQUIRE(!WIFSTOPPED(status)); | ||
| 240 | ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL); | ||
| 241 | ATF_REQUIRE_EQ(!!WCOREDUMP(status), 0); | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | #define RAISE(test, sig) \ | ||
| 246 | ATF_TC(test); \ | ||
| 247 | ATF_TC_HEAD(test, tc) \ | ||
| 248 | { \ | ||
| 249 | \ | ||
| 250 | atf_tc_set_md_var(tc, "descr", \ | ||
| 251 | "raise " #sig " in a child"); \ | ||
| 252 | } \ | ||
| 253 | \ | ||
| 254 | ATF_TC_BODY(test, tc) \ | ||
| 255 | { \ | ||
| 256 | \ | ||
| 257 | raise_raw(sig); \ | ||
| 258 | } | ||
| 259 | |||
| 260 | RAISE(raise1, SIGKILL) /* non-maskable */ | ||
| 261 | RAISE(raise2, SIGSTOP) /* non-maskable */ | ||
| 262 | RAISE(raise3, SIGTSTP) /* ignored in vfork(2) */ | ||
| 263 | RAISE(raise4, SIGTTIN) /* ignored in vfork(2) */ | ||
| 264 | RAISE(raise5, SIGTTOU) /* ignored in vfork(2) */ | ||
| 265 | RAISE(raise6, SIGABRT) /* regular abort trap */ | ||
| 266 | RAISE(raise7, SIGHUP) /* hangup */ | ||
| 267 | RAISE(raise8, SIGCONT) /* continued? */ | ||
| 268 | |||
| 269 | /// ---------------------------------------------------------------------------- | ||
| 270 | |||
| 271 | static int | ||
| 272 | clone_func(void *arg __unused) | ||
| 273 | { | ||
| 274 | |||
| 275 | return 0; | ||
| 276 | } | ||
| 277 | |||
| 278 | static void | ||
| 279 | nested_raw(const char *fn, volatile int flags) | ||
| 280 | { | ||
| 281 | int status; | ||
| 282 | pid_t child, child2, wpid; | ||
| 283 | const size_t stack_size = 1024 * 1024; | ||
| 284 | void *stack, *stack_base; | ||
| 285 | |||
| 286 | stack = malloc(stack_size); | ||
| 287 | ATF_REQUIRE(stack != NULL); | ||
| 288 | |||
| 289 | #ifdef __MACHINE_STACK_GROWS_UP | ||
| 290 | stack_base = stack; | ||
| 291 | #else | ||
| 292 | stack_base = (char *)stack + stack_size; | ||
| 293 | #endif | ||
| 294 | |||
| 295 | flags |= SIGCHLD; | ||
| 296 | |||
| 297 | child = FORK(); | ||
| 298 | ATF_REQUIRE(child != 1); | ||
| 299 | if (child == 0) { | ||
| 300 | if (strcmp(fn, "fork") == 0) | ||
| 301 | child2 = fork(); | ||
| 302 | else if (strcmp(fn, "vfork") == 0) | ||
| 303 | child2 = vfork(); | ||
| 304 | #ifndef __OpenBSD__ | ||
| 305 | else if (strcmp(fn, "clone") == 0) | ||
| 306 | child2 = __clone(clone_func, stack_base, flags, NULL); | ||
| 307 | #endif | ||
| 308 | else | ||
| 309 | __unreachable(); | ||
| 310 | |||
| 311 | ASSERT_NEQ(child2, -1); | ||
| 312 | |||
| 313 | if ((strcmp(fn, "fork") == 0) || (strcmp(fn, "vfork") == 0)) { | ||
| 314 | if (child2 == 0) | ||
| 315 | _exit(0); | ||
| 316 | } | ||
| 317 | |||
| 318 | wpid = waitpid(child2, &status, 0); | ||
| 319 | ASSERT_EQ(child2, wpid); | ||
| 320 | ASSERT_EQ(!!WIFEXITED(status), true); | ||
| 321 | ASSERT_EQ(!!WIFCONTINUED(status), false); | ||
| 322 | ASSERT_EQ(!!WIFSIGNALED(status), false); | ||
| 323 | ASSERT_EQ(!!WIFSTOPPED(status), false); | ||
| 324 | ASSERT_EQ(WEXITSTATUS(status), 0); | ||
| 325 | |||
| 326 | _exit(0); | ||
| 327 | } | ||
| 328 | wpid = waitpid(child, &status, 0); | ||
| 329 | |||
| 330 | ATF_REQUIRE_EQ(wpid, child); | ||
| 331 | ATF_REQUIRE_EQ(!!WIFEXITED(status), true); | ||
| 332 | ATF_REQUIRE_EQ(!!WIFCONTINUED(status), false); | ||
| 333 | ATF_REQUIRE_EQ(!!WIFSIGNALED(status), false); | ||
| 334 | ATF_REQUIRE_EQ(!!WIFSTOPPED(status), false); | ||
| 335 | ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); | ||
| 336 | } | ||
| 337 | |||
| 338 | #define NESTED(test, fn, flags) \ | ||
| 339 | ATF_TC(test); \ | ||
| 340 | ATF_TC_HEAD(test, tc) \ | ||
| 341 | { \ | ||
| 342 | \ | ||
| 343 | atf_tc_set_md_var(tc, "descr", \ | ||
| 344 | "Test nested " #fn " in a child"); \ | ||
| 345 | } \ | ||
| 346 | \ | ||
| 347 | ATF_TC_BODY(test, tc) \ | ||
| 348 | { \ | ||
| 349 | \ | ||
| 350 | nested_raw(#fn, flags); \ | ||
| 351 | } | ||
| 352 | |||
| 353 | NESTED(nested_fork, fork, 0) | ||
| 354 | NESTED(nested_vfork, vfork, 0) | ||
| 355 | #ifndef __OpenBSD__ | ||
| 356 | NESTED(nested_clone, clone, 0) | ||
| 357 | NESTED(nested_clone_vm, clone, CLONE_VM) | ||
| 358 | NESTED(nested_clone_fs, clone, CLONE_FS) | ||
| 359 | NESTED(nested_clone_files, clone, CLONE_FILES) | ||
| 360 | //NESTED(nested_clone_sighand, clone, CLONE_SIGHAND) // XXX | ||
| 361 | NESTED(nested_clone_vfork, clone, CLONE_VFORK) | ||
| 362 | #endif | ||
| 363 | |||
| 364 | ATF_TP_ADD_TCS(tp) | ||
| 365 | { | ||
| 366 | ATF_TP_ADD_TC(tp, raise1); | ||
| 367 | ATF_TP_ADD_TC(tp, raise2); | ||
| 368 | ATF_TP_ADD_TC(tp, raise3); | ||
| 369 | ATF_TP_ADD_TC(tp, raise4); | ||
| 370 | ATF_TP_ADD_TC(tp, raise5); | ||
| 371 | ATF_TP_ADD_TC(tp, raise6); | ||
| 372 | ATF_TP_ADD_TC(tp, raise7); | ||
| 373 | ATF_TP_ADD_TC(tp, raise8); | ||
| 374 | |||
| 375 | ATF_TP_ADD_TC(tp, nested_fork); | ||
| 376 | ATF_TP_ADD_TC(tp, nested_vfork); | ||
| 377 | #ifndef __OpenBSD__ | ||
| 378 | ATF_TP_ADD_TC(tp, nested_clone); | ||
| 379 | ATF_TP_ADD_TC(tp, nested_clone_vm); | ||
| 380 | ATF_TP_ADD_TC(tp, nested_clone_fs); | ||
| 381 | ATF_TP_ADD_TC(tp, nested_clone_files); | ||
| 382 | // ATF_TP_ADD_TC(tp, nested_clone_sighand); // XXX | ||
| 383 | ATF_TP_ADD_TC(tp, nested_clone_vfork); | ||
| 384 | #endif | ||
| 385 | |||
| 386 | return atf_no_error(); | ||
| 387 | } | ||
