1*277539aeSAlan Somers /*-
2*277539aeSAlan Somers * SPDX-License-Identifier: BSD-2-Clause
3*277539aeSAlan Somers *
4*277539aeSAlan Somers * Copyright (c) 2026 ConnectWise
5*277539aeSAlan Somers *
6*277539aeSAlan Somers * Redistribution and use in source and binary forms, with or without
7*277539aeSAlan Somers * modification, are permitted provided that the following conditions
8*277539aeSAlan Somers * are met:
9*277539aeSAlan Somers * 1. Redistributions of source code must retain the above copyright
10*277539aeSAlan Somers * notice, this list of conditions and the following disclaimer.
11*277539aeSAlan Somers * 2. Redistributions in binary form must reproduce the above copyright
12*277539aeSAlan Somers * notice, this list of conditions and the following disclaimer in the
13*277539aeSAlan Somers * documentation and/or other materials provided with the distribution.
14*277539aeSAlan Somers *
15*277539aeSAlan Somers * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*277539aeSAlan Somers * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*277539aeSAlan Somers * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*277539aeSAlan Somers * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*277539aeSAlan Somers * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*277539aeSAlan Somers * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*277539aeSAlan Somers * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*277539aeSAlan Somers * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*277539aeSAlan Somers * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*277539aeSAlan Somers * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*277539aeSAlan Somers * SUCH DAMAGE.
26*277539aeSAlan Somers */
27*277539aeSAlan Somers
28*277539aeSAlan Somers #include <sys/types.h>
29*277539aeSAlan Somers #include <sys/capsicum.h>
30*277539aeSAlan Somers #include <sys/procdesc.h>
31*277539aeSAlan Somers #include <sys/resource.h>
32*277539aeSAlan Somers #include <sys/time.h>
33*277539aeSAlan Somers #include <sys/user.h>
34*277539aeSAlan Somers #include <sys/wait.h>
35*277539aeSAlan Somers
36*277539aeSAlan Somers #include <atf-c.h>
37*277539aeSAlan Somers #include <signal.h>
38*277539aeSAlan Somers #include <string.h>
39*277539aeSAlan Somers
40*277539aeSAlan Somers /* basic usage */
41*277539aeSAlan Somers ATF_TC_WITHOUT_HEAD(basic);
ATF_TC_BODY(basic,tc)42*277539aeSAlan Somers ATF_TC_BODY(basic, tc)
43*277539aeSAlan Somers {
44*277539aeSAlan Somers int fdp = -1;
45*277539aeSAlan Somers pid_t pid;
46*277539aeSAlan Somers int r, status;
47*277539aeSAlan Somers struct __wrusage ru;
48*277539aeSAlan Somers siginfo_t si;
49*277539aeSAlan Somers
50*277539aeSAlan Somers bzero(&ru, sizeof(ru));
51*277539aeSAlan Somers
52*277539aeSAlan Somers pid = pdfork(&fdp, 0);
53*277539aeSAlan Somers if (pid == 0)
54*277539aeSAlan Somers _exit(42);
55*277539aeSAlan Somers ATF_REQUIRE_MSG(pid >= 0, "pdfork failed: %s", strerror(errno));
56*277539aeSAlan Somers ATF_REQUIRE_MSG(fdp >= 0, "pdfork didn't return a process descriptor");
57*277539aeSAlan Somers
58*277539aeSAlan Somers r = pdwait(fdp, &status, WEXITED, &ru, &si);
59*277539aeSAlan Somers ATF_CHECK_EQ(r, 0);
60*277539aeSAlan Somers ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 42);
61*277539aeSAlan Somers ATF_CHECK(ru.wru_self.ru_stime.tv_usec > 0);
62*277539aeSAlan Somers ATF_CHECK_EQ(si.si_signo, SIGCHLD);
63*277539aeSAlan Somers ATF_CHECK_EQ(si.si_pid, pid);
64*277539aeSAlan Somers ATF_CHECK_EQ(si.si_status, WEXITSTATUS(status));
65*277539aeSAlan Somers
66*277539aeSAlan Somers close(fdp);
67*277539aeSAlan Somers }
68*277539aeSAlan Somers
69*277539aeSAlan Somers /* pdwait should work in capability mode */
70*277539aeSAlan Somers ATF_TC_WITHOUT_HEAD(capsicum);
ATF_TC_BODY(capsicum,tc)71*277539aeSAlan Somers ATF_TC_BODY(capsicum, tc)
72*277539aeSAlan Somers {
73*277539aeSAlan Somers int fdp = -1;
74*277539aeSAlan Somers pid_t pid;
75*277539aeSAlan Somers int status, r;
76*277539aeSAlan Somers
77*277539aeSAlan Somers pid = pdfork(&fdp, 0);
78*277539aeSAlan Somers if (pid == 0)
79*277539aeSAlan Somers _exit(42);
80*277539aeSAlan Somers ATF_REQUIRE_MSG(pid >= 0, "pdfork failed: %s", strerror(errno));
81*277539aeSAlan Somers ATF_REQUIRE_MSG(fdp >= 0, "pdfork didn't return a process descriptor");
82*277539aeSAlan Somers
83*277539aeSAlan Somers ATF_CHECK_EQ_MSG(0, cap_enter(), "cap_enter: %s", strerror(errno));
84*277539aeSAlan Somers r = pdwait(fdp, &status, WEXITED, NULL, NULL);
85*277539aeSAlan Somers ATF_CHECK_EQ(r, 0);
86*277539aeSAlan Somers ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 42);
87*277539aeSAlan Somers
88*277539aeSAlan Somers close(fdp);
89*277539aeSAlan Somers }
90*277539aeSAlan Somers
91*277539aeSAlan Somers /* pdwait should return EBADF if its argument is not a file descriptor */
92*277539aeSAlan Somers ATF_TC_WITHOUT_HEAD(ebadf);
ATF_TC_BODY(ebadf,tc)93*277539aeSAlan Somers ATF_TC_BODY(ebadf, tc)
94*277539aeSAlan Somers {
95*277539aeSAlan Somers ATF_REQUIRE_ERRNO(EBADF, pdwait(99999, NULL, WEXITED, NULL, NULL) < 0);
96*277539aeSAlan Somers }
97*277539aeSAlan Somers
98*277539aeSAlan Somers /* pdwait should return efault if the status argument is invalid. */
99*277539aeSAlan Somers ATF_TC_WITHOUT_HEAD(efault1);
ATF_TC_BODY(efault1,tc)100*277539aeSAlan Somers ATF_TC_BODY(efault1, tc)
101*277539aeSAlan Somers {
102*277539aeSAlan Somers int fdp = -1;
103*277539aeSAlan Somers pid_t pid;
104*277539aeSAlan Somers
105*277539aeSAlan Somers pid = pdfork(&fdp, 0);
106*277539aeSAlan Somers if (pid == 0)
107*277539aeSAlan Somers _exit(42);
108*277539aeSAlan Somers ATF_REQUIRE_MSG(pid >= 0, "pdfork failed: %s", strerror(errno));
109*277539aeSAlan Somers ATF_REQUIRE_MSG(fdp >= 0, "pdfork didn't return a process descriptor");
110*277539aeSAlan Somers
111*277539aeSAlan Somers ATF_CHECK_ERRNO(EFAULT, pdwait(fdp, (int*)-1, WEXITED, NULL, NULL) < 0);
112*277539aeSAlan Somers
113*277539aeSAlan Somers close(fdp);
114*277539aeSAlan Somers }
115*277539aeSAlan Somers
116*277539aeSAlan Somers /* pdwait should return efault2 if the usage argument is invalid. */
117*277539aeSAlan Somers ATF_TC_WITHOUT_HEAD(efault2);
ATF_TC_BODY(efault2,tc)118*277539aeSAlan Somers ATF_TC_BODY(efault2, tc)
119*277539aeSAlan Somers {
120*277539aeSAlan Somers int fdp = -1;
121*277539aeSAlan Somers pid_t pid;
122*277539aeSAlan Somers
123*277539aeSAlan Somers pid = pdfork(&fdp, 0);
124*277539aeSAlan Somers if (pid == 0)
125*277539aeSAlan Somers _exit(42);
126*277539aeSAlan Somers ATF_REQUIRE_MSG(pid >= 0, "pdfork failed: %s", strerror(errno));
127*277539aeSAlan Somers ATF_REQUIRE_MSG(fdp >= 0, "pdfork didn't return a process descriptor");
128*277539aeSAlan Somers
129*277539aeSAlan Somers ATF_CHECK_ERRNO(EFAULT,
130*277539aeSAlan Somers pdwait(fdp, NULL, WEXITED, (struct __wrusage*)-1, NULL) < 0);
131*277539aeSAlan Somers
132*277539aeSAlan Somers close(fdp);
133*277539aeSAlan Somers }
134*277539aeSAlan Somers
135*277539aeSAlan Somers /* pdwait should return efault if the siginfo argument is invalid. */
136*277539aeSAlan Somers ATF_TC_WITHOUT_HEAD(efault3);
ATF_TC_BODY(efault3,tc)137*277539aeSAlan Somers ATF_TC_BODY(efault3, tc)
138*277539aeSAlan Somers {
139*277539aeSAlan Somers int fdp = -1;
140*277539aeSAlan Somers pid_t pid;
141*277539aeSAlan Somers
142*277539aeSAlan Somers pid = pdfork(&fdp, 0);
143*277539aeSAlan Somers if (pid == 0)
144*277539aeSAlan Somers _exit(42);
145*277539aeSAlan Somers ATF_REQUIRE_MSG(pid >= 0, "pdfork failed: %s", strerror(errno));
146*277539aeSAlan Somers ATF_REQUIRE_MSG(fdp >= 0, "pdfork didn't return a process descriptor");
147*277539aeSAlan Somers
148*277539aeSAlan Somers ATF_CHECK_ERRNO(EFAULT,
149*277539aeSAlan Somers pdwait(fdp, NULL, WEXITED, NULL, (struct __siginfo*)-1) < 0);
150*277539aeSAlan Somers
151*277539aeSAlan Somers close(fdp);
152*277539aeSAlan Somers }
153*277539aeSAlan Somers
154*277539aeSAlan Somers /* pdwait should return einval if the arguments are bad */
155*277539aeSAlan Somers ATF_TC_WITHOUT_HEAD(einval);
ATF_TC_BODY(einval,tc)156*277539aeSAlan Somers ATF_TC_BODY(einval, tc)
157*277539aeSAlan Somers {
158*277539aeSAlan Somers int fdp = -1;
159*277539aeSAlan Somers pid_t pid;
160*277539aeSAlan Somers
161*277539aeSAlan Somers pid = pdfork(&fdp, 0);
162*277539aeSAlan Somers if (pid == 0)
163*277539aeSAlan Somers _exit(42);
164*277539aeSAlan Somers ATF_REQUIRE_MSG(pid >= 0, "pdfork failed: %s", strerror(errno));
165*277539aeSAlan Somers ATF_REQUIRE_MSG(fdp >= 0, "pdfork didn't return a process descriptor");
166*277539aeSAlan Somers
167*277539aeSAlan Somers ATF_CHECK_ERRNO(EINVAL, pdwait(fdp, NULL, 0, NULL, NULL) < 0);
168*277539aeSAlan Somers ATF_CHECK_ERRNO(EINVAL, pdwait(fdp, NULL, -1, NULL, NULL) < 0);
169*277539aeSAlan Somers ATF_CHECK_ERRNO(EINVAL,
170*277539aeSAlan Somers pdwait(STDERR_FILENO, NULL, WEXITED, NULL, NULL) < 0);
171*277539aeSAlan Somers
172*277539aeSAlan Somers close(fdp);
173*277539aeSAlan Somers }
174*277539aeSAlan Somers
175*277539aeSAlan Somers /* pdwait should fail without the cap_pdwait_rights bit */
176*277539aeSAlan Somers ATF_TC_WITHOUT_HEAD(enotcap);
ATF_TC_BODY(enotcap,tc)177*277539aeSAlan Somers ATF_TC_BODY(enotcap, tc)
178*277539aeSAlan Somers {
179*277539aeSAlan Somers cap_rights_t rights;
180*277539aeSAlan Somers int fdp = -1;
181*277539aeSAlan Somers pid_t pid;
182*277539aeSAlan Somers int status;
183*277539aeSAlan Somers
184*277539aeSAlan Somers /*cap_rights_init(&rights, CAP_RIGHTS_ALL);*/
185*277539aeSAlan Somers CAP_ALL(&rights);
186*277539aeSAlan Somers cap_rights_clear(&rights, CAP_PDWAIT);
187*277539aeSAlan Somers
188*277539aeSAlan Somers pid = pdfork(&fdp, 0);
189*277539aeSAlan Somers if (pid == 0)
190*277539aeSAlan Somers _exit(42);
191*277539aeSAlan Somers ATF_REQUIRE_MSG(pid >= 0, "pdfork failed: %s", strerror(errno));
192*277539aeSAlan Somers ATF_REQUIRE_MSG(fdp >= 0, "pdfork didn't return a process descriptor");
193*277539aeSAlan Somers
194*277539aeSAlan Somers ATF_CHECK_EQ_MSG(0, cap_enter(), "cap_enter: %s", strerror(errno));
195*277539aeSAlan Somers ATF_REQUIRE_EQ_MSG(0, cap_rights_limit(fdp, &rights),
196*277539aeSAlan Somers "cap_rights_limit %s", strerror(errno));
197*277539aeSAlan Somers
198*277539aeSAlan Somers ATF_REQUIRE_ERRNO(ENOTCAPABLE,
199*277539aeSAlan Somers pdwait(fdp, &status, WEXITED, NULL, NULL) < 0);
200*277539aeSAlan Somers
201*277539aeSAlan Somers close(fdp);
202*277539aeSAlan Somers }
203*277539aeSAlan Somers
204*277539aeSAlan Somers /*
205*277539aeSAlan Somers * Even though the process descriptor is still open, there is no more process
206*277539aeSAlan Somers * to signal after pdwait() has returned.
207*277539aeSAlan Somers */
208*277539aeSAlan Somers ATF_TC_WITHOUT_HEAD(pdkill_after_pdwait);
ATF_TC_BODY(pdkill_after_pdwait,tc)209*277539aeSAlan Somers ATF_TC_BODY(pdkill_after_pdwait, tc)
210*277539aeSAlan Somers {
211*277539aeSAlan Somers int fdp = -1;
212*277539aeSAlan Somers pid_t pid;
213*277539aeSAlan Somers int r, status;
214*277539aeSAlan Somers
215*277539aeSAlan Somers pid = pdfork(&fdp, 0);
216*277539aeSAlan Somers if (pid == 0)
217*277539aeSAlan Somers _exit(42);
218*277539aeSAlan Somers ATF_REQUIRE_MSG(pid >= 0, "pdfork failed: %s", strerror(errno));
219*277539aeSAlan Somers ATF_REQUIRE_MSG(fdp >= 0, "pdfork didn't return a process descriptor");
220*277539aeSAlan Somers
221*277539aeSAlan Somers r = pdwait(fdp, &status, WEXITED, NULL, NULL);
222*277539aeSAlan Somers ATF_CHECK_EQ(r, 0);
223*277539aeSAlan Somers ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 42);
224*277539aeSAlan Somers
225*277539aeSAlan Somers ATF_REQUIRE_ERRNO(ESRCH, pdkill(fdp, SIGTERM) < 0);
226*277539aeSAlan Somers
227*277539aeSAlan Somers close(fdp);
228*277539aeSAlan Somers }
229*277539aeSAlan Somers
230*277539aeSAlan Somers /*
231*277539aeSAlan Somers * Even though the process descriptor is still open, there is no more status to
232*277539aeSAlan Somers * return after a pid-based wait() function has already returned it.
233*277539aeSAlan Somers */
234*277539aeSAlan Somers ATF_TC_WITHOUT_HEAD(pdwait_after_waitpid);
ATF_TC_BODY(pdwait_after_waitpid,tc)235*277539aeSAlan Somers ATF_TC_BODY(pdwait_after_waitpid, tc)
236*277539aeSAlan Somers {
237*277539aeSAlan Somers int fdp = -1;
238*277539aeSAlan Somers pid_t pid, waited_pid;
239*277539aeSAlan Somers int status;
240*277539aeSAlan Somers
241*277539aeSAlan Somers pid = pdfork(&fdp, 0);
242*277539aeSAlan Somers if (pid == 0)
243*277539aeSAlan Somers _exit(42);
244*277539aeSAlan Somers ATF_REQUIRE_MSG(pid >= 0, "pdfork failed: %s", strerror(errno));
245*277539aeSAlan Somers ATF_REQUIRE_MSG(fdp >= 0, "pdfork didn't return a process descriptor");
246*277539aeSAlan Somers
247*277539aeSAlan Somers waited_pid = waitpid(pid, &status, WEXITED);
248*277539aeSAlan Somers
249*277539aeSAlan Somers ATF_CHECK_EQ(pid, waited_pid);
250*277539aeSAlan Somers ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 42);
251*277539aeSAlan Somers
252*277539aeSAlan Somers ATF_REQUIRE_ERRNO(ESRCH, pdwait(fdp, NULL, WEXITED, NULL, NULL) < 0);
253*277539aeSAlan Somers
254*277539aeSAlan Somers close(fdp);
255*277539aeSAlan Somers }
256*277539aeSAlan Somers
257*277539aeSAlan Somers /* Called twice, waitpid should return ESRCH the second time */
258*277539aeSAlan Somers ATF_TC_WITHOUT_HEAD(twice);
ATF_TC_BODY(twice,tc)259*277539aeSAlan Somers ATF_TC_BODY(twice, tc)
260*277539aeSAlan Somers {
261*277539aeSAlan Somers int fdp = -1;
262*277539aeSAlan Somers pid_t pid;
263*277539aeSAlan Somers int r, status;
264*277539aeSAlan Somers
265*277539aeSAlan Somers pid = pdfork(&fdp, 0);
266*277539aeSAlan Somers if (pid == 0)
267*277539aeSAlan Somers _exit(42);
268*277539aeSAlan Somers ATF_REQUIRE_MSG(pid >= 0, "pdfork failed: %s", strerror(errno));
269*277539aeSAlan Somers ATF_REQUIRE_MSG(fdp >= 0, "pdfork didn't return a process descriptor");
270*277539aeSAlan Somers
271*277539aeSAlan Somers r = pdwait(fdp, &status, WEXITED, NULL, NULL);
272*277539aeSAlan Somers ATF_CHECK_EQ(r, 0);
273*277539aeSAlan Somers ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 42);
274*277539aeSAlan Somers
275*277539aeSAlan Somers ATF_REQUIRE_ERRNO(ESRCH, pdwait(fdp, NULL, WEXITED, NULL, NULL) < 0);
276*277539aeSAlan Somers
277*277539aeSAlan Somers close(fdp);
278*277539aeSAlan Somers }
279*277539aeSAlan Somers
ATF_TP_ADD_TCS(tp)280*277539aeSAlan Somers ATF_TP_ADD_TCS(tp)
281*277539aeSAlan Somers {
282*277539aeSAlan Somers ATF_TP_ADD_TC(tp, basic);
283*277539aeSAlan Somers ATF_TP_ADD_TC(tp, capsicum);
284*277539aeSAlan Somers ATF_TP_ADD_TC(tp, ebadf);
285*277539aeSAlan Somers ATF_TP_ADD_TC(tp, enotcap);
286*277539aeSAlan Somers ATF_TP_ADD_TC(tp, twice);
287*277539aeSAlan Somers ATF_TP_ADD_TC(tp, efault1);
288*277539aeSAlan Somers ATF_TP_ADD_TC(tp, efault2);
289*277539aeSAlan Somers ATF_TP_ADD_TC(tp, efault3);
290*277539aeSAlan Somers ATF_TP_ADD_TC(tp, einval);
291*277539aeSAlan Somers ATF_TP_ADD_TC(tp, pdwait_after_waitpid);
292*277539aeSAlan Somers ATF_TP_ADD_TC(tp, pdkill_after_pdwait);
293*277539aeSAlan Somers
294*277539aeSAlan Somers return (atf_no_error());
295*277539aeSAlan Somers }
296