t_fork.c revision 1.2 1 1.2 kamil /* $NetBSD: t_fork.c,v 1.2 2018/05/19 02:42:58 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_fork.c,v 1.2 2018/05/19 02:42:58 kamil Exp $");
33 1.1 kamil
34 1.1 kamil #include <sys/param.h>
35 1.1 kamil #include <sys/types.h>
36 1.1 kamil #include <sys/sysctl.h>
37 1.1 kamil #include <sys/wait.h>
38 1.1 kamil #include <signal.h>
39 1.1 kamil #include <stdlib.h>
40 1.1 kamil #include <unistd.h>
41 1.1 kamil #include <err.h>
42 1.1 kamil #include <errno.h>
43 1.1 kamil
44 1.1 kamil #include <atf-c.h>
45 1.1 kamil
46 1.1 kamil #ifdef VFORK
47 1.1 kamil #define FORK vfork
48 1.1 kamil #else
49 1.1 kamil #define FORK fork
50 1.1 kamil #endif
51 1.1 kamil
52 1.1 kamil /*
53 1.1 kamil * A child process cannot call atf functions and expect them to magically
54 1.1 kamil * work like in the parent.
55 1.1 kamil * The printf(3) messaging from a child will not work out of the box as well
56 1.1 kamil * without estabilishing a communication protocol with its parent. To not
57 1.1 kamil * overcomplicate the tests - do not log from a child and use err(3)/errx(3)
58 1.1 kamil * wrapped with ASSERT_EQ()/ASSERT_NEQ() as that is guaranteed to work.
59 1.1 kamil */
60 1.1 kamil #define ASSERT_EQ(x, y) \
61 1.1 kamil do { \
62 1.1 kamil uintmax_t vx = (x); \
63 1.1 kamil uintmax_t vy = (y); \
64 1.1 kamil int ret = vx == vy; \
65 1.1 kamil if (!ret) \
66 1.1 kamil errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \
67 1.1 kamil "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__, \
68 1.1 kamil #x, vx, #y, vy); \
69 1.1 kamil } while (/*CONSTCOND*/0)
70 1.1 kamil
71 1.1 kamil #define ASSERT_NEQ(x, y) \
72 1.1 kamil do { \
73 1.1 kamil uintmax_t vx = (x); \
74 1.1 kamil uintmax_t vy = (y); \
75 1.1 kamil int ret = vx != vy; \
76 1.1 kamil if (!ret) \
77 1.1 kamil errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \
78 1.1 kamil "%s(%ju) != %s(%ju)", __FILE__, __LINE__, __func__, \
79 1.1 kamil #x, vx, #y, vy); \
80 1.1 kamil } while (/*CONSTCOND*/0)
81 1.1 kamil
82 1.1 kamil static pid_t
83 1.1 kamil await_stopped_child(pid_t process)
84 1.1 kamil {
85 1.1 kamil struct kinfo_proc2 *p = NULL;
86 1.1 kamil size_t i, len;
87 1.1 kamil pid_t child = -1;
88 1.1 kamil
89 1.1 kamil int name[] = {
90 1.1 kamil [0] = CTL_KERN,
91 1.1 kamil [1] = KERN_PROC2,
92 1.1 kamil [2] = KERN_PROC_ALL,
93 1.1 kamil [3] = 0,
94 1.1 kamil [4] = sizeof(struct kinfo_proc2),
95 1.1 kamil [5] = 0
96 1.1 kamil };
97 1.1 kamil
98 1.1 kamil const size_t namelen = __arraycount(name);
99 1.1 kamil
100 1.1 kamil /* Await the process becoming a zombie */
101 1.1 kamil while(1) {
102 1.1 kamil name[5] = 0;
103 1.1 kamil
104 1.1 kamil ASSERT_EQ(sysctl(name, namelen, 0, &len, NULL, 0), 0);
105 1.1 kamil
106 1.1 kamil ASSERT_EQ(reallocarr(&p, len, sizeof(struct kinfo_proc2)), 0);
107 1.1 kamil
108 1.1 kamil name[5] = len;
109 1.1 kamil
110 1.1 kamil ASSERT_EQ(sysctl(name, namelen, p, &len, NULL, 0), 0);
111 1.1 kamil
112 1.1 kamil for (i = 0; i < len/sizeof(struct kinfo_proc2); i++) {
113 1.1 kamil if (p[i].p_pid == getpid())
114 1.1 kamil continue;
115 1.1 kamil if (p[i].p_ppid != process)
116 1.1 kamil continue;
117 1.1 kamil if (p[i].p_stat != LSSTOP)
118 1.1 kamil continue;
119 1.1 kamil child = p[i].p_pid;
120 1.1 kamil break;
121 1.1 kamil }
122 1.1 kamil
123 1.1 kamil if (child != -1)
124 1.1 kamil break;
125 1.1 kamil
126 1.1 kamil ASSERT_EQ(usleep(1000), 0);
127 1.1 kamil }
128 1.1 kamil
129 1.1 kamil /* Free the buffer */
130 1.1 kamil ASSERT_EQ(reallocarr(&p, 0, sizeof(struct kinfo_proc2)), 0);
131 1.1 kamil
132 1.1 kamil return child;
133 1.1 kamil }
134 1.1 kamil
135 1.1 kamil static void
136 1.1 kamil raise_raw(int sig)
137 1.1 kamil {
138 1.1 kamil int rv, status;
139 1.1 kamil pid_t child, parent, watcher, wpid;
140 1.1 kamil int expect_core = (sig == SIGABRT) ? 1 : 0;
141 1.1 kamil
142 1.1 kamil /*
143 1.1 kamil * Spawn a dedicated thread to watch for a stopped child and emit
144 1.1 kamil * the SIGTERM signal to it.
145 1.1 kamil *
146 1.1 kamil * This is required in vfork(2)ing parent and optional in fork(2).
147 1.1 kamil *
148 1.1 kamil * vfork(2) might clobber watcher, this means that it's safer and
149 1.1 kamil * simpler to reparent this process to initproc and forget about it.
150 1.1 kamil */
151 1.1 kamil if (sig == SIGSTOP
152 1.1 kamil #ifndef VFORK
153 1.1 kamil || (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
154 1.1 kamil #endif
155 1.1 kamil ) {
156 1.1 kamil
157 1.1 kamil parent = getpid();
158 1.1 kamil
159 1.1 kamil watcher = fork();
160 1.1 kamil ATF_REQUIRE(watcher != 1);
161 1.1 kamil if (watcher == 0) {
162 1.1 kamil /* Double fork(2) trick to reparent to initproc */
163 1.1 kamil watcher = fork();
164 1.1 kamil ASSERT_NEQ(watcher, -1);
165 1.1 kamil if (watcher != 0)
166 1.1 kamil _exit(0);
167 1.1 kamil
168 1.1 kamil child = await_stopped_child(parent);
169 1.1 kamil
170 1.1 kamil errno = 0;
171 1.1 kamil rv = kill(child, SIGKILL);
172 1.1 kamil ASSERT_EQ(rv, 0);
173 1.1 kamil ASSERT_EQ(errno, 0);
174 1.1 kamil
175 1.1 kamil /* This exit value will be collected by initproc */
176 1.1 kamil _exit(0);
177 1.1 kamil }
178 1.1 kamil
179 1.1 kamil wpid = waitpid(watcher, &status, 0);
180 1.1 kamil
181 1.1 kamil ATF_REQUIRE_EQ(wpid, watcher);
182 1.1 kamil
183 1.1 kamil ATF_REQUIRE(WIFEXITED(status));
184 1.1 kamil ATF_REQUIRE(!WIFCONTINUED(status));
185 1.1 kamil ATF_REQUIRE(!WIFSIGNALED(status));
186 1.1 kamil ATF_REQUIRE(!WIFSTOPPED(status));
187 1.1 kamil ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
188 1.1 kamil }
189 1.1 kamil
190 1.1 kamil child = FORK();
191 1.1 kamil ATF_REQUIRE(child != 1);
192 1.1 kamil if (child == 0) {
193 1.1 kamil rv = raise(sig);
194 1.1 kamil ASSERT_EQ(rv, 0);
195 1.1 kamil _exit(0);
196 1.1 kamil }
197 1.1 kamil wpid = waitpid(child, &status, 0);
198 1.1 kamil
199 1.1 kamil ATF_REQUIRE_EQ(wpid, child);
200 1.1 kamil
201 1.1 kamil switch (sig) {
202 1.1 kamil case SIGKILL:
203 1.1 kamil case SIGABRT:
204 1.1 kamil case SIGHUP:
205 1.1 kamil ATF_REQUIRE(!WIFEXITED(status));
206 1.1 kamil ATF_REQUIRE(!WIFCONTINUED(status));
207 1.1 kamil ATF_REQUIRE(WIFSIGNALED(status));
208 1.1 kamil ATF_REQUIRE(!WIFSTOPPED(status));
209 1.1 kamil ATF_REQUIRE_EQ(WTERMSIG(status), sig);
210 1.1 kamil ATF_REQUIRE_EQ(!!WCOREDUMP(status), expect_core);
211 1.1 kamil break;
212 1.1 kamil #ifdef VFORK
213 1.1 kamil case SIGTSTP:
214 1.1 kamil case SIGTTIN:
215 1.1 kamil case SIGTTOU:
216 1.1 kamil #endif
217 1.1 kamil case SIGCONT:
218 1.1 kamil ATF_REQUIRE(WIFEXITED(status));
219 1.1 kamil ATF_REQUIRE(!WIFCONTINUED(status));
220 1.1 kamil ATF_REQUIRE(!WIFSIGNALED(status));
221 1.1 kamil ATF_REQUIRE(!WIFSTOPPED(status));
222 1.1 kamil ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
223 1.1 kamil break;
224 1.1 kamil #ifndef VFORK
225 1.1 kamil case SIGTSTP:
226 1.1 kamil case SIGTTIN:
227 1.1 kamil case SIGTTOU:
228 1.1 kamil #endif
229 1.1 kamil case SIGSTOP:
230 1.1 kamil ATF_REQUIRE(!WIFEXITED(status));
231 1.1 kamil ATF_REQUIRE(!WIFCONTINUED(status));
232 1.1 kamil ATF_REQUIRE(WIFSIGNALED(status));
233 1.1 kamil ATF_REQUIRE(!WIFSTOPPED(status));
234 1.1 kamil ATF_REQUIRE_EQ(WTERMSIG(status), SIGKILL);
235 1.1 kamil ATF_REQUIRE_EQ(!!WCOREDUMP(status), 0);
236 1.1 kamil }
237 1.1 kamil }
238 1.1 kamil
239 1.1 kamil #define RAISE(test, sig) \
240 1.1 kamil ATF_TC(test); \
241 1.1 kamil ATF_TC_HEAD(test, tc) \
242 1.1 kamil { \
243 1.1 kamil \
244 1.1 kamil atf_tc_set_md_var(tc, "descr", \
245 1.1 kamil "raise " #sig " in vfork(2)ed child"); \
246 1.1 kamil } \
247 1.1 kamil \
248 1.1 kamil ATF_TC_BODY(test, tc) \
249 1.1 kamil { \
250 1.1 kamil \
251 1.1 kamil raise_raw(sig); \
252 1.1 kamil }
253 1.1 kamil
254 1.1 kamil RAISE(raise1, SIGKILL) /* non-maskable */
255 1.1 kamil RAISE(raise2, SIGSTOP) /* non-maskable */
256 1.1 kamil RAISE(raise3, SIGTSTP) /* ignored in vfork(2) */
257 1.1 kamil RAISE(raise4, SIGTTIN) /* ignored in vfork(2) */
258 1.1 kamil RAISE(raise5, SIGTTOU) /* ignored in vfork(2) */
259 1.1 kamil RAISE(raise6, SIGABRT) /* regular abort trap */
260 1.1 kamil RAISE(raise7, SIGHUP) /* hangup */
261 1.1 kamil RAISE(raise8, SIGCONT) /* continued? */
262 1.1 kamil
263 1.1 kamil ATF_TP_ADD_TCS(tp)
264 1.1 kamil {
265 1.1 kamil ATF_TP_ADD_TC(tp, raise1);
266 1.1 kamil ATF_TP_ADD_TC(tp, raise2);
267 1.1 kamil ATF_TP_ADD_TC(tp, raise3);
268 1.1 kamil ATF_TP_ADD_TC(tp, raise4);
269 1.1 kamil ATF_TP_ADD_TC(tp, raise5);
270 1.1 kamil ATF_TP_ADD_TC(tp, raise6);
271 1.1 kamil ATF_TP_ADD_TC(tp, raise7);
272 1.1 kamil ATF_TP_ADD_TC(tp, raise8);
273 1.1 kamil
274 1.1 kamil return atf_no_error();
275 1.1 kamil }
276