xref: /netbsd-src/tests/lib/libc/sys/t_ptrace_topology_wait.h (revision 29ff229651f9900c7fcaff2e5578e0160e8a0e6e)
1 /*	$NetBSD: t_ptrace_topology_wait.h,v 1.1 2020/05/05 00:33:37 kamil Exp $	*/
2 
3 /*-
4  * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 
30 ATF_TC(traceme_pid1_parent);
ATF_TC_HEAD(traceme_pid1_parent,tc)31 ATF_TC_HEAD(traceme_pid1_parent, tc)
32 {
33 	atf_tc_set_md_var(tc, "descr",
34 	    "Verify that PT_TRACE_ME is not allowed when our parent is PID1");
35 }
36 
ATF_TC_BODY(traceme_pid1_parent,tc)37 ATF_TC_BODY(traceme_pid1_parent, tc)
38 {
39 	struct msg_fds parent_child;
40 	int exitval_child1 = 1, exitval_child2 = 2;
41 	pid_t child1, child2, wpid;
42 	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
43 #if defined(TWAIT_HAVE_STATUS)
44 	int status;
45 #endif
46 
47 	SYSCALL_REQUIRE(msg_open(&parent_child) == 0);
48 
49 	DPRINTF("Before forking process PID=%d\n", getpid());
50 	SYSCALL_REQUIRE((child1 = fork()) != -1);
51 	if (child1 == 0) {
52 		DPRINTF("Before forking process PID=%d\n", getpid());
53 		SYSCALL_REQUIRE((child2 = fork()) != -1);
54 		if (child2 != 0) {
55 			DPRINTF("Parent process PID=%d, child2's PID=%d\n",
56 			    getpid(), child2);
57 			_exit(exitval_child1);
58 		}
59 		CHILD_FROM_PARENT("exit child1", parent_child, msg);
60 
61 		DPRINTF("Assert that our parent is PID1 (initproc)\n");
62 		FORKEE_ASSERT_EQ(getppid(), 1);
63 
64 		DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
65 		FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) == -1);
66 		SYSCALL_REQUIRE_ERRNO(errno, EPERM);
67 
68 		CHILD_TO_PARENT("child2 exiting", parent_child, msg);
69 
70 		_exit(exitval_child2);
71 	}
72 	DPRINTF("Parent process PID=%d, child1's PID=%d\n", getpid(), child1);
73 
74 	DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
75 	TWAIT_REQUIRE_SUCCESS(
76 	    wpid = TWAIT_GENERIC(child1, &status, WEXITED), child1);
77 
78 	validate_status_exited(status, exitval_child1);
79 
80 	DPRINTF("Notify that child1 is dead\n");
81 	PARENT_TO_CHILD("exit child1", parent_child, msg);
82 
83 	DPRINTF("Wait for exiting of child2\n");
84 	PARENT_FROM_CHILD("child2 exiting", parent_child, msg);
85 }
86 
87 /// ----------------------------------------------------------------------------
88 
89 #if defined(TWAIT_HAVE_PID)
90 static void
tracer_sees_terminaton_before_the_parent_raw(bool notimeout,bool unrelated,bool stopped)91 tracer_sees_terminaton_before_the_parent_raw(bool notimeout, bool unrelated,
92                                              bool stopped)
93 {
94 	/*
95 	 * notimeout - disable timeout in await zombie function
96 	 * unrelated - attach from unrelated tracer reparented to initproc
97 	 * stopped - attach to a stopped process
98 	 */
99 
100 	struct msg_fds parent_tracee, parent_tracer;
101 	const int exitval_tracee = 5;
102 	const int exitval_tracer = 10;
103 	pid_t tracee, tracer, wpid;
104 	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
105 #if defined(TWAIT_HAVE_STATUS)
106 	int status;
107 #endif
108 
109 	/*
110 	 * Only a subset of options are supported.
111 	 */
112 	ATF_REQUIRE((!notimeout && !unrelated && !stopped) ||
113 	            (!notimeout && unrelated && !stopped) ||
114 	            (notimeout && !unrelated && !stopped) ||
115 	            (!notimeout && unrelated && stopped));
116 
117 	DPRINTF("Spawn tracee\n");
118 	SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
119 	tracee = atf_utils_fork();
120 	if (tracee == 0) {
121 		if (stopped) {
122 			DPRINTF("Stop self PID %d\n", getpid());
123 			raise(SIGSTOP);
124 		}
125 
126 		// Wait for parent to let us exit
127 		CHILD_FROM_PARENT("exit tracee", parent_tracee, msg);
128 		_exit(exitval_tracee);
129 	}
130 
131 	DPRINTF("Spawn debugger\n");
132 	SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0);
133 	tracer = atf_utils_fork();
134 	if (tracer == 0) {
135 		if(unrelated) {
136 			/* Fork again and drop parent to reattach to PID 1 */
137 			tracer = atf_utils_fork();
138 			if (tracer != 0)
139 				_exit(exitval_tracer);
140 		}
141 
142 		if (stopped) {
143 			DPRINTF("Await for a stopped parent PID %d\n", tracee);
144 			await_stopped(tracee);
145 		}
146 
147 		DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid());
148 		FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
149 
150 		/* Wait for tracee and assert that it was stopped w/ SIGSTOP */
151 		FORKEE_REQUIRE_SUCCESS(
152 		    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
153 
154 		forkee_status_stopped(status, SIGSTOP);
155 
156 		/* Resume tracee with PT_CONTINUE */
157 		FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
158 
159 		/* Inform parent that tracer has attached to tracee */
160 		CHILD_TO_PARENT("tracer ready", parent_tracer, msg);
161 
162 		/* Wait for parent to tell use that tracee should have exited */
163 		CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg);
164 
165 		/* Wait for tracee and assert that it exited */
166 		FORKEE_REQUIRE_SUCCESS(
167 		    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
168 
169 		forkee_status_exited(status, exitval_tracee);
170 		DPRINTF("Tracee %d exited with %d\n", tracee, exitval_tracee);
171 
172 		DPRINTF("Before exiting of the tracer process\n");
173 		_exit(unrelated ? 0 /* collect by initproc */ : exitval_tracer);
174 	}
175 
176 	if (unrelated) {
177 		DPRINTF("Wait for the tracer process (direct child) to exit "
178 		    "calling %s()\n", TWAIT_FNAME);
179 		TWAIT_REQUIRE_SUCCESS(
180 		    wpid = TWAIT_GENERIC(tracer, &status, 0), tracer);
181 
182 		validate_status_exited(status, exitval_tracer);
183 
184 		DPRINTF("Wait for the non-exited tracee process with %s()\n",
185 		    TWAIT_FNAME);
186 		TWAIT_REQUIRE_SUCCESS(
187 		    wpid = TWAIT_GENERIC(tracee, NULL, WNOHANG), 0);
188 	}
189 
190 	DPRINTF("Wait for the tracer to attach to the tracee\n");
191 	PARENT_FROM_CHILD("tracer ready", parent_tracer, msg);
192 
193 	DPRINTF("Resume the tracee and let it exit\n");
194 	PARENT_TO_CHILD("exit tracee", parent_tracee,  msg);
195 
196 	DPRINTF("Detect that tracee is zombie\n");
197 	if (notimeout)
198 		await_zombie_raw(tracee, 0);
199 	else
200 		await_zombie(tracee);
201 
202 	DPRINTF("Assert that there is no status about tracee %d - "
203 	    "Tracer must detect zombie first - calling %s()\n", tracee,
204 	    TWAIT_FNAME);
205 	TWAIT_REQUIRE_SUCCESS(
206 	    wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
207 
208 	if (unrelated) {
209 		DPRINTF("Resume the tracer and let it detect exited tracee\n");
210 		PARENT_TO_CHILD("Message 2", parent_tracer, msg);
211 	} else {
212 		DPRINTF("Tell the tracer child should have exited\n");
213 		PARENT_TO_CHILD("wait for tracee exit", parent_tracer,  msg);
214 		DPRINTF("Wait for tracer to finish its job and exit - calling "
215 			"%s()\n", TWAIT_FNAME);
216 
217 		DPRINTF("Wait from tracer child to complete waiting for "
218 			"tracee\n");
219 		TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
220 		    tracer);
221 
222 		validate_status_exited(status, exitval_tracer);
223 	}
224 
225 	DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n",
226 	    TWAIT_FNAME);
227 	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
228 
229 	validate_status_exited(status, exitval_tracee);
230 
231 	msg_close(&parent_tracer);
232 	msg_close(&parent_tracee);
233 }
234 
235 ATF_TC(tracer_sees_terminaton_before_the_parent);
ATF_TC_HEAD(tracer_sees_terminaton_before_the_parent,tc)236 ATF_TC_HEAD(tracer_sees_terminaton_before_the_parent, tc)
237 {
238 	atf_tc_set_md_var(tc, "descr",
239 	    "Assert that tracer sees process termination before the parent");
240 }
241 
ATF_TC_BODY(tracer_sees_terminaton_before_the_parent,tc)242 ATF_TC_BODY(tracer_sees_terminaton_before_the_parent, tc)
243 {
244 
245 	tracer_sees_terminaton_before_the_parent_raw(false, false, false);
246 }
247 
248 ATF_TC(tracer_sysctl_lookup_without_duplicates);
ATF_TC_HEAD(tracer_sysctl_lookup_without_duplicates,tc)249 ATF_TC_HEAD(tracer_sysctl_lookup_without_duplicates, tc)
250 {
251 	atf_tc_set_md_var(tc, "timeout", "15");
252 	atf_tc_set_md_var(tc, "descr",
253 	    "Assert that await_zombie() in attach1 always finds a single "
254 	    "process and no other error is reported");
255 }
256 
ATF_TC_BODY(tracer_sysctl_lookup_without_duplicates,tc)257 ATF_TC_BODY(tracer_sysctl_lookup_without_duplicates, tc)
258 {
259 	time_t start, end;
260 	double diff;
261 	unsigned long N = 0;
262 
263 	/*
264 	 * Reuse this test with tracer_sees_terminaton_before_the_parent_raw().
265 	 * This test body isn't specific to this race, however it's just good
266 	 * enough for this purposes, no need to invent a dedicated code flow.
267 	 */
268 
269 	start = time(NULL);
270 	while (true) {
271 		DPRINTF("Step: %lu\n", N);
272 		tracer_sees_terminaton_before_the_parent_raw(true, false,
273 		                                             false);
274 		end = time(NULL);
275 		diff = difftime(end, start);
276 		if (diff >= 5.0)
277 			break;
278 		++N;
279 	}
280 	DPRINTF("Iterations: %lu\n", N);
281 }
282 
283 ATF_TC(unrelated_tracer_sees_terminaton_before_the_parent);
ATF_TC_HEAD(unrelated_tracer_sees_terminaton_before_the_parent,tc)284 ATF_TC_HEAD(unrelated_tracer_sees_terminaton_before_the_parent, tc)
285 {
286 	atf_tc_set_md_var(tc, "descr",
287 	    "Assert that tracer sees process termination before the parent");
288 }
289 
ATF_TC_BODY(unrelated_tracer_sees_terminaton_before_the_parent,tc)290 ATF_TC_BODY(unrelated_tracer_sees_terminaton_before_the_parent, tc)
291 {
292 
293 	tracer_sees_terminaton_before_the_parent_raw(false, true, false);
294 }
295 
296 ATF_TC(tracer_attach_to_unrelated_stopped_process);
ATF_TC_HEAD(tracer_attach_to_unrelated_stopped_process,tc)297 ATF_TC_HEAD(tracer_attach_to_unrelated_stopped_process, tc)
298 {
299 	atf_tc_set_md_var(tc, "descr",
300 	    "Assert that tracer can attach to an unrelated stopped process");
301 }
302 
ATF_TC_BODY(tracer_attach_to_unrelated_stopped_process,tc)303 ATF_TC_BODY(tracer_attach_to_unrelated_stopped_process, tc)
304 {
305 
306 	tracer_sees_terminaton_before_the_parent_raw(false, true, true);
307 }
308 #endif
309 
310 /// ----------------------------------------------------------------------------
311 
312 static void
parent_attach_to_its_child(bool stopped)313 parent_attach_to_its_child(bool stopped)
314 {
315 	struct msg_fds parent_tracee;
316 	const int exitval_tracee = 5;
317 	pid_t tracee, wpid;
318 	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
319 #if defined(TWAIT_HAVE_STATUS)
320 	int status;
321 #endif
322 
323 	DPRINTF("Spawn tracee\n");
324 	SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
325 	tracee = atf_utils_fork();
326 	if (tracee == 0) {
327 		CHILD_FROM_PARENT("Message 1", parent_tracee, msg);
328 		DPRINTF("Parent should now attach to tracee\n");
329 
330 		if (stopped) {
331 			DPRINTF("Stop self PID %d\n", getpid());
332 			SYSCALL_REQUIRE(raise(SIGSTOP) != -1);
333 		}
334 
335 		CHILD_FROM_PARENT("Message 2", parent_tracee, msg);
336 		/* Wait for message from the parent */
337 		_exit(exitval_tracee);
338 	}
339 	PARENT_TO_CHILD("Message 1", parent_tracee, msg);
340 
341 	if (stopped) {
342 		DPRINTF("Await for a stopped tracee PID %d\n", tracee);
343 		await_stopped(tracee);
344 	}
345 
346 	DPRINTF("Before calling PT_ATTACH for tracee %d\n", tracee);
347 	SYSCALL_REQUIRE(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
348 
349 	DPRINTF("Wait for the stopped tracee process with %s()\n",
350 	    TWAIT_FNAME);
351 	TWAIT_REQUIRE_SUCCESS(
352 	    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
353 
354 	validate_status_stopped(status, SIGSTOP);
355 
356 	DPRINTF("Resume tracee with PT_CONTINUE\n");
357 	SYSCALL_REQUIRE(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
358 
359 	DPRINTF("Let the tracee exit now\n");
360 	PARENT_TO_CHILD("Message 2", parent_tracee, msg);
361 
362 	DPRINTF("Wait for tracee to exit with %s()\n", TWAIT_FNAME);
363 	TWAIT_REQUIRE_SUCCESS(
364 	    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
365 
366 	validate_status_exited(status, exitval_tracee);
367 
368 	DPRINTF("Before calling %s() for tracee\n", TWAIT_FNAME);
369 	TWAIT_REQUIRE_FAILURE(ECHILD,
370 	    wpid = TWAIT_GENERIC(tracee, &status, 0));
371 
372 	msg_close(&parent_tracee);
373 }
374 
375 ATF_TC(parent_attach_to_its_child);
ATF_TC_HEAD(parent_attach_to_its_child,tc)376 ATF_TC_HEAD(parent_attach_to_its_child, tc)
377 {
378 	atf_tc_set_md_var(tc, "descr",
379 	    "Assert that tracer parent can PT_ATTACH to its child");
380 }
381 
ATF_TC_BODY(parent_attach_to_its_child,tc)382 ATF_TC_BODY(parent_attach_to_its_child, tc)
383 {
384 
385 	parent_attach_to_its_child(false);
386 }
387 
388 ATF_TC(parent_attach_to_its_stopped_child);
ATF_TC_HEAD(parent_attach_to_its_stopped_child,tc)389 ATF_TC_HEAD(parent_attach_to_its_stopped_child, tc)
390 {
391 	atf_tc_set_md_var(tc, "descr",
392 	    "Assert that tracer parent can PT_ATTACH to its stopped child");
393 }
394 
ATF_TC_BODY(parent_attach_to_its_stopped_child,tc)395 ATF_TC_BODY(parent_attach_to_its_stopped_child, tc)
396 {
397 
398 	parent_attach_to_its_child(true);
399 }
400 
401 /// ----------------------------------------------------------------------------
402 
403 static void
child_attach_to_its_parent(bool stopped)404 child_attach_to_its_parent(bool stopped)
405 {
406 	struct msg_fds parent_tracee;
407 	const int exitval_tracer = 5;
408 	pid_t tracer, wpid;
409 	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
410 #if defined(TWAIT_HAVE_STATUS)
411 	int status;
412 #endif
413 
414 	DPRINTF("Spawn tracer\n");
415 	SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
416 	tracer = atf_utils_fork();
417 	if (tracer == 0) {
418 		/* Wait for message from the parent */
419 		CHILD_FROM_PARENT("Message 1", parent_tracee, msg);
420 
421 		if (stopped) {
422 			DPRINTF("Await for a stopped parent PID %d\n",
423 			        getppid());
424 			await_stopped(getppid());
425 		}
426 
427 		DPRINTF("Attach to parent PID %d with PT_ATTACH from child\n",
428 		    getppid());
429 		FORKEE_ASSERT(ptrace(PT_ATTACH, getppid(), NULL, 0) != -1);
430 
431 		DPRINTF("Wait for the stopped parent process with %s()\n",
432 		    TWAIT_FNAME);
433 		FORKEE_REQUIRE_SUCCESS(
434 		    wpid = TWAIT_GENERIC(getppid(), &status, 0), getppid());
435 
436 		forkee_status_stopped(status, SIGSTOP);
437 
438 		DPRINTF("Resume parent with PT_DETACH\n");
439 		FORKEE_ASSERT(ptrace(PT_DETACH, getppid(), (void *)1, 0)
440 		    != -1);
441 
442 		/* Tell parent we are ready */
443 		CHILD_TO_PARENT("Message 1", parent_tracee, msg);
444 
445 		_exit(exitval_tracer);
446 	}
447 
448 	DPRINTF("Wait for the tracer to become ready\n");
449 	PARENT_TO_CHILD("Message 1", parent_tracee, msg);
450 
451 	if (stopped) {
452 		DPRINTF("Stop self PID %d\n", getpid());
453 		SYSCALL_REQUIRE(raise(SIGSTOP) != -1);
454 	}
455 
456 	DPRINTF("Allow the tracer to exit now\n");
457 	PARENT_FROM_CHILD("Message 1", parent_tracee, msg);
458 
459 	DPRINTF("Wait for tracer to exit with %s()\n", TWAIT_FNAME);
460 	TWAIT_REQUIRE_SUCCESS(
461 	    wpid = TWAIT_GENERIC(tracer, &status, 0), tracer);
462 
463 	validate_status_exited(status, exitval_tracer);
464 
465 	DPRINTF("Before calling %s() for tracer\n", TWAIT_FNAME);
466 	TWAIT_REQUIRE_FAILURE(ECHILD,
467 	    wpid = TWAIT_GENERIC(tracer, &status, 0));
468 
469 	msg_close(&parent_tracee);
470 }
471 
472 ATF_TC(child_attach_to_its_parent);
ATF_TC_HEAD(child_attach_to_its_parent,tc)473 ATF_TC_HEAD(child_attach_to_its_parent, tc)
474 {
475 	atf_tc_set_md_var(tc, "descr",
476 	    "Assert that tracer child can PT_ATTACH to its parent");
477 }
478 
ATF_TC_BODY(child_attach_to_its_parent,tc)479 ATF_TC_BODY(child_attach_to_its_parent, tc)
480 {
481 
482 	child_attach_to_its_parent(false);
483 }
484 
485 ATF_TC(child_attach_to_its_stopped_parent);
ATF_TC_HEAD(child_attach_to_its_stopped_parent,tc)486 ATF_TC_HEAD(child_attach_to_its_stopped_parent, tc)
487 {
488 	atf_tc_set_md_var(tc, "descr",
489 	    "Assert that tracer child can PT_ATTACH to its stopped parent");
490 }
491 
ATF_TC_BODY(child_attach_to_its_stopped_parent,tc)492 ATF_TC_BODY(child_attach_to_its_stopped_parent, tc)
493 {
494 	/*
495 	 * The ATF framework (atf-run) does not tolerate raise(SIGSTOP), as
496 	 * this causes a pipe (established from atf-run) to be broken.
497 	 * atf-run uses this mechanism to monitor whether a test is alive.
498 	 *
499 	 * As a workaround spawn this test as a subprocess.
500 	 */
501 
502 	const int exitval = 15;
503 	pid_t child, wpid;
504 #if defined(TWAIT_HAVE_STATUS)
505 	int status;
506 #endif
507 
508 	SYSCALL_REQUIRE((child = fork()) != -1);
509 	if (child == 0) {
510 		child_attach_to_its_parent(true);
511 		_exit(exitval);
512 	} else {
513 		DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
514 		TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
515 
516 		validate_status_exited(status, exitval);
517 
518 		DPRINTF("Before calling %s() for the exited child\n", TWAIT_FNAME);
519 		TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
520 	}
521 }
522 
523 /// ----------------------------------------------------------------------------
524 
525 #if defined(TWAIT_HAVE_PID)
526 
527 enum tracee_sees_its_original_parent_type {
528 	TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID,
529 	TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2,
530 	TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS
531 };
532 
533 static void
tracee_sees_its_original_parent(enum tracee_sees_its_original_parent_type type)534 tracee_sees_its_original_parent(enum tracee_sees_its_original_parent_type type)
535 {
536 	struct msg_fds parent_tracer, parent_tracee;
537 	const int exitval_tracee = 5;
538 	const int exitval_tracer = 10;
539 	pid_t parent, tracee, tracer, wpid;
540 	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
541 #if defined(TWAIT_HAVE_STATUS)
542 	int status;
543 #endif
544 	/* sysctl(3) - kinfo_proc2 */
545 	int name[CTL_MAXNAME];
546 	struct kinfo_proc2 kp;
547 	size_t len = sizeof(kp);
548 	unsigned int namelen;
549 
550 	/* procfs - status  */
551 	FILE *fp;
552 	struct stat st;
553 	const char *fname = "/proc/curproc/status";
554 	char s_executable[MAXPATHLEN];
555 	int s_pid, s_ppid;
556 	int rv;
557 
558 	if (type == TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS) {
559 		SYSCALL_REQUIRE(
560 		    (rv = stat(fname, &st)) == 0 || (errno == ENOENT));
561 		if (rv != 0)
562 			atf_tc_skip("/proc/curproc/status not found");
563 	}
564 
565 	DPRINTF("Spawn tracee\n");
566 	SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0);
567 	SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
568 	tracee = atf_utils_fork();
569 	if (tracee == 0) {
570 		parent = getppid();
571 
572 		/* Emit message to the parent */
573 		CHILD_TO_PARENT("tracee ready", parent_tracee, msg);
574 		CHILD_FROM_PARENT("exit tracee", parent_tracee, msg);
575 
576 		switch (type) {
577 		case TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID:
578 			FORKEE_ASSERT_EQ(parent, getppid());
579 			break;
580 		case TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2:
581 			namelen = 0;
582 			name[namelen++] = CTL_KERN;
583 			name[namelen++] = KERN_PROC2;
584 			name[namelen++] = KERN_PROC_PID;
585 			name[namelen++] = getpid();
586 			name[namelen++] = len;
587 			name[namelen++] = 1;
588 
589 			FORKEE_ASSERT_EQ(
590 			    sysctl(name, namelen, &kp, &len, NULL, 0), 0);
591 			FORKEE_ASSERT_EQ(parent, kp.p_ppid);
592 			break;
593 		case TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS:
594 			/*
595 			 * Format:
596 			 *  EXECUTABLE PID PPID ...
597 			 */
598 			FORKEE_ASSERT((fp = fopen(fname, "r")) != NULL);
599 			fscanf(fp, "%s %d %d", s_executable, &s_pid, &s_ppid);
600 			FORKEE_ASSERT_EQ(fclose(fp), 0);
601 			FORKEE_ASSERT_EQ(parent, s_ppid);
602 			break;
603 		}
604 
605 		_exit(exitval_tracee);
606 	}
607 	DPRINTF("Wait for child to record its parent identifier (pid)\n");
608 	PARENT_FROM_CHILD("tracee ready", parent_tracee, msg);
609 
610 	DPRINTF("Spawn debugger\n");
611 	tracer = atf_utils_fork();
612 	if (tracer == 0) {
613 		/* No IPC to communicate with the child */
614 		DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid());
615 		FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
616 
617 		/* Wait for tracee and assert that it was stopped w/ SIGSTOP */
618 		FORKEE_REQUIRE_SUCCESS(
619 		    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
620 
621 		forkee_status_stopped(status, SIGSTOP);
622 
623 		/* Resume tracee with PT_CONTINUE */
624 		FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
625 
626 		/* Inform parent that tracer has attached to tracee */
627 		CHILD_TO_PARENT("tracer ready", parent_tracer, msg);
628 
629 		/* Wait for parent to tell use that tracee should have exited */
630 		CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg);
631 
632 		/* Wait for tracee and assert that it exited */
633 		FORKEE_REQUIRE_SUCCESS(
634 		    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
635 
636 		forkee_status_exited(status, exitval_tracee);
637 
638 		DPRINTF("Before exiting of the tracer process\n");
639 		_exit(exitval_tracer);
640 	}
641 
642 	DPRINTF("Wait for the tracer to attach to the tracee\n");
643 	PARENT_FROM_CHILD("tracer ready",  parent_tracer, msg);
644 
645 	DPRINTF("Resume the tracee and let it exit\n");
646 	PARENT_TO_CHILD("exit tracee",  parent_tracee, msg);
647 
648 	DPRINTF("Detect that tracee is zombie\n");
649 	await_zombie(tracee);
650 
651 	DPRINTF("Assert that there is no status about tracee - "
652 	    "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME);
653 	TWAIT_REQUIRE_SUCCESS(
654 	    wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
655 
656 	DPRINTF("Tell the tracer child should have exited\n");
657 	PARENT_TO_CHILD("wait for tracee exit",  parent_tracer, msg);
658 
659 	DPRINTF("Wait from tracer child to complete waiting for tracee\n");
660 	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
661 	    tracer);
662 
663 	validate_status_exited(status, exitval_tracer);
664 
665 	DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n",
666 	    TWAIT_FNAME);
667 	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG),
668 	    tracee);
669 
670 	validate_status_exited(status, exitval_tracee);
671 
672 	msg_close(&parent_tracer);
673 	msg_close(&parent_tracee);
674 }
675 
676 #define TRACEE_SEES_ITS_ORIGINAL_PARENT(test, type, descr)		\
677 ATF_TC(test);								\
678 ATF_TC_HEAD(test, tc)							\
679 {									\
680 	atf_tc_set_md_var(tc, "descr",					\
681 	    "Assert that tracee sees its original parent when being traced " \
682 	    "(check " descr ")");					\
683 }									\
684 									\
685 ATF_TC_BODY(test, tc)							\
686 {									\
687 									\
688 	tracee_sees_its_original_parent(type);				\
689 }
690 
691 TRACEE_SEES_ITS_ORIGINAL_PARENT(
692 	tracee_sees_its_original_parent_getppid,
693 	TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID,
694 	"getppid(2)");
695 TRACEE_SEES_ITS_ORIGINAL_PARENT(
696 	tracee_sees_its_original_parent_sysctl_kinfo_proc2,
697 	TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2,
698 	"sysctl(3) and kinfo_proc2");
699 TRACEE_SEES_ITS_ORIGINAL_PARENT(
700 	tracee_sees_its_original_parent_procfs_status,
701 	TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS,
702 	"the status file in procfs");
703 #endif
704 
705 #define ATF_TP_ADD_TCS_PTRACE_WAIT_TOPOLOGY() \
706 	ATF_TP_ADD_TC(tp, traceme_pid1_parent); \
707 	ATF_TP_ADD_TC_HAVE_PID(tp, tracer_sees_terminaton_before_the_parent); \
708 	ATF_TP_ADD_TC_HAVE_PID(tp, tracer_sysctl_lookup_without_duplicates); \
709 	ATF_TP_ADD_TC_HAVE_PID(tp, \
710 		unrelated_tracer_sees_terminaton_before_the_parent); \
711 	ATF_TP_ADD_TC_HAVE_PID(tp, tracer_attach_to_unrelated_stopped_process); \
712 	ATF_TP_ADD_TC(tp, parent_attach_to_its_child); \
713 	ATF_TP_ADD_TC(tp, parent_attach_to_its_stopped_child); \
714 	ATF_TP_ADD_TC(tp, child_attach_to_its_parent); \
715 	ATF_TP_ADD_TC(tp, child_attach_to_its_stopped_parent); \
716 	ATF_TP_ADD_TC_HAVE_PID(tp, \
717 		tracee_sees_its_original_parent_getppid); \
718 	ATF_TP_ADD_TC_HAVE_PID(tp, \
719 		tracee_sees_its_original_parent_sysctl_kinfo_proc2); \
720 	ATF_TP_ADD_TC_HAVE_PID(tp, \
721 		tracee_sees_its_original_parent_procfs_status);
722