xref: /freebsd/lib/libthr/tests/atfork_test.c (revision 7e6ac503ffeb81733272d54af367db58e45e57ca)
1*7e6ac503SKyle Evans /*-
2*7e6ac503SKyle Evans  *
3*7e6ac503SKyle Evans  * Copyright (C) 2024 Kyle Evans <kevans@FreeBSD.org>
4*7e6ac503SKyle Evans  *
5*7e6ac503SKyle Evans  * SPDX-License-Identifier: BSD-2-Clause
6*7e6ac503SKyle Evans  *
7*7e6ac503SKyle Evans  */
8*7e6ac503SKyle Evans 
9*7e6ac503SKyle Evans #include <sys/wait.h>
10*7e6ac503SKyle Evans #include <errno.h>
11*7e6ac503SKyle Evans #include <pthread.h>
12*7e6ac503SKyle Evans #include <signal.h>
13*7e6ac503SKyle Evans #include <stdbool.h>
14*7e6ac503SKyle Evans #include <stdio.h>
15*7e6ac503SKyle Evans #include <unistd.h>
16*7e6ac503SKyle Evans 
17*7e6ac503SKyle Evans #include <atf-c.h>
18*7e6ac503SKyle Evans 
19*7e6ac503SKyle Evans #define	EXIT_NOPREPARE		1
20*7e6ac503SKyle Evans #define	EXIT_CALLEDPARENT	2
21*7e6ac503SKyle Evans #define	EXIT_NOCHILD		3
22*7e6ac503SKyle Evans #define	EXIT_BADORDER		4
23*7e6ac503SKyle Evans 
24*7e6ac503SKyle Evans static int child;
25*7e6ac503SKyle Evans static int forked;
26*7e6ac503SKyle Evans static int parent;
27*7e6ac503SKyle Evans 
28*7e6ac503SKyle Evans static void
29*7e6ac503SKyle Evans basic_prepare(void)
30*7e6ac503SKyle Evans {
31*7e6ac503SKyle Evans 	ATF_REQUIRE(parent == 0);
32*7e6ac503SKyle Evans 	forked++;
33*7e6ac503SKyle Evans }
34*7e6ac503SKyle Evans 
35*7e6ac503SKyle Evans static void
36*7e6ac503SKyle Evans basic_parent(void)
37*7e6ac503SKyle Evans {
38*7e6ac503SKyle Evans 	ATF_REQUIRE(forked != 0);
39*7e6ac503SKyle Evans 	parent++;
40*7e6ac503SKyle Evans }
41*7e6ac503SKyle Evans 
42*7e6ac503SKyle Evans static void
43*7e6ac503SKyle Evans basic_child(void)
44*7e6ac503SKyle Evans {
45*7e6ac503SKyle Evans 	if (!forked)
46*7e6ac503SKyle Evans 		_exit(EXIT_NOPREPARE);
47*7e6ac503SKyle Evans 	if (parent != 0)
48*7e6ac503SKyle Evans 		_exit(EXIT_CALLEDPARENT);
49*7e6ac503SKyle Evans 	child++;
50*7e6ac503SKyle Evans }
51*7e6ac503SKyle Evans 
52*7e6ac503SKyle Evans /*
53*7e6ac503SKyle Evans  * In the basic test, we'll register just once and set some globals to confirm
54*7e6ac503SKyle Evans  * that the prepare/parent callbacks were executed as expected.  The child will
55*7e6ac503SKyle Evans  * use its exit status to communicate to us if the callback was not executed
56*7e6ac503SKyle Evans  * properly since we cannot assert there.  This is a subset of the
57*7e6ac503SKyle Evans  * multi-callback test, but separated out so that it's more obvious from running
58*7e6ac503SKyle Evans  * the atfork_test if pthread_atfork() is completely broken or just
59*7e6ac503SKyle Evans  * out-of-order.
60*7e6ac503SKyle Evans  */
61*7e6ac503SKyle Evans ATF_TC(basic_atfork);
62*7e6ac503SKyle Evans ATF_TC_HEAD(basic_atfork, tc)
63*7e6ac503SKyle Evans {
64*7e6ac503SKyle Evans 	atf_tc_set_md_var(tc, "descr",
65*7e6ac503SKyle Evans 	    "Checks invocation of all three atfork callbacks");
66*7e6ac503SKyle Evans }
67*7e6ac503SKyle Evans ATF_TC_BODY(basic_atfork, tc)
68*7e6ac503SKyle Evans {
69*7e6ac503SKyle Evans 	pid_t p, wpid;
70*7e6ac503SKyle Evans 	int status;
71*7e6ac503SKyle Evans 
72*7e6ac503SKyle Evans 	pthread_atfork(basic_prepare, basic_parent, basic_child);
73*7e6ac503SKyle Evans 
74*7e6ac503SKyle Evans 	p = fork();
75*7e6ac503SKyle Evans 
76*7e6ac503SKyle Evans 	ATF_REQUIRE(p >= 0);
77*7e6ac503SKyle Evans 	if (p == 0)
78*7e6ac503SKyle Evans 		_exit(child != 0 ? 0 : EXIT_NOCHILD);
79*7e6ac503SKyle Evans 
80*7e6ac503SKyle Evans 	/*
81*7e6ac503SKyle Evans 	 * The child can't use any of our standard atf-c(3) macros, so we have
82*7e6ac503SKyle Evans 	 * to rely on the exit status to convey any shenanigans.
83*7e6ac503SKyle Evans 	 */
84*7e6ac503SKyle Evans 	while ((wpid = waitpid(p, &status, 0)) != p) {
85*7e6ac503SKyle Evans 		ATF_REQUIRE_ERRNO(EINTR, wpid == -1);
86*7e6ac503SKyle Evans 		if (wpid == -1)
87*7e6ac503SKyle Evans 			continue;
88*7e6ac503SKyle Evans 	}
89*7e6ac503SKyle Evans 
90*7e6ac503SKyle Evans 	ATF_REQUIRE_MSG(WIFEXITED(status),
91*7e6ac503SKyle Evans 	    "child did not exit cleanly, status %x", status);
92*7e6ac503SKyle Evans 
93*7e6ac503SKyle Evans 	status = WEXITSTATUS(status);
94*7e6ac503SKyle Evans 	ATF_REQUIRE_MSG(status == 0, "atfork in child %s",
95*7e6ac503SKyle Evans 	   status == EXIT_NOPREPARE ? "did not see `prepare` execute" :
96*7e6ac503SKyle Evans 	   (status == EXIT_CALLEDPARENT ? "observed `parent` executing" :
97*7e6ac503SKyle Evans 	   (status == EXIT_NOCHILD ? "did not see `child` execute" :
98*7e6ac503SKyle Evans 	    "mystery")));
99*7e6ac503SKyle Evans 
100*7e6ac503SKyle Evans 	ATF_REQUIRE(forked != 0);
101*7e6ac503SKyle Evans 	ATF_REQUIRE(parent != 0);
102*7e6ac503SKyle Evans 	ATF_REQUIRE(child == 0);
103*7e6ac503SKyle Evans }
104*7e6ac503SKyle Evans 
105*7e6ac503SKyle Evans static void
106*7e6ac503SKyle Evans multi_assert(bool cond, bool can_assert)
107*7e6ac503SKyle Evans {
108*7e6ac503SKyle Evans 	if (can_assert)
109*7e6ac503SKyle Evans 		ATF_REQUIRE((cond));
110*7e6ac503SKyle Evans 	else if (!(cond))
111*7e6ac503SKyle Evans 		_exit(EXIT_BADORDER);
112*7e6ac503SKyle Evans }
113*7e6ac503SKyle Evans 
114*7e6ac503SKyle Evans static void
115*7e6ac503SKyle Evans multi_bump(int *var, int bit, bool can_assert)
116*7e6ac503SKyle Evans {
117*7e6ac503SKyle Evans 	int mask, val;
118*7e6ac503SKyle Evans 
119*7e6ac503SKyle Evans 	mask = (1 << (bit - 1));
120*7e6ac503SKyle Evans 	val = *var;
121*7e6ac503SKyle Evans 
122*7e6ac503SKyle Evans 	/*
123*7e6ac503SKyle Evans 	 * Every bit below this one must be set, and none of the upper bits
124*7e6ac503SKyle Evans 	 * should be set.
125*7e6ac503SKyle Evans 	 */
126*7e6ac503SKyle Evans 	multi_assert((val & mask) == 0, can_assert);
127*7e6ac503SKyle Evans 	if (bit == 1)
128*7e6ac503SKyle Evans 		multi_assert(val == 0, can_assert);
129*7e6ac503SKyle Evans 	else
130*7e6ac503SKyle Evans 		multi_assert((val & ~mask) == (mask - 1), can_assert);
131*7e6ac503SKyle Evans 
132*7e6ac503SKyle Evans 	*var |= mask;
133*7e6ac503SKyle Evans }
134*7e6ac503SKyle Evans 
135*7e6ac503SKyle Evans static void
136*7e6ac503SKyle Evans multi_prepare1(void)
137*7e6ac503SKyle Evans {
138*7e6ac503SKyle Evans 	/*
139*7e6ac503SKyle Evans 	 * The bits are flipped for prepare because it's supposed to be called
140*7e6ac503SKyle Evans 	 * in the reverse order of registration.
141*7e6ac503SKyle Evans 	 */
142*7e6ac503SKyle Evans 	multi_bump(&forked, 2, true);
143*7e6ac503SKyle Evans }
144*7e6ac503SKyle Evans static void
145*7e6ac503SKyle Evans multi_prepare2(void)
146*7e6ac503SKyle Evans {
147*7e6ac503SKyle Evans 	multi_bump(&forked, 1, true);
148*7e6ac503SKyle Evans }
149*7e6ac503SKyle Evans 
150*7e6ac503SKyle Evans static void
151*7e6ac503SKyle Evans multi_parent1(void)
152*7e6ac503SKyle Evans {
153*7e6ac503SKyle Evans 	multi_bump(&parent, 1, true);
154*7e6ac503SKyle Evans }
155*7e6ac503SKyle Evans static void
156*7e6ac503SKyle Evans multi_parent2(void)
157*7e6ac503SKyle Evans {
158*7e6ac503SKyle Evans 	multi_bump(&parent, 2, true);
159*7e6ac503SKyle Evans }
160*7e6ac503SKyle Evans 
161*7e6ac503SKyle Evans static void
162*7e6ac503SKyle Evans multi_child1(void)
163*7e6ac503SKyle Evans {
164*7e6ac503SKyle Evans 	multi_bump(&child, 1, false);
165*7e6ac503SKyle Evans }
166*7e6ac503SKyle Evans static void
167*7e6ac503SKyle Evans multi_child2(void)
168*7e6ac503SKyle Evans {
169*7e6ac503SKyle Evans 	multi_bump(&child, 2, false);
170*7e6ac503SKyle Evans }
171*7e6ac503SKyle Evans 
172*7e6ac503SKyle Evans /*
173*7e6ac503SKyle Evans  * The multi-atfork test works much like the basic one, but it registers
174*7e6ac503SKyle Evans  * multiple times and enforces an order.  The child still does just as strict
175*7e6ac503SKyle Evans  * of tests as the parent and continues to communicate the results of those
176*7e6ac503SKyle Evans  * tests back via its exit status.
177*7e6ac503SKyle Evans  */
178*7e6ac503SKyle Evans ATF_TC(multi_atfork);
179*7e6ac503SKyle Evans ATF_TC_HEAD(multi_atfork, tc)
180*7e6ac503SKyle Evans {
181*7e6ac503SKyle Evans 	atf_tc_set_md_var(tc, "descr",
182*7e6ac503SKyle Evans 	    "Checks that multiple callbacks are called in the documented order");
183*7e6ac503SKyle Evans }
184*7e6ac503SKyle Evans ATF_TC_BODY(multi_atfork, tc)
185*7e6ac503SKyle Evans {
186*7e6ac503SKyle Evans 	pid_t p, wpid;
187*7e6ac503SKyle Evans 	int status;
188*7e6ac503SKyle Evans 
189*7e6ac503SKyle Evans 	pthread_atfork(multi_prepare1, multi_parent1, multi_child1);
190*7e6ac503SKyle Evans 	pthread_atfork(multi_prepare2, multi_parent2, multi_child2);
191*7e6ac503SKyle Evans 
192*7e6ac503SKyle Evans 	p = fork();
193*7e6ac503SKyle Evans 
194*7e6ac503SKyle Evans 	ATF_REQUIRE(p >= 0);
195*7e6ac503SKyle Evans 	if (p == 0)
196*7e6ac503SKyle Evans 		_exit(child != 0 ? 0 : EXIT_NOCHILD);
197*7e6ac503SKyle Evans 
198*7e6ac503SKyle Evans 	/*
199*7e6ac503SKyle Evans 	 * The child can't use any of our standard atf-c(3) macros, so we have
200*7e6ac503SKyle Evans 	 * to rely on the exit status to convey any shenanigans.
201*7e6ac503SKyle Evans 	 */
202*7e6ac503SKyle Evans 	while ((wpid = waitpid(p, &status, 0)) != p) {
203*7e6ac503SKyle Evans 		ATF_REQUIRE_ERRNO(EINTR, wpid == -1);
204*7e6ac503SKyle Evans 		if (wpid == -1)
205*7e6ac503SKyle Evans 			continue;
206*7e6ac503SKyle Evans 	}
207*7e6ac503SKyle Evans 
208*7e6ac503SKyle Evans 	ATF_REQUIRE_MSG(WIFEXITED(status),
209*7e6ac503SKyle Evans 	    "child did not exit cleanly, status %x", status);
210*7e6ac503SKyle Evans 
211*7e6ac503SKyle Evans 	status = WEXITSTATUS(status);
212*7e6ac503SKyle Evans 	ATF_REQUIRE_MSG(status == 0, "atfork in child %s",
213*7e6ac503SKyle Evans 	   status == EXIT_BADORDER ? "called in wrong order" :
214*7e6ac503SKyle Evans 	   (status == EXIT_NOCHILD ? "did not see `child` execute" :
215*7e6ac503SKyle Evans 	    "mystery"));
216*7e6ac503SKyle Evans 
217*7e6ac503SKyle Evans 	ATF_REQUIRE(forked != 0);
218*7e6ac503SKyle Evans 	ATF_REQUIRE(parent != 0);
219*7e6ac503SKyle Evans 	ATF_REQUIRE(child == 0);
220*7e6ac503SKyle Evans }
221*7e6ac503SKyle Evans 
222*7e6ac503SKyle Evans ATF_TP_ADD_TCS(tp)
223*7e6ac503SKyle Evans {
224*7e6ac503SKyle Evans 	ATF_TP_ADD_TC(tp, basic_atfork);
225*7e6ac503SKyle Evans 	ATF_TP_ADD_TC(tp, multi_atfork);
226*7e6ac503SKyle Evans 	return (atf_no_error());
227*7e6ac503SKyle Evans }
228