Home | History | Annotate | Line # | Download | only in sys
t_ptrace_topology_wait.h revision 1.1
      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);
     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 
     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
     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);
    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 
    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);
    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 
    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);
    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 
    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);
    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 
    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
    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);
    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 
    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);
    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 
    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
    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);
    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 
    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);
    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 
    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
    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