t_zombie.c revision 1.2 1 1.2 kamil /* $NetBSD: t_zombie.c,v 1.2 2018/05/18 00:25:30 kamil Exp $ */
2 1.1 kamil
3 1.1 kamil /*-
4 1.1 kamil * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 1.1 kamil * All rights reserved.
6 1.1 kamil *
7 1.1 kamil * Redistribution and use in source and binary forms, with or without
8 1.1 kamil * modification, are permitted provided that the following conditions
9 1.1 kamil * are met:
10 1.1 kamil * 1. Redistributions of source code must retain the above copyright
11 1.1 kamil * notice, this list of conditions and the following disclaimer.
12 1.1 kamil * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 kamil * notice, this list of conditions and the following disclaimer in the
14 1.1 kamil * documentation and/or other materials provided with the distribution.
15 1.1 kamil *
16 1.1 kamil * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 kamil * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 kamil * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 kamil * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 kamil * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 kamil * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 kamil * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 kamil * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 kamil * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 kamil * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 kamil * POSSIBILITY OF SUCH DAMAGE.
27 1.1 kamil */
28 1.1 kamil
29 1.1 kamil #include <sys/cdefs.h>
30 1.1 kamil __COPYRIGHT("@(#) Copyright (c) 2018\
31 1.1 kamil The NetBSD Foundation, inc. All rights reserved.");
32 1.2 kamil __RCSID("$NetBSD: t_zombie.c,v 1.2 2018/05/18 00:25:30 kamil Exp $");
33 1.1 kamil
34 1.1 kamil #include <sys/types.h>
35 1.1 kamil #include <sys/sysctl.h>
36 1.1 kamil #include <sys/wait.h>
37 1.1 kamil #include <errno.h>
38 1.1 kamil #include <stdbool.h>
39 1.1 kamil #include <stddef.h>
40 1.1 kamil #include <stdio.h>
41 1.1 kamil #include <stdlib.h>
42 1.1 kamil #include <signal.h>
43 1.1 kamil #include <time.h>
44 1.1 kamil #include <unistd.h>
45 1.1 kamil #include <err.h>
46 1.1 kamil
47 1.1 kamil #include <atf-c.h>
48 1.1 kamil
49 1.1 kamil static int debug = 0;
50 1.1 kamil
51 1.1 kamil #define DPRINTF(a, ...) \
52 1.1 kamil do { \
53 1.1 kamil if (debug) printf(a, ##__VA_ARGS__); \
54 1.1 kamil } while (/*CONSTCOND*/0)
55 1.1 kamil
56 1.1 kamil /*
57 1.1 kamil * A child process cannot call atf functions and expect them to magically
58 1.1 kamil * work like in the parent.
59 1.1 kamil * The printf(3) messaging from a child will not work out of the box as well
60 1.1 kamil * without estabilishing a communication protocol with its parent. To not
61 1.1 kamil * overcomplicate the tests - do not log from a child and use err(3)/errx(3)
62 1.1 kamil * wrapped with ASSERT_EQ()/ASSERT_NEQ() as that is guaranteed to work.
63 1.1 kamil */
64 1.1 kamil #define ASSERT_EQ(x, y) \
65 1.1 kamil do { \
66 1.1 kamil uintmax_t vx = (x); \
67 1.1 kamil uintmax_t vy = (y); \
68 1.1 kamil int ret = vx == vy; \
69 1.1 kamil if (!ret) \
70 1.1 kamil errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \
71 1.1 kamil "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__, \
72 1.1 kamil #x, vx, #y, vy); \
73 1.1 kamil } while (/*CONSTCOND*/0)
74 1.1 kamil
75 1.1 kamil #define ASSERT_NEQ(x, y) \
76 1.1 kamil do { \
77 1.1 kamil uintmax_t vx = (x); \
78 1.1 kamil uintmax_t vy = (y); \
79 1.1 kamil int ret = vx != vy; \
80 1.1 kamil if (!ret) \
81 1.1 kamil errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \
82 1.1 kamil "%s(%ju) != %s(%ju)", __FILE__, __LINE__, __func__, \
83 1.1 kamil #x, vx, #y, vy); \
84 1.1 kamil } while (/*CONSTCOND*/0)
85 1.1 kamil
86 1.1 kamil #define ASSERT(x) \
87 1.1 kamil do { \
88 1.1 kamil int ret = (x); \
89 1.1 kamil if (!ret) \
90 1.1 kamil errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \
91 1.1 kamil __FILE__, __LINE__, __func__, #x); \
92 1.1 kamil } while (/*CONSTCOND*/0)
93 1.1 kamil
94 1.1 kamil static bool
95 1.1 kamil check_zombie(pid_t process)
96 1.1 kamil {
97 1.1 kamil struct kinfo_proc2 p;
98 1.1 kamil size_t len = sizeof(p);
99 1.1 kamil
100 1.1 kamil const int name[] = {
101 1.1 kamil [0] = CTL_KERN,
102 1.1 kamil [1] = KERN_PROC2,
103 1.1 kamil [2] = KERN_PROC_PID,
104 1.1 kamil [3] = process,
105 1.1 kamil [4] = sizeof(p),
106 1.1 kamil [5] = 1
107 1.1 kamil };
108 1.1 kamil
109 1.1 kamil const size_t namelen = __arraycount(name);
110 1.1 kamil
111 1.1 kamil ASSERT_EQ(sysctl(name, namelen, &p, &len, NULL, 0), 0);
112 1.1 kamil
113 1.1 kamil return (p.p_stat == LSZOMB);
114 1.1 kamil }
115 1.1 kamil
116 1.1 kamil static void __used
117 1.1 kamil await_zombie(pid_t process)
118 1.1 kamil {
119 1.1 kamil
120 1.1 kamil /* Await the process becoming a zombie */
121 1.1 kamil while (!check_zombie(process)) {
122 1.1 kamil ASSERT_EQ(usleep(100), 0);
123 1.1 kamil }
124 1.1 kamil }
125 1.1 kamil
126 1.1 kamil static void
127 1.1 kamil signal_raw(int sig)
128 1.1 kamil {
129 1.1 kamil int status;
130 1.1 kamil pid_t child1, child2, pid;
131 1.1 kamil
132 1.1 kamil child1 = atf_utils_fork();
133 1.2 kamil ATF_REQUIRE(child1 != -1);
134 1.1 kamil if (child1 == 0) {
135 1.1 kamil /* Just die and turn into a zombie */
136 1.1 kamil _exit(0);
137 1.1 kamil }
138 1.1 kamil
139 1.1 kamil child2 = atf_utils_fork();
140 1.2 kamil ATF_REQUIRE(child2 != -1);
141 1.1 kamil if (child2 == 0) {
142 1.1 kamil await_zombie(child1);
143 1.1 kamil
144 1.1 kamil /*
145 1.1 kamil * zombie does not process signals
146 1.1 kamil * POSIX requires that zombie does not set errno ESRCH
147 1.1 kamil * return value of kill() for a zombie is not specified
148 1.1 kamil *
149 1.1 kamil * Try to emit a signal towards it from an unrelated process.
150 1.1 kamil */
151 1.1 kamil errno = 0;
152 1.1 kamil kill(child1, sig);
153 1.1 kamil ASSERT_NEQ(errno, ESRCH);
154 1.1 kamil
155 1.1 kamil /* A zombie is still a zombie waiting for collecting */
156 1.1 kamil ASSERT(check_zombie(child1));
157 1.1 kamil
158 1.1 kamil _exit(0);
159 1.1 kamil }
160 1.1 kamil
161 1.1 kamil pid = waitpid(child2, &status, WEXITED);
162 1.1 kamil ATF_REQUIRE_EQ(pid, child2);
163 1.1 kamil ATF_REQUIRE(WIFEXITED(status));
164 1.1 kamil ATF_REQUIRE(!WIFCONTINUED(status));
165 1.1 kamil ATF_REQUIRE(!WIFSIGNALED(status));
166 1.1 kamil ATF_REQUIRE(!WIFSTOPPED(status));
167 1.1 kamil ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
168 1.1 kamil
169 1.1 kamil /* Assert that child1 is still a zombie after collecting child2 */
170 1.1 kamil ATF_REQUIRE(check_zombie(child1));
171 1.1 kamil
172 1.1 kamil /*
173 1.1 kamil * zombie does not process signals
174 1.1 kamil * POSIX requires that zombie does not set errno ESRCH
175 1.1 kamil * return value of kill() for a zombie is not specified
176 1.1 kamil *
177 1.1 kamil * Try to emit a signal towards it from the parent.
178 1.1 kamil */
179 1.1 kamil errno = 0;
180 1.1 kamil kill(child1, sig);
181 1.1 kamil // ATF_CHECK_NEQ not available
182 1.1 kamil ASSERT_NEQ(errno, ESRCH);
183 1.1 kamil
184 1.1 kamil /* Assert that child1 is still a zombie after emitting a signal */
185 1.1 kamil ATF_REQUIRE(check_zombie(child1));
186 1.1 kamil
187 1.1 kamil pid = waitpid(child1, &status, WEXITED);
188 1.1 kamil ATF_REQUIRE_EQ(pid, child1);
189 1.1 kamil ATF_REQUIRE(WIFEXITED(status));
190 1.1 kamil ATF_REQUIRE(!WIFCONTINUED(status));
191 1.1 kamil ATF_REQUIRE(!WIFSIGNALED(status));
192 1.1 kamil ATF_REQUIRE(!WIFSTOPPED(status));
193 1.1 kamil ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
194 1.1 kamil }
195 1.1 kamil
196 1.1 kamil #define KILLABLE(test, sig) \
197 1.1 kamil ATF_TC(test); \
198 1.1 kamil ATF_TC_HEAD(test, tc) \
199 1.1 kamil { \
200 1.1 kamil \
201 1.1 kamil atf_tc_set_md_var(tc, "descr", \
202 1.1 kamil "process is not killable with " #sig); \
203 1.1 kamil } \
204 1.1 kamil \
205 1.1 kamil ATF_TC_BODY(test, tc) \
206 1.1 kamil { \
207 1.1 kamil \
208 1.1 kamil signal_raw(sig); \
209 1.1 kamil }
210 1.1 kamil
211 1.1 kamil KILLABLE(signal1, SIGKILL) /* non-maskable */
212 1.1 kamil KILLABLE(signal2, SIGSTOP) /* non-maskable */
213 1.1 kamil KILLABLE(signal3, SIGABRT) /* regular abort trap */
214 1.1 kamil KILLABLE(signal4, SIGHUP) /* hangup */
215 1.1 kamil KILLABLE(signal5, SIGCONT) /* continued? */
216 1.1 kamil
217 1.1 kamil ATF_TC(race1);
218 1.1 kamil ATF_TC_HEAD(race1, tc)
219 1.1 kamil {
220 1.1 kamil
221 1.1 kamil atf_tc_set_md_var(tc, "descr",
222 1.1 kamil "check if there are any races with sending signals, killing and "
223 1.1 kamil "lookup of a zombie");
224 1.1 kamil }
225 1.1 kamil
226 1.1 kamil ATF_TC_BODY(race1, tc)
227 1.1 kamil {
228 1.1 kamil time_t start, end;
229 1.1 kamil double diff;
230 1.1 kamil unsigned long N = 0;
231 1.1 kamil int sig;
232 1.1 kamil
233 1.1 kamil /*
234 1.1 kamil * Assert that a dying process can be correctly looked up
235 1.1 kamil * with sysctl(3) kern.proc and operation KERN_PROC_PID.
236 1.1 kamil *
237 1.1 kamil * This test has been inspired by a bug fixed in
238 1.1 kamil * sys/kern/kern_proc.c 1.211
239 1.1 kamil * "Make sysctl_doeproc() more predictable"
240 1.1 kamil */
241 1.1 kamil
242 1.1 kamil start = time(NULL);
243 1.1 kamil while (true) {
244 1.1 kamil /*
245 1.1 kamil * A signal number does not matter, but it does not harm to
246 1.1 kamil * randomize it.
247 1.1 kamil *
248 1.1 kamil * Skip signal 0 as sending to it to a zombie is not
249 1.1 kamil * defined in POSIX, and explicitly discouraged.
250 1.1 kamil */
251 1.1 kamil sig = 1 + arc4random_uniform(NSIG - 2);
252 1.1 kamil
253 1.1 kamil DPRINTF("Step: %lu (signal: %s)\n", N, signalname(sig));
254 1.1 kamil
255 1.1 kamil signal_raw(sig);
256 1.1 kamil end = time(NULL);
257 1.1 kamil diff = difftime(end, start);
258 1.1 kamil if (diff >= 5.0)
259 1.1 kamil break;
260 1.1 kamil ++N;
261 1.1 kamil }
262 1.1 kamil DPRINTF("Iterations: %lu\n", N);
263 1.1 kamil }
264 1.1 kamil
265 1.1 kamil ATF_TP_ADD_TCS(tp)
266 1.1 kamil {
267 1.1 kamil ATF_TP_ADD_TC(tp, signal1);
268 1.1 kamil ATF_TP_ADD_TC(tp, signal2);
269 1.1 kamil ATF_TP_ADD_TC(tp, signal3);
270 1.1 kamil ATF_TP_ADD_TC(tp, signal4);
271 1.1 kamil ATF_TP_ADD_TC(tp, signal5);
272 1.1 kamil
273 1.1 kamil ATF_TP_ADD_TC(tp, race1);
274 1.1 kamil
275 1.1 kamil return atf_no_error();
276 1.1 kamil }
277