t_fork.c revision 1.3.2.2 1 /* $NetBSD: t_fork.c,v 1.3.2.2 2018/05/21 04:36:17 pgoyette 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.3.2.2 2018/05/21 04:36:17 pgoyette 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) \
61 do { \
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) \
72 do { \
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
82 static pid_t
83 await_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
135 static void
136 raise_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 SIGKILL 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) \
240 ATF_TC(test); \
241 ATF_TC_HEAD(test, tc) \
242 { \
243 \
244 atf_tc_set_md_var(tc, "descr", \
245 "raise " #sig " in vfork(2)ed child"); \
246 } \
247 \
248 ATF_TC_BODY(test, tc) \
249 { \
250 \
251 raise_raw(sig); \
252 }
253
254 RAISE(raise1, SIGKILL) /* non-maskable */
255 RAISE(raise2, SIGSTOP) /* non-maskable */
256 RAISE(raise3, SIGTSTP) /* ignored in vfork(2) */
257 RAISE(raise4, SIGTTIN) /* ignored in vfork(2) */
258 RAISE(raise5, SIGTTOU) /* ignored in vfork(2) */
259 RAISE(raise6, SIGABRT) /* regular abort trap */
260 RAISE(raise7, SIGHUP) /* hangup */
261 RAISE(raise8, SIGCONT) /* continued? */
262
263 ATF_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