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