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