xref: /netbsd-src/tests/lib/libc/sys/t_fork.c (revision deb6f0161a9109e7de9b519dc8dfb9478668dcdd)
1 /*	$NetBSD: t_fork.c,v 1.3 2018/05/19 05:10:16 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.3 2018/05/19 05:10:16 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 	/*
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