t_fork.c revision 1.1 1 /* $NetBSD: t_fork.c,v 1.1 2018/05/18 06:39: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.1 2018/05/18 06:39: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) \
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 #ifdef VFORK
143 if (sig == SIGSTOP) {
144 atf_tc_expect_fail("SIGSTOP shall not be ignored");
145 }
146 #endif
147
148 /*
149 * Spawn a dedicated thread to watch for a stopped child and emit
150 * the SIGTERM 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 vfork(2)ed 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 ATF_TP_ADD_TCS(tp)
270 {
271 ATF_TP_ADD_TC(tp, raise1);
272 ATF_TP_ADD_TC(tp, raise2);
273 ATF_TP_ADD_TC(tp, raise3);
274 ATF_TP_ADD_TC(tp, raise4);
275 ATF_TP_ADD_TC(tp, raise5);
276 ATF_TP_ADD_TC(tp, raise6);
277 ATF_TP_ADD_TC(tp, raise7);
278 ATF_TP_ADD_TC(tp, raise8);
279
280 return atf_no_error();
281 }
282