xref: /freebsd/tests/sys/kern/ptrace_test.c (revision dfa8ba12e17796cf7d67aaf6ad5c73ea443c0763)
1c209e3e2SJohn Baldwin /*-
2c209e3e2SJohn Baldwin  * Copyright (c) 2015 John Baldwin <jhb@FreeBSD.org>
3c209e3e2SJohn Baldwin  * All rights reserved.
4c209e3e2SJohn Baldwin  *
5c209e3e2SJohn Baldwin  * Redistribution and use in source and binary forms, with or without
6c209e3e2SJohn Baldwin  * modification, are permitted provided that the following conditions
7c209e3e2SJohn Baldwin  * are met:
8c209e3e2SJohn Baldwin  * 1. Redistributions of source code must retain the above copyright
9c209e3e2SJohn Baldwin  *    notice, this list of conditions and the following disclaimer.
10c209e3e2SJohn Baldwin  * 2. Redistributions in binary form must reproduce the above copyright
11c209e3e2SJohn Baldwin  *    notice, this list of conditions and the following disclaimer in the
12c209e3e2SJohn Baldwin  *    documentation and/or other materials provided with the distribution.
13c209e3e2SJohn Baldwin  *
14c209e3e2SJohn Baldwin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15c209e3e2SJohn Baldwin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16c209e3e2SJohn Baldwin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17c209e3e2SJohn Baldwin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18c209e3e2SJohn Baldwin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19c209e3e2SJohn Baldwin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20c209e3e2SJohn Baldwin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21c209e3e2SJohn Baldwin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22c209e3e2SJohn Baldwin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23c209e3e2SJohn Baldwin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24c209e3e2SJohn Baldwin  * SUCH DAMAGE.
25c209e3e2SJohn Baldwin  */
26c209e3e2SJohn Baldwin 
27c209e3e2SJohn Baldwin #include <sys/cdefs.h>
28c209e3e2SJohn Baldwin __FBSDID("$FreeBSD$");
29c209e3e2SJohn Baldwin 
30c209e3e2SJohn Baldwin #include <sys/types.h>
31c209e3e2SJohn Baldwin #include <sys/ptrace.h>
3257c74f5bSJohn Baldwin #include <sys/sysctl.h>
3357c74f5bSJohn Baldwin #include <sys/user.h>
34c209e3e2SJohn Baldwin #include <sys/wait.h>
35c209e3e2SJohn Baldwin #include <errno.h>
36c209e3e2SJohn Baldwin #include <signal.h>
37*dfa8ba12SJohn Baldwin #include <stdio.h>
38c209e3e2SJohn Baldwin #include <stdlib.h>
39c209e3e2SJohn Baldwin #include <unistd.h>
40c209e3e2SJohn Baldwin #include <atf-c.h>
41c209e3e2SJohn Baldwin 
42c209e3e2SJohn Baldwin /*
43*dfa8ba12SJohn Baldwin  * A variant of ATF_REQUIRE that is suitable for use in child
44*dfa8ba12SJohn Baldwin  * processes.  This only works if the parent process is tripped up by
45*dfa8ba12SJohn Baldwin  * the early exit and fails some requirement itself.
46*dfa8ba12SJohn Baldwin  */
47*dfa8ba12SJohn Baldwin #define	CHILD_REQUIRE(exp) do {						\
48*dfa8ba12SJohn Baldwin 		if (!(exp))						\
49*dfa8ba12SJohn Baldwin 			child_fail_require(__FILE__, __LINE__,		\
50*dfa8ba12SJohn Baldwin 			    #exp " not met");				\
51*dfa8ba12SJohn Baldwin 	} while (0)
52*dfa8ba12SJohn Baldwin 
53*dfa8ba12SJohn Baldwin static void __dead2
54*dfa8ba12SJohn Baldwin child_fail_require(const char *file, int line, const char *str)
55*dfa8ba12SJohn Baldwin {
56*dfa8ba12SJohn Baldwin 	char buf[128];
57*dfa8ba12SJohn Baldwin 
58*dfa8ba12SJohn Baldwin 	snprintf(buf, sizeof(buf), "%s:%d: %s\n", file, line, str);
59*dfa8ba12SJohn Baldwin 	write(2, buf, strlen(buf));
60*dfa8ba12SJohn Baldwin 	_exit(32);
61*dfa8ba12SJohn Baldwin }
62*dfa8ba12SJohn Baldwin 
63*dfa8ba12SJohn Baldwin /*
64c209e3e2SJohn Baldwin  * Verify that a parent debugger process "sees" the exit of a debugged
65c209e3e2SJohn Baldwin  * process exactly once when attached via PT_TRACE_ME.
66c209e3e2SJohn Baldwin  */
67c209e3e2SJohn Baldwin ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_trace_me);
68c209e3e2SJohn Baldwin ATF_TC_BODY(ptrace__parent_wait_after_trace_me, tc)
69c209e3e2SJohn Baldwin {
70c209e3e2SJohn Baldwin 	pid_t child, wpid;
71c209e3e2SJohn Baldwin 	int status;
72c209e3e2SJohn Baldwin 
73c209e3e2SJohn Baldwin 	ATF_REQUIRE((child = fork()) != -1);
74c209e3e2SJohn Baldwin 	if (child == 0) {
75c209e3e2SJohn Baldwin 		/* Child process. */
76*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
77c209e3e2SJohn Baldwin 
78c209e3e2SJohn Baldwin 		/* Trigger a stop. */
79c209e3e2SJohn Baldwin 		raise(SIGSTOP);
80c209e3e2SJohn Baldwin 
81c209e3e2SJohn Baldwin 		exit(1);
82c209e3e2SJohn Baldwin 	}
83c209e3e2SJohn Baldwin 
84c209e3e2SJohn Baldwin 	/* Parent process. */
85c209e3e2SJohn Baldwin 
86c209e3e2SJohn Baldwin 	/* The first wait() should report the stop from SIGSTOP. */
87c209e3e2SJohn Baldwin 	wpid = waitpid(child, &status, 0);
88c209e3e2SJohn Baldwin 	ATF_REQUIRE(wpid == child);
89c209e3e2SJohn Baldwin 	ATF_REQUIRE(WIFSTOPPED(status));
90c209e3e2SJohn Baldwin 	ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
91c209e3e2SJohn Baldwin 
92c209e3e2SJohn Baldwin 	/* Continue the child ignoring the SIGSTOP. */
93c209e3e2SJohn Baldwin 	ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
94c209e3e2SJohn Baldwin 
95c209e3e2SJohn Baldwin 	/* The second wait() should report the exit status. */
96c209e3e2SJohn Baldwin 	wpid = waitpid(child, &status, 0);
97c209e3e2SJohn Baldwin 	ATF_REQUIRE(wpid == child);
98c209e3e2SJohn Baldwin 	ATF_REQUIRE(WIFEXITED(status));
99c209e3e2SJohn Baldwin 	ATF_REQUIRE(WEXITSTATUS(status) == 1);
100c209e3e2SJohn Baldwin 
101c209e3e2SJohn Baldwin 	/* The child should no longer exist. */
102c209e3e2SJohn Baldwin 	wpid = waitpid(child, &status, 0);
103c209e3e2SJohn Baldwin 	ATF_REQUIRE(wpid == -1);
104c209e3e2SJohn Baldwin 	ATF_REQUIRE(errno == ECHILD);
105c209e3e2SJohn Baldwin }
106c209e3e2SJohn Baldwin 
107c209e3e2SJohn Baldwin /*
108c209e3e2SJohn Baldwin  * Verify that a parent debugger process "sees" the exit of a debugged
109c209e3e2SJohn Baldwin  * process exactly once when attached via PT_ATTACH.
110c209e3e2SJohn Baldwin  */
111c209e3e2SJohn Baldwin ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_attach);
112c209e3e2SJohn Baldwin ATF_TC_BODY(ptrace__parent_wait_after_attach, tc)
113c209e3e2SJohn Baldwin {
114c209e3e2SJohn Baldwin 	pid_t child, wpid;
115c209e3e2SJohn Baldwin 	int cpipe[2], status;
116c209e3e2SJohn Baldwin 	char c;
117c209e3e2SJohn Baldwin 
118c209e3e2SJohn Baldwin 	ATF_REQUIRE(pipe(cpipe) == 0);
119c209e3e2SJohn Baldwin 	ATF_REQUIRE((child = fork()) != -1);
120c209e3e2SJohn Baldwin 	if (child == 0) {
121c209e3e2SJohn Baldwin 		/* Child process. */
122c209e3e2SJohn Baldwin 		close(cpipe[0]);
123c209e3e2SJohn Baldwin 
124c209e3e2SJohn Baldwin 		/* Wait for the parent to attach. */
125*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == 0);
126c209e3e2SJohn Baldwin 
127c209e3e2SJohn Baldwin 		exit(1);
128c209e3e2SJohn Baldwin 	}
129c209e3e2SJohn Baldwin 	close(cpipe[1]);
130c209e3e2SJohn Baldwin 
131c209e3e2SJohn Baldwin 	/* Parent process. */
132c209e3e2SJohn Baldwin 
133c209e3e2SJohn Baldwin 	/* Attach to the child process. */
134c209e3e2SJohn Baldwin 	ATF_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) == 0);
135c209e3e2SJohn Baldwin 
136c209e3e2SJohn Baldwin 	/* The first wait() should report the SIGSTOP from PT_ATTACH. */
137c209e3e2SJohn Baldwin 	wpid = waitpid(child, &status, 0);
138c209e3e2SJohn Baldwin 	ATF_REQUIRE(wpid == child);
139c209e3e2SJohn Baldwin 	ATF_REQUIRE(WIFSTOPPED(status));
140c209e3e2SJohn Baldwin 	ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
141c209e3e2SJohn Baldwin 
142c209e3e2SJohn Baldwin 	/* Continue the child ignoring the SIGSTOP. */
143c209e3e2SJohn Baldwin 	ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
144c209e3e2SJohn Baldwin 
145c209e3e2SJohn Baldwin 	/* Signal the child to exit. */
146c209e3e2SJohn Baldwin 	close(cpipe[0]);
147c209e3e2SJohn Baldwin 
148c209e3e2SJohn Baldwin 	/* The second wait() should report the exit status. */
149c209e3e2SJohn Baldwin 	wpid = waitpid(child, &status, 0);
150c209e3e2SJohn Baldwin 	ATF_REQUIRE(wpid == child);
151c209e3e2SJohn Baldwin 	ATF_REQUIRE(WIFEXITED(status));
152c209e3e2SJohn Baldwin 	ATF_REQUIRE(WEXITSTATUS(status) == 1);
153c209e3e2SJohn Baldwin 
154c209e3e2SJohn Baldwin 	/* The child should no longer exist. */
155c209e3e2SJohn Baldwin 	wpid = waitpid(child, &status, 0);
156c209e3e2SJohn Baldwin 	ATF_REQUIRE(wpid == -1);
157c209e3e2SJohn Baldwin 	ATF_REQUIRE(errno == ECHILD);
158c209e3e2SJohn Baldwin }
159c209e3e2SJohn Baldwin 
16057c74f5bSJohn Baldwin /*
16157c74f5bSJohn Baldwin  * Verify that a parent process "sees" the exit of a debugged process only
16257c74f5bSJohn Baldwin  * after the debugger has seen it.
16357c74f5bSJohn Baldwin  */
16457c74f5bSJohn Baldwin ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_child_debugger);
16557c74f5bSJohn Baldwin ATF_TC_BODY(ptrace__parent_sees_exit_after_child_debugger, tc)
16657c74f5bSJohn Baldwin {
16757c74f5bSJohn Baldwin 	pid_t child, debugger, wpid;
16857c74f5bSJohn Baldwin 	int cpipe[2], dpipe[2], status;
16957c74f5bSJohn Baldwin 	char c;
17057c74f5bSJohn Baldwin 
17157c74f5bSJohn Baldwin 	ATF_REQUIRE(pipe(cpipe) == 0);
17257c74f5bSJohn Baldwin 	ATF_REQUIRE((child = fork()) != -1);
17357c74f5bSJohn Baldwin 
17457c74f5bSJohn Baldwin 	if (child == 0) {
17557c74f5bSJohn Baldwin 		/* Child process. */
17657c74f5bSJohn Baldwin 		close(cpipe[0]);
17757c74f5bSJohn Baldwin 
17857c74f5bSJohn Baldwin 		/* Wait for parent to be ready. */
179*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c));
18057c74f5bSJohn Baldwin 
18157c74f5bSJohn Baldwin 		exit(1);
18257c74f5bSJohn Baldwin 	}
18357c74f5bSJohn Baldwin 	close(cpipe[1]);
18457c74f5bSJohn Baldwin 
18557c74f5bSJohn Baldwin 	ATF_REQUIRE(pipe(dpipe) == 0);
18657c74f5bSJohn Baldwin 	ATF_REQUIRE((debugger = fork()) != -1);
18757c74f5bSJohn Baldwin 
18857c74f5bSJohn Baldwin 	if (debugger == 0) {
18957c74f5bSJohn Baldwin 		/* Debugger process. */
19057c74f5bSJohn Baldwin 		close(dpipe[0]);
19157c74f5bSJohn Baldwin 
192*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1);
19357c74f5bSJohn Baldwin 
19457c74f5bSJohn Baldwin 		wpid = waitpid(child, &status, 0);
195*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(wpid == child);
196*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(WIFSTOPPED(status));
197*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP);
19857c74f5bSJohn Baldwin 
199*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
20057c74f5bSJohn Baldwin 
20157c74f5bSJohn Baldwin 		/* Signal parent that debugger is attached. */
202*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c));
20357c74f5bSJohn Baldwin 
20457c74f5bSJohn Baldwin 		/* Wait for parent's failed wait. */
205*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(read(dpipe[1], &c, sizeof(c)) == 0);
20657c74f5bSJohn Baldwin 
20757c74f5bSJohn Baldwin 		wpid = waitpid(child, &status, 0);
208*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(wpid == child);
209*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(WIFEXITED(status));
210*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(WEXITSTATUS(status) == 1);
21157c74f5bSJohn Baldwin 
21257c74f5bSJohn Baldwin 		exit(0);
21357c74f5bSJohn Baldwin 	}
21457c74f5bSJohn Baldwin 	close(dpipe[1]);
21557c74f5bSJohn Baldwin 
21657c74f5bSJohn Baldwin 	/* Parent process. */
21757c74f5bSJohn Baldwin 
21857c74f5bSJohn Baldwin 	/* Wait for the debugger to attach to the child. */
21957c74f5bSJohn Baldwin 	ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c));
22057c74f5bSJohn Baldwin 
22157c74f5bSJohn Baldwin 	/* Release the child. */
22257c74f5bSJohn Baldwin 	ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c));
22357c74f5bSJohn Baldwin 	ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0);
22457c74f5bSJohn Baldwin 	close(cpipe[0]);
22557c74f5bSJohn Baldwin 
22657c74f5bSJohn Baldwin 	/*
22757c74f5bSJohn Baldwin 	 * Wait for the child to exit.  This is kind of gross, but
22857c74f5bSJohn Baldwin 	 * there is not a better way.
22957c74f5bSJohn Baldwin 	 */
23057c74f5bSJohn Baldwin 	for (;;) {
23157c74f5bSJohn Baldwin 		struct kinfo_proc kp;
23257c74f5bSJohn Baldwin 		size_t len;
23357c74f5bSJohn Baldwin 		int mib[4];
23457c74f5bSJohn Baldwin 
23557c74f5bSJohn Baldwin 		mib[0] = CTL_KERN;
23657c74f5bSJohn Baldwin 		mib[1] = KERN_PROC;
23757c74f5bSJohn Baldwin 		mib[2] = KERN_PROC_PID;
23857c74f5bSJohn Baldwin 		mib[3] = child;
23957c74f5bSJohn Baldwin 		len = sizeof(kp);
24057c74f5bSJohn Baldwin 		if (sysctl(mib, nitems(mib), &kp, &len, NULL, 0) == -1) {
24157c74f5bSJohn Baldwin 			/* The KERN_PROC_PID sysctl fails for zombies. */
24257c74f5bSJohn Baldwin 			ATF_REQUIRE(errno == ESRCH);
24357c74f5bSJohn Baldwin 			break;
24457c74f5bSJohn Baldwin 		}
24557c74f5bSJohn Baldwin 		usleep(5000);
24657c74f5bSJohn Baldwin 	}
24757c74f5bSJohn Baldwin 
24857c74f5bSJohn Baldwin 	/*
2492f021998SJohn Baldwin 	 * This wait should return a pid of 0 to indicate no status to
2502f021998SJohn Baldwin 	 * report.  The parent should see the child as non-exited
2512f021998SJohn Baldwin 	 * until the debugger sees the exit.
25257c74f5bSJohn Baldwin 	 */
25357c74f5bSJohn Baldwin 	wpid = waitpid(child, &status, WNOHANG);
25457c74f5bSJohn Baldwin 	ATF_REQUIRE(wpid == 0);
25557c74f5bSJohn Baldwin 
25657c74f5bSJohn Baldwin 	/* Signal the debugger to wait for the child. */
25757c74f5bSJohn Baldwin 	close(dpipe[0]);
25857c74f5bSJohn Baldwin 
25957c74f5bSJohn Baldwin 	/* Wait for the debugger. */
26057c74f5bSJohn Baldwin 	wpid = waitpid(debugger, &status, 0);
26157c74f5bSJohn Baldwin 	ATF_REQUIRE(wpid == debugger);
26257c74f5bSJohn Baldwin 	ATF_REQUIRE(WIFEXITED(status));
26357c74f5bSJohn Baldwin 	ATF_REQUIRE(WEXITSTATUS(status) == 0);
26457c74f5bSJohn Baldwin 
26557c74f5bSJohn Baldwin 	/* The child process should now be ready. */
26657c74f5bSJohn Baldwin 	wpid = waitpid(child, &status, WNOHANG);
26757c74f5bSJohn Baldwin 	ATF_REQUIRE(wpid == child);
26857c74f5bSJohn Baldwin 	ATF_REQUIRE(WIFEXITED(status));
26957c74f5bSJohn Baldwin 	ATF_REQUIRE(WEXITSTATUS(status) == 1);
27057c74f5bSJohn Baldwin }
27157c74f5bSJohn Baldwin 
27257c74f5bSJohn Baldwin /*
27357c74f5bSJohn Baldwin  * Verify that a parent process "sees" the exit of a debugged process
27457c74f5bSJohn Baldwin  * only after a non-direct-child debugger has seen it.  In particular,
27557c74f5bSJohn Baldwin  * various wait() calls in the parent must avoid failing with ESRCH by
27657c74f5bSJohn Baldwin  * checking the parent's orphan list for the debugee.
27757c74f5bSJohn Baldwin  */
27857c74f5bSJohn Baldwin ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_unrelated_debugger);
27957c74f5bSJohn Baldwin ATF_TC_BODY(ptrace__parent_sees_exit_after_unrelated_debugger, tc)
28057c74f5bSJohn Baldwin {
28157c74f5bSJohn Baldwin 	pid_t child, debugger, fpid, wpid;
28257c74f5bSJohn Baldwin 	int cpipe[2], dpipe[2], status;
28357c74f5bSJohn Baldwin 	char c;
28457c74f5bSJohn Baldwin 
28557c74f5bSJohn Baldwin 	ATF_REQUIRE(pipe(cpipe) == 0);
28657c74f5bSJohn Baldwin 	ATF_REQUIRE((child = fork()) != -1);
28757c74f5bSJohn Baldwin 
28857c74f5bSJohn Baldwin 	if (child == 0) {
28957c74f5bSJohn Baldwin 		/* Child process. */
29057c74f5bSJohn Baldwin 		close(cpipe[0]);
29157c74f5bSJohn Baldwin 
29257c74f5bSJohn Baldwin 		/* Wait for parent to be ready. */
293*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c));
29457c74f5bSJohn Baldwin 
29557c74f5bSJohn Baldwin 		exit(1);
29657c74f5bSJohn Baldwin 	}
29757c74f5bSJohn Baldwin 	close(cpipe[1]);
29857c74f5bSJohn Baldwin 
29957c74f5bSJohn Baldwin 	ATF_REQUIRE(pipe(dpipe) == 0);
30057c74f5bSJohn Baldwin 	ATF_REQUIRE((debugger = fork()) != -1);
30157c74f5bSJohn Baldwin 
30257c74f5bSJohn Baldwin 	if (debugger == 0) {
30357c74f5bSJohn Baldwin 		/* Debugger parent. */
30457c74f5bSJohn Baldwin 
30557c74f5bSJohn Baldwin 		/*
30657c74f5bSJohn Baldwin 		 * Fork again and drop the debugger parent so that the
30757c74f5bSJohn Baldwin 		 * debugger is not a child of the main parent.
30857c74f5bSJohn Baldwin 		 */
309*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE((fpid = fork()) != -1);
31057c74f5bSJohn Baldwin 		if (fpid != 0)
31157c74f5bSJohn Baldwin 			exit(2);
31257c74f5bSJohn Baldwin 
31357c74f5bSJohn Baldwin 		/* Debugger process. */
31457c74f5bSJohn Baldwin 		close(dpipe[0]);
31557c74f5bSJohn Baldwin 
316*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1);
31757c74f5bSJohn Baldwin 
31857c74f5bSJohn Baldwin 		wpid = waitpid(child, &status, 0);
319*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(wpid == child);
320*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(WIFSTOPPED(status));
321*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP);
32257c74f5bSJohn Baldwin 
323*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
32457c74f5bSJohn Baldwin 
32557c74f5bSJohn Baldwin 		/* Signal parent that debugger is attached. */
326*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c));
32757c74f5bSJohn Baldwin 
32857c74f5bSJohn Baldwin 		/* Wait for parent's failed wait. */
329*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(read(dpipe[1], &c, sizeof(c)) == sizeof(c));
33057c74f5bSJohn Baldwin 
33157c74f5bSJohn Baldwin 		wpid = waitpid(child, &status, 0);
332*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(wpid == child);
333*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(WIFEXITED(status));
334*dfa8ba12SJohn Baldwin 		CHILD_REQUIRE(WEXITSTATUS(status) == 1);
33557c74f5bSJohn Baldwin 
33657c74f5bSJohn Baldwin 		exit(0);
33757c74f5bSJohn Baldwin 	}
338eddb85c6SJohn Baldwin 	close(dpipe[1]);
33957c74f5bSJohn Baldwin 
34057c74f5bSJohn Baldwin 	/* Parent process. */
34157c74f5bSJohn Baldwin 
34257c74f5bSJohn Baldwin 	/* Wait for the debugger parent process to exit. */
34357c74f5bSJohn Baldwin 	wpid = waitpid(debugger, &status, 0);
34457c74f5bSJohn Baldwin 	ATF_REQUIRE(wpid == debugger);
34557c74f5bSJohn Baldwin 	ATF_REQUIRE(WIFEXITED(status));
34657c74f5bSJohn Baldwin 	ATF_REQUIRE(WEXITSTATUS(status) == 2);
34757c74f5bSJohn Baldwin 
34857c74f5bSJohn Baldwin 	/* A WNOHANG wait here should see the non-exited child. */
34957c74f5bSJohn Baldwin 	wpid = waitpid(child, &status, WNOHANG);
35057c74f5bSJohn Baldwin 	ATF_REQUIRE(wpid == 0);
35157c74f5bSJohn Baldwin 
35257c74f5bSJohn Baldwin 	/* Wait for the debugger to attach to the child. */
35357c74f5bSJohn Baldwin 	ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c));
35457c74f5bSJohn Baldwin 
35557c74f5bSJohn Baldwin 	/* Release the child. */
35657c74f5bSJohn Baldwin 	ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c));
35757c74f5bSJohn Baldwin 	ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0);
35857c74f5bSJohn Baldwin 	close(cpipe[0]);
35957c74f5bSJohn Baldwin 
36057c74f5bSJohn Baldwin 	/*
36157c74f5bSJohn Baldwin 	 * Wait for the child to exit.  This is kind of gross, but
36257c74f5bSJohn Baldwin 	 * there is not a better way.
36357c74f5bSJohn Baldwin 	 */
36457c74f5bSJohn Baldwin 	for (;;) {
36557c74f5bSJohn Baldwin 		struct kinfo_proc kp;
36657c74f5bSJohn Baldwin 		size_t len;
36757c74f5bSJohn Baldwin 		int mib[4];
36857c74f5bSJohn Baldwin 
36957c74f5bSJohn Baldwin 		mib[0] = CTL_KERN;
37057c74f5bSJohn Baldwin 		mib[1] = KERN_PROC;
37157c74f5bSJohn Baldwin 		mib[2] = KERN_PROC_PID;
37257c74f5bSJohn Baldwin 		mib[3] = child;
37357c74f5bSJohn Baldwin 		len = sizeof(kp);
37457c74f5bSJohn Baldwin 		if (sysctl(mib, nitems(mib), &kp, &len, NULL, 0) == -1) {
37557c74f5bSJohn Baldwin 			/* The KERN_PROC_PID sysctl fails for zombies. */
37657c74f5bSJohn Baldwin 			ATF_REQUIRE(errno == ESRCH);
37757c74f5bSJohn Baldwin 			break;
37857c74f5bSJohn Baldwin 		}
37957c74f5bSJohn Baldwin 		usleep(5000);
38057c74f5bSJohn Baldwin 	}
38157c74f5bSJohn Baldwin 
38257c74f5bSJohn Baldwin 	/*
3832f021998SJohn Baldwin 	 * This wait should return a pid of 0 to indicate no status to
3842f021998SJohn Baldwin 	 * report.  The parent should see the child as non-exited
3852f021998SJohn Baldwin 	 * until the debugger sees the exit.
38657c74f5bSJohn Baldwin 	 */
38757c74f5bSJohn Baldwin 	wpid = waitpid(child, &status, WNOHANG);
38857c74f5bSJohn Baldwin 	ATF_REQUIRE(wpid == 0);
38957c74f5bSJohn Baldwin 
39057c74f5bSJohn Baldwin 	/* Signal the debugger to wait for the child. */
391eddb85c6SJohn Baldwin 	ATF_REQUIRE(write(dpipe[0], &c, sizeof(c)) == sizeof(c));
39257c74f5bSJohn Baldwin 
39357c74f5bSJohn Baldwin 	/* Wait for the debugger. */
394eddb85c6SJohn Baldwin 	ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == 0);
395eddb85c6SJohn Baldwin 	close(dpipe[0]);
39657c74f5bSJohn Baldwin 
39757c74f5bSJohn Baldwin 	/* The child process should now be ready. */
39857c74f5bSJohn Baldwin 	wpid = waitpid(child, &status, WNOHANG);
39957c74f5bSJohn Baldwin 	ATF_REQUIRE(wpid == child);
40057c74f5bSJohn Baldwin 	ATF_REQUIRE(WIFEXITED(status));
40157c74f5bSJohn Baldwin 	ATF_REQUIRE(WEXITSTATUS(status) == 1);
40257c74f5bSJohn Baldwin }
40357c74f5bSJohn Baldwin 
404c209e3e2SJohn Baldwin ATF_TP_ADD_TCS(tp)
405c209e3e2SJohn Baldwin {
406c209e3e2SJohn Baldwin 
407c209e3e2SJohn Baldwin 	ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_trace_me);
408c209e3e2SJohn Baldwin 	ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_attach);
40957c74f5bSJohn Baldwin 	ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_child_debugger);
41057c74f5bSJohn Baldwin 	ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_unrelated_debugger);
411c209e3e2SJohn Baldwin 
412c209e3e2SJohn Baldwin 	return (atf_no_error());
413c209e3e2SJohn Baldwin }
414