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