1 /* $NetBSD: t_zombie.c,v 1.2 2018/05/18 00:25:30 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_zombie.c,v 1.2 2018/05/18 00:25:30 kamil Exp $"); 33 34 #include <sys/types.h> 35 #include <sys/sysctl.h> 36 #include <sys/wait.h> 37 #include <errno.h> 38 #include <stdbool.h> 39 #include <stddef.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <signal.h> 43 #include <time.h> 44 #include <unistd.h> 45 #include <err.h> 46 47 #include <atf-c.h> 48 49 static int debug = 0; 50 51 #define DPRINTF(a, ...) \ 52 do { \ 53 if (debug) printf(a, ##__VA_ARGS__); \ 54 } while (/*CONSTCOND*/0) 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 #define ASSERT(x) \ 87 do { \ 88 int ret = (x); \ 89 if (!ret) \ 90 errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \ 91 __FILE__, __LINE__, __func__, #x); \ 92 } while (/*CONSTCOND*/0) 93 94 static bool 95 check_zombie(pid_t process) 96 { 97 struct kinfo_proc2 p; 98 size_t len = sizeof(p); 99 100 const int name[] = { 101 [0] = CTL_KERN, 102 [1] = KERN_PROC2, 103 [2] = KERN_PROC_PID, 104 [3] = process, 105 [4] = sizeof(p), 106 [5] = 1 107 }; 108 109 const size_t namelen = __arraycount(name); 110 111 ASSERT_EQ(sysctl(name, namelen, &p, &len, NULL, 0), 0); 112 113 return (p.p_stat == LSZOMB); 114 } 115 116 static void __used 117 await_zombie(pid_t process) 118 { 119 120 /* Await the process becoming a zombie */ 121 while (!check_zombie(process)) { 122 ASSERT_EQ(usleep(100), 0); 123 } 124 } 125 126 static void 127 signal_raw(int sig) 128 { 129 int status; 130 pid_t child1, child2, pid; 131 132 child1 = atf_utils_fork(); 133 ATF_REQUIRE(child1 != -1); 134 if (child1 == 0) { 135 /* Just die and turn into a zombie */ 136 _exit(0); 137 } 138 139 child2 = atf_utils_fork(); 140 ATF_REQUIRE(child2 != -1); 141 if (child2 == 0) { 142 await_zombie(child1); 143 144 /* 145 * zombie does not process signals 146 * POSIX requires that zombie does not set errno ESRCH 147 * return value of kill() for a zombie is not specified 148 * 149 * Try to emit a signal towards it from an unrelated process. 150 */ 151 errno = 0; 152 kill(child1, sig); 153 ASSERT_NEQ(errno, ESRCH); 154 155 /* A zombie is still a zombie waiting for collecting */ 156 ASSERT(check_zombie(child1)); 157 158 _exit(0); 159 } 160 161 pid = waitpid(child2, &status, WEXITED); 162 ATF_REQUIRE_EQ(pid, child2); 163 ATF_REQUIRE(WIFEXITED(status)); 164 ATF_REQUIRE(!WIFCONTINUED(status)); 165 ATF_REQUIRE(!WIFSIGNALED(status)); 166 ATF_REQUIRE(!WIFSTOPPED(status)); 167 ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); 168 169 /* Assert that child1 is still a zombie after collecting child2 */ 170 ATF_REQUIRE(check_zombie(child1)); 171 172 /* 173 * zombie does not process signals 174 * POSIX requires that zombie does not set errno ESRCH 175 * return value of kill() for a zombie is not specified 176 * 177 * Try to emit a signal towards it from the parent. 178 */ 179 errno = 0; 180 kill(child1, sig); 181 // ATF_CHECK_NEQ not available 182 ASSERT_NEQ(errno, ESRCH); 183 184 /* Assert that child1 is still a zombie after emitting a signal */ 185 ATF_REQUIRE(check_zombie(child1)); 186 187 pid = waitpid(child1, &status, WEXITED); 188 ATF_REQUIRE_EQ(pid, child1); 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 #define KILLABLE(test, sig) \ 197 ATF_TC(test); \ 198 ATF_TC_HEAD(test, tc) \ 199 { \ 200 \ 201 atf_tc_set_md_var(tc, "descr", \ 202 "process is not killable with " #sig); \ 203 } \ 204 \ 205 ATF_TC_BODY(test, tc) \ 206 { \ 207 \ 208 signal_raw(sig); \ 209 } 210 211 KILLABLE(signal1, SIGKILL) /* non-maskable */ 212 KILLABLE(signal2, SIGSTOP) /* non-maskable */ 213 KILLABLE(signal3, SIGABRT) /* regular abort trap */ 214 KILLABLE(signal4, SIGHUP) /* hangup */ 215 KILLABLE(signal5, SIGCONT) /* continued? */ 216 217 ATF_TC(race1); 218 ATF_TC_HEAD(race1, tc) 219 { 220 221 atf_tc_set_md_var(tc, "descr", 222 "check if there are any races with sending signals, killing and " 223 "lookup of a zombie"); 224 } 225 226 ATF_TC_BODY(race1, tc) 227 { 228 time_t start, end; 229 double diff; 230 unsigned long N = 0; 231 int sig; 232 233 /* 234 * Assert that a dying process can be correctly looked up 235 * with sysctl(3) kern.proc and operation KERN_PROC_PID. 236 * 237 * This test has been inspired by a bug fixed in 238 * sys/kern/kern_proc.c 1.211 239 * "Make sysctl_doeproc() more predictable" 240 */ 241 242 start = time(NULL); 243 while (true) { 244 /* 245 * A signal number does not matter, but it does not harm to 246 * randomize it. 247 * 248 * Skip signal 0 as sending to it to a zombie is not 249 * defined in POSIX, and explicitly discouraged. 250 */ 251 sig = 1 + arc4random_uniform(NSIG - 2); 252 253 DPRINTF("Step: %lu (signal: %s)\n", N, signalname(sig)); 254 255 signal_raw(sig); 256 end = time(NULL); 257 diff = difftime(end, start); 258 if (diff >= 5.0) 259 break; 260 ++N; 261 } 262 DPRINTF("Iterations: %lu\n", N); 263 } 264 265 ATF_TP_ADD_TCS(tp) 266 { 267 ATF_TP_ADD_TC(tp, signal1); 268 ATF_TP_ADD_TC(tp, signal2); 269 ATF_TP_ADD_TC(tp, signal3); 270 ATF_TP_ADD_TC(tp, signal4); 271 ATF_TP_ADD_TC(tp, signal5); 272 273 ATF_TP_ADD_TC(tp, race1); 274 275 return atf_no_error(); 276 } 277