xref: /openbsd/regress/sys/kern/ptrace2/ptrace_test.c (revision 49a6e16f)
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