1 /* $OpenBSD: ptrace_test.c,v 1.4 2021/12/13 16:56:50 deraadt Exp $ */
2
3 /*-
4 * Copyright (c) 2015 John Baldwin <jhb@FreeBSD.org>
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 "macros.h"
29
30 #include <sys/types.h>
31 #include <sys/event.h>
32 #include <sys/file.h>
33 #include <sys/time.h>
34 #include <sys/signal.h>
35 #include <sys/proc.h>
36 #include <sys/ptrace.h>
37 #include <sys/queue.h>
38 #include <sys/syscall.h>
39 #include <sys/sysctl.h>
40 #include <sys/user.h>
41 #include <sys/wait.h>
42 #include <errno.h>
43 #include <pthread.h>
44 #include <sched.h>
45 #include <semaphore.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 #include "atf-c.h"
51
52 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
53
54 /*
55 * A variant of ATF_REQUIRE that is suitable for use in child
56 * processes. This only works if the parent process is tripped up by
57 * the early exit and fails some requirement itself.
58 */
59 #define CHILD_REQUIRE(exp) do { \
60 if (!(exp)) \
61 child_fail_require(__FILE__, __LINE__, \
62 #exp " not met"); \
63 } while (0)
64
65 static __dead2 void
child_fail_require(const char * file,int line,const char * str)66 child_fail_require(const char *file, int line, const char *str)
67 {
68 char buf[128];
69
70 snprintf(buf, sizeof(buf), "%s:%d: %s\n", file, line, str);
71 write(2, buf, strlen(buf));
72 _exit(32);
73 }
74
75 static void
trace_me(void)76 trace_me(void)
77 {
78
79 /* Attach the parent process as a tracer of this process. */
80 CHILD_REQUIRE(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
81
82 /* Trigger a stop. */
83 raise(SIGSTOP);
84 }
85
86 static void
attach_child(pid_t pid)87 attach_child(pid_t pid)
88 {
89 pid_t wpid;
90 int status;
91
92 ATF_REQUIRE(ptrace(PT_ATTACH, pid, NULL, 0) == 0);
93
94 wpid = waitpid(pid, &status, 0);
95 ATF_REQUIRE(wpid == pid);
96 ATF_REQUIRE(WIFSTOPPED(status));
97 ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
98 }
99
100 static void
wait_for_zombie(pid_t pid)101 wait_for_zombie(pid_t pid)
102 {
103
104 /*
105 * Wait for a process to exit. This is kind of gross, but
106 * there is not a better way.
107 *
108 * Prior to r325719, the kern.proc.pid.<pid> sysctl failed
109 * with ESRCH. After that change, a valid struct kinfo_proc
110 * is returned for zombies with ki_stat set to SZOMB.
111 */
112 for (;;) {
113 struct kinfo_proc kp;
114 size_t len;
115 int mib[6];
116
117 mib[0] = CTL_KERN;
118 mib[1] = KERN_PROC;
119 mib[2] = KERN_PROC_PID;
120 mib[3] = pid;
121 mib[4] = len = sizeof(kp);
122 mib[5] = 1;
123 if (sysctl(mib, nitems(mib), &kp, &len, NULL, 0) == -1) {
124 ATF_REQUIRE(errno == ESRCH);
125 break;
126 }
127 if (kp.p_stat == SDEAD)
128 break;
129 usleep(5000);
130 }
131 }
132
133 /*
134 * Verify that a parent debugger process "sees" the exit of a debugged
135 * process exactly once when attached via PT_TRACE_ME.
136 */
137 ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_trace_me);
ATF_TC_BODY(ptrace__parent_wait_after_trace_me,tc)138 ATF_TC_BODY(ptrace__parent_wait_after_trace_me, tc)
139 {
140 pid_t child, wpid;
141 int status;
142
143 ATF_REQUIRE((child = fork()) != -1);
144 if (child == 0) {
145 /* Child process. */
146 trace_me();
147
148 _exit(1);
149 }
150
151 /* Parent process. */
152
153 /* The first wait() should report the stop from SIGSTOP. */
154 wpid = waitpid(child, &status, 0);
155 printf("first %d\n", wpid);
156 ATF_REQUIRE(wpid == child);
157 ATF_REQUIRE(WIFSTOPPED(status));
158 ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
159
160 /* Continue the child ignoring the SIGSTOP. */
161 ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
162
163 /* The second wait() should report the exit status. */
164 wpid = waitpid(child, &status, 0);
165 printf("second %d\n", wpid);
166 ATF_REQUIRE(wpid == child);
167 ATF_REQUIRE(WIFEXITED(status));
168 ATF_REQUIRE(WEXITSTATUS(status) == 1);
169
170 /* The child should no longer exist. */
171 wpid = waitpid(child, &status, 0);
172 printf("third %d\n", wpid);
173 ATF_REQUIRE(wpid == -1);
174 ATF_REQUIRE(errno == ECHILD);
175 }
176
177 /*
178 * Verify that a parent debugger process "sees" the exit of a debugged
179 * process exactly once when attached via PT_ATTACH.
180 */
181 ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_attach);
ATF_TC_BODY(ptrace__parent_wait_after_attach,tc)182 ATF_TC_BODY(ptrace__parent_wait_after_attach, tc)
183 {
184 pid_t child, wpid;
185 int cpipe[2], status;
186 char c;
187
188 ATF_REQUIRE(pipe(cpipe) == 0);
189 ATF_REQUIRE((child = fork()) != -1);
190 if (child == 0) {
191 /* Child process. */
192 close(cpipe[0]);
193
194 /* Wait for the parent to attach. */
195 CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == 0);
196
197 _exit(1);
198 }
199 close(cpipe[1]);
200
201 /* Parent process. */
202
203 /* Attach to the child process. */
204 attach_child(child);
205
206 /* Continue the child ignoring the SIGSTOP. */
207 ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
208
209 /* Signal the child to exit. */
210 close(cpipe[0]);
211
212 /* The second wait() should report the exit status. */
213 wpid = waitpid(child, &status, 0);
214 ATF_REQUIRE(wpid == child);
215 ATF_REQUIRE(WIFEXITED(status));
216 ATF_REQUIRE(WEXITSTATUS(status) == 1);
217
218 /* The child should no longer exist. */
219 wpid = waitpid(child, &status, 0);
220 ATF_REQUIRE(wpid == -1);
221 ATF_REQUIRE(errno == ECHILD);
222 }
223
224 /*
225 * Verify that a parent process "sees" the exit of a debugged process only
226 * after the debugger has seen it.
227 */
228 ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_child_debugger);
ATF_TC_BODY(ptrace__parent_sees_exit_after_child_debugger,tc)229 ATF_TC_BODY(ptrace__parent_sees_exit_after_child_debugger, tc)
230 {
231 pid_t child, debugger, wpid;
232 int cpipe[2], dpipe[2], status;
233 char c;
234
235 if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false))
236 atf_tc_skip("https://bugs.freebsd.org/239399");
237
238 ATF_REQUIRE(pipe(cpipe) == 0);
239 ATF_REQUIRE((child = fork()) != -1);
240
241 if (child == 0) {
242 /* Child process. */
243 close(cpipe[0]);
244
245 /* Wait for parent to be ready. */
246 CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c));
247
248 _exit(1);
249 }
250 close(cpipe[1]);
251
252 ATF_REQUIRE(pipe(dpipe) == 0);
253 ATF_REQUIRE((debugger = fork()) != -1);
254
255 if (debugger == 0) {
256 /* Debugger process. */
257 close(dpipe[0]);
258
259 CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1);
260
261 wpid = waitpid(child, &status, 0);
262 CHILD_REQUIRE(wpid == child);
263 CHILD_REQUIRE(WIFSTOPPED(status));
264 CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP);
265
266 CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
267
268 /* Signal parent that debugger is attached. */
269 CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c));
270
271 /* Wait for parent's failed wait. */
272 CHILD_REQUIRE(read(dpipe[1], &c, sizeof(c)) == 0);
273
274 wpid = waitpid(child, &status, 0);
275 CHILD_REQUIRE(wpid == child);
276 CHILD_REQUIRE(WIFEXITED(status));
277 CHILD_REQUIRE(WEXITSTATUS(status) == 1);
278
279 _exit(0);
280 }
281 close(dpipe[1]);
282
283 /* Parent process. */
284
285 /* Wait for the debugger to attach to the child. */
286 ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c));
287
288 /* Release the child. */
289 ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c));
290 ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0);
291 close(cpipe[0]);
292
293 wait_for_zombie(child);
294
295 /*
296 * This wait should return a pid of 0 to indicate no status to
297 * report. The parent should see the child as non-exited
298 * until the debugger sees the exit.
299 */
300 wpid = waitpid(child, &status, WNOHANG);
301 ATF_REQUIRE(wpid == 0);
302
303 /* Signal the debugger to wait for the child. */
304 close(dpipe[0]);
305
306 /* Wait for the debugger. */
307 wpid = waitpid(debugger, &status, 0);
308 ATF_REQUIRE(wpid == debugger);
309 ATF_REQUIRE(WIFEXITED(status));
310 ATF_REQUIRE(WEXITSTATUS(status) == 0);
311
312 /* The child process should now be ready. */
313 wpid = waitpid(child, &status, WNOHANG);
314 ATF_REQUIRE(wpid == child);
315 ATF_REQUIRE(WIFEXITED(status));
316 ATF_REQUIRE(WEXITSTATUS(status) == 1);
317 }
318
319 /*
320 * Verify that a parent process "sees" the exit of a debugged process
321 * only after a non-direct-child debugger has seen it. In particular,
322 * various wait() calls in the parent must avoid failing with ESRCH by
323 * checking the parent's orphan list for the debugee.
324 */
325 ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_unrelated_debugger);
ATF_TC_BODY(ptrace__parent_sees_exit_after_unrelated_debugger,tc)326 ATF_TC_BODY(ptrace__parent_sees_exit_after_unrelated_debugger, tc)
327 {
328 pid_t child, debugger, fpid, wpid;
329 int cpipe[2], dpipe[2], status;
330 char c;
331
332 ATF_REQUIRE(pipe(cpipe) == 0);
333 ATF_REQUIRE((child = fork()) != -1);
334
335 if (child == 0) {
336 /* Child process. */
337 close(cpipe[0]);
338
339 /* Wait for parent to be ready. */
340 CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c));
341
342 _exit(1);
343 }
344 close(cpipe[1]);
345
346 ATF_REQUIRE(pipe(dpipe) == 0);
347 ATF_REQUIRE((debugger = fork()) != -1);
348
349 if (debugger == 0) {
350 /* Debugger parent. */
351
352 /*
353 * Fork again and drop the debugger parent so that the
354 * debugger is not a child of the main parent.
355 */
356 CHILD_REQUIRE((fpid = fork()) != -1);
357 if (fpid != 0)
358 _exit(2);
359
360 /* Debugger process. */
361 close(dpipe[0]);
362
363 CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1);
364
365 wpid = waitpid(child, &status, 0);
366 CHILD_REQUIRE(wpid == child);
367 CHILD_REQUIRE(WIFSTOPPED(status));
368 CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP);
369
370 CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
371
372 /* Signal parent that debugger is attached. */
373 CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c));
374
375 /* Wait for parent's failed wait. */
376 CHILD_REQUIRE(read(dpipe[1], &c, sizeof(c)) == sizeof(c));
377
378 wpid = waitpid(child, &status, 0);
379 CHILD_REQUIRE(wpid == child);
380 CHILD_REQUIRE(WIFEXITED(status));
381 CHILD_REQUIRE(WEXITSTATUS(status) == 1);
382
383 _exit(0);
384 }
385 close(dpipe[1]);
386
387 /* Parent process. */
388
389 /* Wait for the debugger parent process to exit. */
390 wpid = waitpid(debugger, &status, 0);
391 ATF_REQUIRE(wpid == debugger);
392 ATF_REQUIRE(WIFEXITED(status));
393 ATF_REQUIRE(WEXITSTATUS(status) == 2);
394
395 /* A WNOHANG wait here should see the non-exited child. */
396 wpid = waitpid(child, &status, WNOHANG);
397 ATF_REQUIRE(wpid == 0);
398
399 /* Wait for the debugger to attach to the child. */
400 ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c));
401
402 /* Release the child. */
403 ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c));
404 ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0);
405 close(cpipe[0]);
406
407 wait_for_zombie(child);
408
409 /*
410 * This wait should return a pid of 0 to indicate no status to
411 * report. The parent should see the child as non-exited
412 * until the debugger sees the exit.
413 */
414 wpid = waitpid(child, &status, WNOHANG);
415 ATF_REQUIRE(wpid == 0);
416
417 /* Signal the debugger to wait for the child. */
418 ATF_REQUIRE(write(dpipe[0], &c, sizeof(c)) == sizeof(c));
419
420 /* Wait for the debugger. */
421 ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == 0);
422 close(dpipe[0]);
423
424 /* The child process should now be ready. */
425 wpid = waitpid(child, &status, WNOHANG);
426 ATF_REQUIRE(wpid == child);
427 ATF_REQUIRE(WIFEXITED(status));
428 ATF_REQUIRE(WEXITSTATUS(status) == 1);
429 }
430
ATF_TP_ADD_TCS(tp)431 ATF_TP_ADD_TCS(tp)
432 {
433
434 ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_trace_me);
435 ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_attach);
436 ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_child_debugger);
437 ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_unrelated_debugger);
438
439 return (atf_no_error());
440 }
441
442