Module Name:    src
Committed By:   kamil
Date:           Tue May  5 00:33:37 UTC 2020

Modified Files:
        src/tests/lib/libc/sys: t_ptrace_wait.c
Added Files:
        src/tests/lib/libc/sys: t_ptrace_topology_wait.h

Log Message:
Move topology tests out of t_ptrace_wait.c to t_ptrace_topology_wait.h

The same tests are now included with the preprocessor in t_ptrace_wait.c.

No functional change intended.


To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/tests/lib/libc/sys/t_ptrace_topology_wait.h
cvs rdiff -u -r1.186 -r1.187 src/tests/lib/libc/sys/t_ptrace_wait.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/tests/lib/libc/sys/t_ptrace_wait.c
diff -u src/tests/lib/libc/sys/t_ptrace_wait.c:1.186 src/tests/lib/libc/sys/t_ptrace_wait.c:1.187
--- src/tests/lib/libc/sys/t_ptrace_wait.c:1.186	Tue May  5 00:23:12 2020
+++ src/tests/lib/libc/sys/t_ptrace_wait.c	Tue May  5 00:33:37 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: t_ptrace_wait.c,v 1.186 2020/05/05 00:23:12 kamil Exp $	*/
+/*	$NetBSD: t_ptrace_wait.c,v 1.187 2020/05/05 00:33:37 kamil Exp $	*/
 
 /*-
  * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: t_ptrace_wait.c,v 1.186 2020/05/05 00:23:12 kamil Exp $");
+__RCSID("$NetBSD: t_ptrace_wait.c,v 1.187 2020/05/05 00:33:37 kamil Exp $");
 
 #define __LEGACY_PT_LWPINFO
 
@@ -103,683 +103,6 @@ static int debug = 0;
 
 /// ----------------------------------------------------------------------------
 
-ATF_TC(traceme_pid1_parent);
-ATF_TC_HEAD(traceme_pid1_parent, tc)
-{
-	atf_tc_set_md_var(tc, "descr",
-	    "Verify that PT_TRACE_ME is not allowed when our parent is PID1");
-}
-
-ATF_TC_BODY(traceme_pid1_parent, tc)
-{
-	struct msg_fds parent_child;
-	int exitval_child1 = 1, exitval_child2 = 2;
-	pid_t child1, child2, wpid;
-	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
-#if defined(TWAIT_HAVE_STATUS)
-	int status;
-#endif
-
-	SYSCALL_REQUIRE(msg_open(&parent_child) == 0);
-
-	DPRINTF("Before forking process PID=%d\n", getpid());
-	SYSCALL_REQUIRE((child1 = fork()) != -1);
-	if (child1 == 0) {
-		DPRINTF("Before forking process PID=%d\n", getpid());
-		SYSCALL_REQUIRE((child2 = fork()) != -1);
-		if (child2 != 0) {
-			DPRINTF("Parent process PID=%d, child2's PID=%d\n",
-			    getpid(), child2);
-			_exit(exitval_child1);
-		}
-		CHILD_FROM_PARENT("exit child1", parent_child, msg);
-
-		DPRINTF("Assert that our parent is PID1 (initproc)\n");
-		FORKEE_ASSERT_EQ(getppid(), 1);
-
-		DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
-		FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) == -1);
-		SYSCALL_REQUIRE_ERRNO(errno, EPERM);
-
-		CHILD_TO_PARENT("child2 exiting", parent_child, msg);
-
-		_exit(exitval_child2);
-	}
-	DPRINTF("Parent process PID=%d, child1's PID=%d\n", getpid(), child1);
-
-	DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
-	TWAIT_REQUIRE_SUCCESS(
-	    wpid = TWAIT_GENERIC(child1, &status, WEXITED), child1);
-
-	validate_status_exited(status, exitval_child1);
-
-	DPRINTF("Notify that child1 is dead\n");
-	PARENT_TO_CHILD("exit child1", parent_child, msg);
-
-	DPRINTF("Wait for exiting of child2\n");
-	PARENT_FROM_CHILD("child2 exiting", parent_child, msg);
-}
-
-/// ----------------------------------------------------------------------------
-
-#if defined(TWAIT_HAVE_PID)
-static void
-tracer_sees_terminaton_before_the_parent_raw(bool notimeout, bool unrelated,
-                                             bool stopped)
-{
-	/*
-	 * notimeout - disable timeout in await zombie function
-	 * unrelated - attach from unrelated tracer reparented to initproc
-	 * stopped - attach to a stopped process
-	 */
-
-	struct msg_fds parent_tracee, parent_tracer;
-	const int exitval_tracee = 5;
-	const int exitval_tracer = 10;
-	pid_t tracee, tracer, wpid;
-	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
-#if defined(TWAIT_HAVE_STATUS)
-	int status;
-#endif
-
-	/*
-	 * Only a subset of options are supported.
-	 */
-	ATF_REQUIRE((!notimeout && !unrelated && !stopped) ||
-	            (!notimeout && unrelated && !stopped) ||
-	            (notimeout && !unrelated && !stopped) ||
-	            (!notimeout && unrelated && stopped));
-
-	DPRINTF("Spawn tracee\n");
-	SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
-	tracee = atf_utils_fork();
-	if (tracee == 0) {
-		if (stopped) {
-			DPRINTF("Stop self PID %d\n", getpid());
-			raise(SIGSTOP);
-		}
-
-		// Wait for parent to let us exit
-		CHILD_FROM_PARENT("exit tracee", parent_tracee, msg);
-		_exit(exitval_tracee);
-	}
-
-	DPRINTF("Spawn debugger\n");
-	SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0);
-	tracer = atf_utils_fork();
-	if (tracer == 0) {
-		if(unrelated) {
-			/* Fork again and drop parent to reattach to PID 1 */
-			tracer = atf_utils_fork();
-			if (tracer != 0)
-				_exit(exitval_tracer);
-		}
-
-		if (stopped) {
-			DPRINTF("Await for a stopped parent PID %d\n", tracee);
-			await_stopped(tracee);
-		}
-
-		DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid());
-		FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
-
-		/* Wait for tracee and assert that it was stopped w/ SIGSTOP */
-		FORKEE_REQUIRE_SUCCESS(
-		    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
-
-		forkee_status_stopped(status, SIGSTOP);
-
-		/* Resume tracee with PT_CONTINUE */
-		FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
-
-		/* Inform parent that tracer has attached to tracee */
-		CHILD_TO_PARENT("tracer ready", parent_tracer, msg);
-
-		/* Wait for parent to tell use that tracee should have exited */
-		CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg);
-
-		/* Wait for tracee and assert that it exited */
-		FORKEE_REQUIRE_SUCCESS(
-		    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
-
-		forkee_status_exited(status, exitval_tracee);
-		DPRINTF("Tracee %d exited with %d\n", tracee, exitval_tracee);
-
-		DPRINTF("Before exiting of the tracer process\n");
-		_exit(unrelated ? 0 /* collect by initproc */ : exitval_tracer);
-	}
-
-	if (unrelated) {
-		DPRINTF("Wait for the tracer process (direct child) to exit "
-		    "calling %s()\n", TWAIT_FNAME);
-		TWAIT_REQUIRE_SUCCESS(
-		    wpid = TWAIT_GENERIC(tracer, &status, 0), tracer);
-
-		validate_status_exited(status, exitval_tracer);
-
-		DPRINTF("Wait for the non-exited tracee process with %s()\n",
-		    TWAIT_FNAME);
-		TWAIT_REQUIRE_SUCCESS(
-		    wpid = TWAIT_GENERIC(tracee, NULL, WNOHANG), 0);
-	}
-
-	DPRINTF("Wait for the tracer to attach to the tracee\n");
-	PARENT_FROM_CHILD("tracer ready", parent_tracer, msg);
-
-	DPRINTF("Resume the tracee and let it exit\n");
-	PARENT_TO_CHILD("exit tracee", parent_tracee,  msg);
-
-	DPRINTF("Detect that tracee is zombie\n");
-	if (notimeout)
-		await_zombie_raw(tracee, 0);
-	else
-		await_zombie(tracee);
-
-	DPRINTF("Assert that there is no status about tracee %d - "
-	    "Tracer must detect zombie first - calling %s()\n", tracee,
-	    TWAIT_FNAME);
-	TWAIT_REQUIRE_SUCCESS(
-	    wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
-
-	if (unrelated) {
-		DPRINTF("Resume the tracer and let it detect exited tracee\n");
-		PARENT_TO_CHILD("Message 2", parent_tracer, msg);
-	} else {
-		DPRINTF("Tell the tracer child should have exited\n");
-		PARENT_TO_CHILD("wait for tracee exit", parent_tracer,  msg);
-		DPRINTF("Wait for tracer to finish its job and exit - calling "
-			"%s()\n", TWAIT_FNAME);
-
-		DPRINTF("Wait from tracer child to complete waiting for "
-			"tracee\n");
-		TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
-		    tracer);
-
-		validate_status_exited(status, exitval_tracer);
-	}
-
-	DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n",
-	    TWAIT_FNAME);
-	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
-
-	validate_status_exited(status, exitval_tracee);
-
-	msg_close(&parent_tracer);
-	msg_close(&parent_tracee);
-}
-
-ATF_TC(tracer_sees_terminaton_before_the_parent);
-ATF_TC_HEAD(tracer_sees_terminaton_before_the_parent, tc)
-{
-	atf_tc_set_md_var(tc, "descr",
-	    "Assert that tracer sees process termination before the parent");
-}
-
-ATF_TC_BODY(tracer_sees_terminaton_before_the_parent, tc)
-{
-
-	tracer_sees_terminaton_before_the_parent_raw(false, false, false);
-}
-
-ATF_TC(tracer_sysctl_lookup_without_duplicates);
-ATF_TC_HEAD(tracer_sysctl_lookup_without_duplicates, tc)
-{
-	atf_tc_set_md_var(tc, "timeout", "15");
-	atf_tc_set_md_var(tc, "descr",
-	    "Assert that await_zombie() in attach1 always finds a single "
-	    "process and no other error is reported");
-}
-
-ATF_TC_BODY(tracer_sysctl_lookup_without_duplicates, tc)
-{
-	time_t start, end;
-	double diff;
-	unsigned long N = 0;
-
-	/*
-	 * Reuse this test with tracer_sees_terminaton_before_the_parent_raw().
-	 * This test body isn't specific to this race, however it's just good
-	 * enough for this purposes, no need to invent a dedicated code flow.
-	 */
-
-	start = time(NULL);
-	while (true) {
-		DPRINTF("Step: %lu\n", N);
-		tracer_sees_terminaton_before_the_parent_raw(true, false,
-		                                             false);
-		end = time(NULL);
-		diff = difftime(end, start);
-		if (diff >= 5.0)
-			break;
-		++N;
-	}
-	DPRINTF("Iterations: %lu\n", N);
-}
-
-ATF_TC(unrelated_tracer_sees_terminaton_before_the_parent);
-ATF_TC_HEAD(unrelated_tracer_sees_terminaton_before_the_parent, tc)
-{
-	atf_tc_set_md_var(tc, "descr",
-	    "Assert that tracer sees process termination before the parent");
-}
-
-ATF_TC_BODY(unrelated_tracer_sees_terminaton_before_the_parent, tc)
-{
-
-	tracer_sees_terminaton_before_the_parent_raw(false, true, false);
-}
-
-ATF_TC(tracer_attach_to_unrelated_stopped_process);
-ATF_TC_HEAD(tracer_attach_to_unrelated_stopped_process, tc)
-{
-	atf_tc_set_md_var(tc, "descr",
-	    "Assert that tracer can attach to an unrelated stopped process");
-}
-
-ATF_TC_BODY(tracer_attach_to_unrelated_stopped_process, tc)
-{
-
-	tracer_sees_terminaton_before_the_parent_raw(false, true, true);
-}
-#endif
-
-/// ----------------------------------------------------------------------------
-
-static void
-parent_attach_to_its_child(bool stopped)
-{
-	struct msg_fds parent_tracee;
-	const int exitval_tracee = 5;
-	pid_t tracee, wpid;
-	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
-#if defined(TWAIT_HAVE_STATUS)
-	int status;
-#endif
-
-	DPRINTF("Spawn tracee\n");
-	SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
-	tracee = atf_utils_fork();
-	if (tracee == 0) {
-		CHILD_FROM_PARENT("Message 1", parent_tracee, msg);
-		DPRINTF("Parent should now attach to tracee\n");
-
-		if (stopped) {
-			DPRINTF("Stop self PID %d\n", getpid());
-			SYSCALL_REQUIRE(raise(SIGSTOP) != -1);
-		}
-
-		CHILD_FROM_PARENT("Message 2", parent_tracee, msg);
-		/* Wait for message from the parent */
-		_exit(exitval_tracee);
-	}
-	PARENT_TO_CHILD("Message 1", parent_tracee, msg);
-
-	if (stopped) {
-		DPRINTF("Await for a stopped tracee PID %d\n", tracee);
-		await_stopped(tracee);
-	}
-
-	DPRINTF("Before calling PT_ATTACH for tracee %d\n", tracee);
-	SYSCALL_REQUIRE(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
-
-	DPRINTF("Wait for the stopped tracee process with %s()\n",
-	    TWAIT_FNAME);
-	TWAIT_REQUIRE_SUCCESS(
-	    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
-
-	validate_status_stopped(status, SIGSTOP);
-
-	DPRINTF("Resume tracee with PT_CONTINUE\n");
-	SYSCALL_REQUIRE(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
-
-	DPRINTF("Let the tracee exit now\n");
-	PARENT_TO_CHILD("Message 2", parent_tracee, msg);
-
-	DPRINTF("Wait for tracee to exit with %s()\n", TWAIT_FNAME);
-	TWAIT_REQUIRE_SUCCESS(
-	    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
-
-	validate_status_exited(status, exitval_tracee);
-
-	DPRINTF("Before calling %s() for tracee\n", TWAIT_FNAME);
-	TWAIT_REQUIRE_FAILURE(ECHILD,
-	    wpid = TWAIT_GENERIC(tracee, &status, 0));
-
-	msg_close(&parent_tracee);
-}
-
-ATF_TC(parent_attach_to_its_child);
-ATF_TC_HEAD(parent_attach_to_its_child, tc)
-{
-	atf_tc_set_md_var(tc, "descr",
-	    "Assert that tracer parent can PT_ATTACH to its child");
-}
-
-ATF_TC_BODY(parent_attach_to_its_child, tc)
-{
-
-	parent_attach_to_its_child(false);
-}
-
-ATF_TC(parent_attach_to_its_stopped_child);
-ATF_TC_HEAD(parent_attach_to_its_stopped_child, tc)
-{
-	atf_tc_set_md_var(tc, "descr",
-	    "Assert that tracer parent can PT_ATTACH to its stopped child");
-}
-
-ATF_TC_BODY(parent_attach_to_its_stopped_child, tc)
-{
-
-	parent_attach_to_its_child(true);
-}
-
-/// ----------------------------------------------------------------------------
-
-static void
-child_attach_to_its_parent(bool stopped)
-{
-	struct msg_fds parent_tracee;
-	const int exitval_tracer = 5;
-	pid_t tracer, wpid;
-	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
-#if defined(TWAIT_HAVE_STATUS)
-	int status;
-#endif
-
-	DPRINTF("Spawn tracer\n");
-	SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
-	tracer = atf_utils_fork();
-	if (tracer == 0) {
-		/* Wait for message from the parent */
-		CHILD_FROM_PARENT("Message 1", parent_tracee, msg);
-
-		if (stopped) {
-			DPRINTF("Await for a stopped parent PID %d\n",
-			        getppid());
-			await_stopped(getppid());
-		}
-
-		DPRINTF("Attach to parent PID %d with PT_ATTACH from child\n",
-		    getppid());
-		FORKEE_ASSERT(ptrace(PT_ATTACH, getppid(), NULL, 0) != -1);
-
-		DPRINTF("Wait for the stopped parent process with %s()\n",
-		    TWAIT_FNAME);
-		FORKEE_REQUIRE_SUCCESS(
-		    wpid = TWAIT_GENERIC(getppid(), &status, 0), getppid());
-
-		forkee_status_stopped(status, SIGSTOP);
-
-		DPRINTF("Resume parent with PT_DETACH\n");
-		FORKEE_ASSERT(ptrace(PT_DETACH, getppid(), (void *)1, 0)
-		    != -1);
-
-		/* Tell parent we are ready */
-		CHILD_TO_PARENT("Message 1", parent_tracee, msg);
-
-		_exit(exitval_tracer);
-	}
-
-	DPRINTF("Wait for the tracer to become ready\n");
-	PARENT_TO_CHILD("Message 1", parent_tracee, msg);
-
-	if (stopped) {
-		DPRINTF("Stop self PID %d\n", getpid());
-		SYSCALL_REQUIRE(raise(SIGSTOP) != -1);
-	}
-
-	DPRINTF("Allow the tracer to exit now\n");
-	PARENT_FROM_CHILD("Message 1", parent_tracee, msg);
-
-	DPRINTF("Wait for tracer to exit with %s()\n", TWAIT_FNAME);
-	TWAIT_REQUIRE_SUCCESS(
-	    wpid = TWAIT_GENERIC(tracer, &status, 0), tracer);
-
-	validate_status_exited(status, exitval_tracer);
-
-	DPRINTF("Before calling %s() for tracer\n", TWAIT_FNAME);
-	TWAIT_REQUIRE_FAILURE(ECHILD,
-	    wpid = TWAIT_GENERIC(tracer, &status, 0));
-
-	msg_close(&parent_tracee);
-}
-
-ATF_TC(child_attach_to_its_parent);
-ATF_TC_HEAD(child_attach_to_its_parent, tc)
-{
-	atf_tc_set_md_var(tc, "descr",
-	    "Assert that tracer child can PT_ATTACH to its parent");
-}
-
-ATF_TC_BODY(child_attach_to_its_parent, tc)
-{
-
-	child_attach_to_its_parent(false);
-}
-
-ATF_TC(child_attach_to_its_stopped_parent);
-ATF_TC_HEAD(child_attach_to_its_stopped_parent, tc)
-{
-	atf_tc_set_md_var(tc, "descr",
-	    "Assert that tracer child can PT_ATTACH to its stopped parent");
-}
-
-ATF_TC_BODY(child_attach_to_its_stopped_parent, tc)
-{
-	/*
-	 * The ATF framework (atf-run) does not tolerate raise(SIGSTOP), as
-	 * this causes a pipe (established from atf-run) to be broken.
-	 * atf-run uses this mechanism to monitor whether a test is alive.
-	 *
-	 * As a workaround spawn this test as a subprocess.
-	 */
-
-	const int exitval = 15;
-	pid_t child, wpid;
-#if defined(TWAIT_HAVE_STATUS)
-	int status;
-#endif
-
-	SYSCALL_REQUIRE((child = fork()) != -1);
-	if (child == 0) {
-		child_attach_to_its_parent(true);
-		_exit(exitval);
-	} else {
-		DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
-		TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
-
-		validate_status_exited(status, exitval);
-
-		DPRINTF("Before calling %s() for the exited child\n", TWAIT_FNAME);
-		TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
-	}
-}
-
-/// ----------------------------------------------------------------------------
-
-#if defined(TWAIT_HAVE_PID)
-
-enum tracee_sees_its_original_parent_type {
-	TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID,
-	TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2,
-	TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS
-};
-
-static void
-tracee_sees_its_original_parent(enum tracee_sees_its_original_parent_type type)
-{
-	struct msg_fds parent_tracer, parent_tracee;
-	const int exitval_tracee = 5;
-	const int exitval_tracer = 10;
-	pid_t parent, tracee, tracer, wpid;
-	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
-#if defined(TWAIT_HAVE_STATUS)
-	int status;
-#endif
-	/* sysctl(3) - kinfo_proc2 */
-	int name[CTL_MAXNAME];
-	struct kinfo_proc2 kp;
-	size_t len = sizeof(kp);
-	unsigned int namelen;
-
-	/* procfs - status  */
-	FILE *fp;
-	struct stat st;
-	const char *fname = "/proc/curproc/status";
-	char s_executable[MAXPATHLEN];
-	int s_pid, s_ppid;
-	int rv;
-
-	if (type == TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS) {
-		SYSCALL_REQUIRE(
-		    (rv = stat(fname, &st)) == 0 || (errno == ENOENT));
-		if (rv != 0)
-			atf_tc_skip("/proc/curproc/status not found");
-	}
-
-	DPRINTF("Spawn tracee\n");
-	SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0);
-	SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
-	tracee = atf_utils_fork();
-	if (tracee == 0) {
-		parent = getppid();
-
-		/* Emit message to the parent */
-		CHILD_TO_PARENT("tracee ready", parent_tracee, msg);
-		CHILD_FROM_PARENT("exit tracee", parent_tracee, msg);
-
-		switch (type) {
-		case TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID:
-			FORKEE_ASSERT_EQ(parent, getppid());
-			break;
-		case TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2:
-			namelen = 0;
-			name[namelen++] = CTL_KERN;
-			name[namelen++] = KERN_PROC2;
-			name[namelen++] = KERN_PROC_PID;
-			name[namelen++] = getpid();
-			name[namelen++] = len;
-			name[namelen++] = 1;
-
-			FORKEE_ASSERT_EQ(
-			    sysctl(name, namelen, &kp, &len, NULL, 0), 0);
-			FORKEE_ASSERT_EQ(parent, kp.p_ppid);
-			break;
-		case TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS:
-			/*
-			 * Format:
-			 *  EXECUTABLE PID PPID ...
-			 */
-			FORKEE_ASSERT((fp = fopen(fname, "r")) != NULL);
-			fscanf(fp, "%s %d %d", s_executable, &s_pid, &s_ppid);
-			FORKEE_ASSERT_EQ(fclose(fp), 0);
-			FORKEE_ASSERT_EQ(parent, s_ppid);
-			break;
-		}
-
-		_exit(exitval_tracee);
-	}
-	DPRINTF("Wait for child to record its parent identifier (pid)\n");
-	PARENT_FROM_CHILD("tracee ready", parent_tracee, msg);
-
-	DPRINTF("Spawn debugger\n");
-	tracer = atf_utils_fork();
-	if (tracer == 0) {
-		/* No IPC to communicate with the child */
-		DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid());
-		FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
-
-		/* Wait for tracee and assert that it was stopped w/ SIGSTOP */
-		FORKEE_REQUIRE_SUCCESS(
-		    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
-
-		forkee_status_stopped(status, SIGSTOP);
-
-		/* Resume tracee with PT_CONTINUE */
-		FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
-
-		/* Inform parent that tracer has attached to tracee */
-		CHILD_TO_PARENT("tracer ready", parent_tracer, msg);
-
-		/* Wait for parent to tell use that tracee should have exited */
-		CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg);
-
-		/* Wait for tracee and assert that it exited */
-		FORKEE_REQUIRE_SUCCESS(
-		    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
-
-		forkee_status_exited(status, exitval_tracee);
-
-		DPRINTF("Before exiting of the tracer process\n");
-		_exit(exitval_tracer);
-	}
-
-	DPRINTF("Wait for the tracer to attach to the tracee\n");
-	PARENT_FROM_CHILD("tracer ready",  parent_tracer, msg);
-
-	DPRINTF("Resume the tracee and let it exit\n");
-	PARENT_TO_CHILD("exit tracee",  parent_tracee, msg);
-
-	DPRINTF("Detect that tracee is zombie\n");
-	await_zombie(tracee);
-
-	DPRINTF("Assert that there is no status about tracee - "
-	    "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME);
-	TWAIT_REQUIRE_SUCCESS(
-	    wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
-
-	DPRINTF("Tell the tracer child should have exited\n");
-	PARENT_TO_CHILD("wait for tracee exit",  parent_tracer, msg);
-
-	DPRINTF("Wait from tracer child to complete waiting for tracee\n");
-	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
-	    tracer);
-
-	validate_status_exited(status, exitval_tracer);
-
-	DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n",
-	    TWAIT_FNAME);
-	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG),
-	    tracee);
-
-	validate_status_exited(status, exitval_tracee);
-
-	msg_close(&parent_tracer);
-	msg_close(&parent_tracee);
-}
-
-#define TRACEE_SEES_ITS_ORIGINAL_PARENT(test, type, descr)		\
-ATF_TC(test);								\
-ATF_TC_HEAD(test, tc)							\
-{									\
-	atf_tc_set_md_var(tc, "descr",					\
-	    "Assert that tracee sees its original parent when being traced " \
-	    "(check " descr ")");					\
-}									\
-									\
-ATF_TC_BODY(test, tc)							\
-{									\
-									\
-	tracee_sees_its_original_parent(type);				\
-}
-
-TRACEE_SEES_ITS_ORIGINAL_PARENT(
-	tracee_sees_its_original_parent_getppid,
-	TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID,
-	"getppid(2)");
-TRACEE_SEES_ITS_ORIGINAL_PARENT(
-	tracee_sees_its_original_parent_sysctl_kinfo_proc2,
-	TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2,
-	"sysctl(3) and kinfo_proc2");
-TRACEE_SEES_ITS_ORIGINAL_PARENT(
-	tracee_sees_its_original_parent_procfs_status,
-	TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS,
-	"the status file in procfs");
-#endif
-
-/// ----------------------------------------------------------------------------
-
 static void
 ptrace_siginfo(bool faked, void (*sah)(int a, siginfo_t *b, void *c), int *signal_caught)
 {
@@ -2214,6 +1537,7 @@ THREAD_CONCURRENT_TEST(thread_concurrent
 #include "t_ptrace_eventmask_wait.h"
 #include "t_ptrace_lwp_wait.h"
 #include "t_ptrace_exec_wait.h"
+#include "t_ptrace_topology_wait.h"
 
 /// ----------------------------------------------------------------------------
 
@@ -2244,27 +1568,6 @@ ATF_TP_ADD_TCS(tp)
 	setvbuf(stderr, NULL, _IONBF, 0);
 
 #ifdef ENABLE_TESTS
-	ATF_TP_ADD_TC(tp, traceme_pid1_parent);
-
-	ATF_TP_ADD_TC_HAVE_PID(tp, tracer_sees_terminaton_before_the_parent);
-	ATF_TP_ADD_TC_HAVE_PID(tp, tracer_sysctl_lookup_without_duplicates);
-	ATF_TP_ADD_TC_HAVE_PID(tp,
-		unrelated_tracer_sees_terminaton_before_the_parent);
-	ATF_TP_ADD_TC_HAVE_PID(tp, tracer_attach_to_unrelated_stopped_process);
-
-	ATF_TP_ADD_TC(tp, parent_attach_to_its_child);
-	ATF_TP_ADD_TC(tp, parent_attach_to_its_stopped_child);
-
-	ATF_TP_ADD_TC(tp, child_attach_to_its_parent);
-	ATF_TP_ADD_TC(tp, child_attach_to_its_stopped_parent);
-
-	ATF_TP_ADD_TC_HAVE_PID(tp,
-		tracee_sees_its_original_parent_getppid);
-	ATF_TP_ADD_TC_HAVE_PID(tp,
-		tracee_sees_its_original_parent_sysctl_kinfo_proc2);
-	ATF_TP_ADD_TC_HAVE_PID(tp,
-		tracee_sees_its_original_parent_procfs_status);
-
 	ATF_TP_ADD_TC(tp, siginfo_set_unmodified);
 	ATF_TP_ADD_TC(tp, siginfo_set_faked);
 
@@ -2320,6 +1623,7 @@ ATF_TP_ADD_TCS(tp)
 	ATF_TP_ADD_TCS_PTRACE_WAIT_EVENTMASK();
 	ATF_TP_ADD_TCS_PTRACE_WAIT_LWP();
 	ATF_TP_ADD_TCS_PTRACE_WAIT_EXEC();
+	ATF_TP_ADD_TCS_PTRACE_WAIT_TOPOLOGY();
 
 	ATF_TP_ADD_TCS_PTRACE_WAIT_AMD64();
 	ATF_TP_ADD_TCS_PTRACE_WAIT_I386();

Added files:

Index: src/tests/lib/libc/sys/t_ptrace_topology_wait.h
diff -u /dev/null src/tests/lib/libc/sys/t_ptrace_topology_wait.h:1.1
--- /dev/null	Tue May  5 00:33:37 2020
+++ src/tests/lib/libc/sys/t_ptrace_topology_wait.h	Tue May  5 00:33:37 2020
@@ -0,0 +1,721 @@
+/*	$NetBSD: t_ptrace_topology_wait.h,v 1.1 2020/05/05 00:33:37 kamil Exp $	*/
+
+/*-
+ * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+ATF_TC(traceme_pid1_parent);
+ATF_TC_HEAD(traceme_pid1_parent, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Verify that PT_TRACE_ME is not allowed when our parent is PID1");
+}
+
+ATF_TC_BODY(traceme_pid1_parent, tc)
+{
+	struct msg_fds parent_child;
+	int exitval_child1 = 1, exitval_child2 = 2;
+	pid_t child1, child2, wpid;
+	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+
+	SYSCALL_REQUIRE(msg_open(&parent_child) == 0);
+
+	DPRINTF("Before forking process PID=%d\n", getpid());
+	SYSCALL_REQUIRE((child1 = fork()) != -1);
+	if (child1 == 0) {
+		DPRINTF("Before forking process PID=%d\n", getpid());
+		SYSCALL_REQUIRE((child2 = fork()) != -1);
+		if (child2 != 0) {
+			DPRINTF("Parent process PID=%d, child2's PID=%d\n",
+			    getpid(), child2);
+			_exit(exitval_child1);
+		}
+		CHILD_FROM_PARENT("exit child1", parent_child, msg);
+
+		DPRINTF("Assert that our parent is PID1 (initproc)\n");
+		FORKEE_ASSERT_EQ(getppid(), 1);
+
+		DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
+		FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) == -1);
+		SYSCALL_REQUIRE_ERRNO(errno, EPERM);
+
+		CHILD_TO_PARENT("child2 exiting", parent_child, msg);
+
+		_exit(exitval_child2);
+	}
+	DPRINTF("Parent process PID=%d, child1's PID=%d\n", getpid(), child1);
+
+	DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(
+	    wpid = TWAIT_GENERIC(child1, &status, WEXITED), child1);
+
+	validate_status_exited(status, exitval_child1);
+
+	DPRINTF("Notify that child1 is dead\n");
+	PARENT_TO_CHILD("exit child1", parent_child, msg);
+
+	DPRINTF("Wait for exiting of child2\n");
+	PARENT_FROM_CHILD("child2 exiting", parent_child, msg);
+}
+
+/// ----------------------------------------------------------------------------
+
+#if defined(TWAIT_HAVE_PID)
+static void
+tracer_sees_terminaton_before_the_parent_raw(bool notimeout, bool unrelated,
+                                             bool stopped)
+{
+	/*
+	 * notimeout - disable timeout in await zombie function
+	 * unrelated - attach from unrelated tracer reparented to initproc
+	 * stopped - attach to a stopped process
+	 */
+
+	struct msg_fds parent_tracee, parent_tracer;
+	const int exitval_tracee = 5;
+	const int exitval_tracer = 10;
+	pid_t tracee, tracer, wpid;
+	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+
+	/*
+	 * Only a subset of options are supported.
+	 */
+	ATF_REQUIRE((!notimeout && !unrelated && !stopped) ||
+	            (!notimeout && unrelated && !stopped) ||
+	            (notimeout && !unrelated && !stopped) ||
+	            (!notimeout && unrelated && stopped));
+
+	DPRINTF("Spawn tracee\n");
+	SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
+	tracee = atf_utils_fork();
+	if (tracee == 0) {
+		if (stopped) {
+			DPRINTF("Stop self PID %d\n", getpid());
+			raise(SIGSTOP);
+		}
+
+		// Wait for parent to let us exit
+		CHILD_FROM_PARENT("exit tracee", parent_tracee, msg);
+		_exit(exitval_tracee);
+	}
+
+	DPRINTF("Spawn debugger\n");
+	SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0);
+	tracer = atf_utils_fork();
+	if (tracer == 0) {
+		if(unrelated) {
+			/* Fork again and drop parent to reattach to PID 1 */
+			tracer = atf_utils_fork();
+			if (tracer != 0)
+				_exit(exitval_tracer);
+		}
+
+		if (stopped) {
+			DPRINTF("Await for a stopped parent PID %d\n", tracee);
+			await_stopped(tracee);
+		}
+
+		DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid());
+		FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
+
+		/* Wait for tracee and assert that it was stopped w/ SIGSTOP */
+		FORKEE_REQUIRE_SUCCESS(
+		    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+		forkee_status_stopped(status, SIGSTOP);
+
+		/* Resume tracee with PT_CONTINUE */
+		FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
+
+		/* Inform parent that tracer has attached to tracee */
+		CHILD_TO_PARENT("tracer ready", parent_tracer, msg);
+
+		/* Wait for parent to tell use that tracee should have exited */
+		CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg);
+
+		/* Wait for tracee and assert that it exited */
+		FORKEE_REQUIRE_SUCCESS(
+		    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+		forkee_status_exited(status, exitval_tracee);
+		DPRINTF("Tracee %d exited with %d\n", tracee, exitval_tracee);
+
+		DPRINTF("Before exiting of the tracer process\n");
+		_exit(unrelated ? 0 /* collect by initproc */ : exitval_tracer);
+	}
+
+	if (unrelated) {
+		DPRINTF("Wait for the tracer process (direct child) to exit "
+		    "calling %s()\n", TWAIT_FNAME);
+		TWAIT_REQUIRE_SUCCESS(
+		    wpid = TWAIT_GENERIC(tracer, &status, 0), tracer);
+
+		validate_status_exited(status, exitval_tracer);
+
+		DPRINTF("Wait for the non-exited tracee process with %s()\n",
+		    TWAIT_FNAME);
+		TWAIT_REQUIRE_SUCCESS(
+		    wpid = TWAIT_GENERIC(tracee, NULL, WNOHANG), 0);
+	}
+
+	DPRINTF("Wait for the tracer to attach to the tracee\n");
+	PARENT_FROM_CHILD("tracer ready", parent_tracer, msg);
+
+	DPRINTF("Resume the tracee and let it exit\n");
+	PARENT_TO_CHILD("exit tracee", parent_tracee,  msg);
+
+	DPRINTF("Detect that tracee is zombie\n");
+	if (notimeout)
+		await_zombie_raw(tracee, 0);
+	else
+		await_zombie(tracee);
+
+	DPRINTF("Assert that there is no status about tracee %d - "
+	    "Tracer must detect zombie first - calling %s()\n", tracee,
+	    TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(
+	    wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
+
+	if (unrelated) {
+		DPRINTF("Resume the tracer and let it detect exited tracee\n");
+		PARENT_TO_CHILD("Message 2", parent_tracer, msg);
+	} else {
+		DPRINTF("Tell the tracer child should have exited\n");
+		PARENT_TO_CHILD("wait for tracee exit", parent_tracer,  msg);
+		DPRINTF("Wait for tracer to finish its job and exit - calling "
+			"%s()\n", TWAIT_FNAME);
+
+		DPRINTF("Wait from tracer child to complete waiting for "
+			"tracee\n");
+		TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
+		    tracer);
+
+		validate_status_exited(status, exitval_tracer);
+	}
+
+	DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n",
+	    TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+	validate_status_exited(status, exitval_tracee);
+
+	msg_close(&parent_tracer);
+	msg_close(&parent_tracee);
+}
+
+ATF_TC(tracer_sees_terminaton_before_the_parent);
+ATF_TC_HEAD(tracer_sees_terminaton_before_the_parent, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Assert that tracer sees process termination before the parent");
+}
+
+ATF_TC_BODY(tracer_sees_terminaton_before_the_parent, tc)
+{
+
+	tracer_sees_terminaton_before_the_parent_raw(false, false, false);
+}
+
+ATF_TC(tracer_sysctl_lookup_without_duplicates);
+ATF_TC_HEAD(tracer_sysctl_lookup_without_duplicates, tc)
+{
+	atf_tc_set_md_var(tc, "timeout", "15");
+	atf_tc_set_md_var(tc, "descr",
+	    "Assert that await_zombie() in attach1 always finds a single "
+	    "process and no other error is reported");
+}
+
+ATF_TC_BODY(tracer_sysctl_lookup_without_duplicates, tc)
+{
+	time_t start, end;
+	double diff;
+	unsigned long N = 0;
+
+	/*
+	 * Reuse this test with tracer_sees_terminaton_before_the_parent_raw().
+	 * This test body isn't specific to this race, however it's just good
+	 * enough for this purposes, no need to invent a dedicated code flow.
+	 */
+
+	start = time(NULL);
+	while (true) {
+		DPRINTF("Step: %lu\n", N);
+		tracer_sees_terminaton_before_the_parent_raw(true, false,
+		                                             false);
+		end = time(NULL);
+		diff = difftime(end, start);
+		if (diff >= 5.0)
+			break;
+		++N;
+	}
+	DPRINTF("Iterations: %lu\n", N);
+}
+
+ATF_TC(unrelated_tracer_sees_terminaton_before_the_parent);
+ATF_TC_HEAD(unrelated_tracer_sees_terminaton_before_the_parent, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Assert that tracer sees process termination before the parent");
+}
+
+ATF_TC_BODY(unrelated_tracer_sees_terminaton_before_the_parent, tc)
+{
+
+	tracer_sees_terminaton_before_the_parent_raw(false, true, false);
+}
+
+ATF_TC(tracer_attach_to_unrelated_stopped_process);
+ATF_TC_HEAD(tracer_attach_to_unrelated_stopped_process, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Assert that tracer can attach to an unrelated stopped process");
+}
+
+ATF_TC_BODY(tracer_attach_to_unrelated_stopped_process, tc)
+{
+
+	tracer_sees_terminaton_before_the_parent_raw(false, true, true);
+}
+#endif
+
+/// ----------------------------------------------------------------------------
+
+static void
+parent_attach_to_its_child(bool stopped)
+{
+	struct msg_fds parent_tracee;
+	const int exitval_tracee = 5;
+	pid_t tracee, wpid;
+	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+
+	DPRINTF("Spawn tracee\n");
+	SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
+	tracee = atf_utils_fork();
+	if (tracee == 0) {
+		CHILD_FROM_PARENT("Message 1", parent_tracee, msg);
+		DPRINTF("Parent should now attach to tracee\n");
+
+		if (stopped) {
+			DPRINTF("Stop self PID %d\n", getpid());
+			SYSCALL_REQUIRE(raise(SIGSTOP) != -1);
+		}
+
+		CHILD_FROM_PARENT("Message 2", parent_tracee, msg);
+		/* Wait for message from the parent */
+		_exit(exitval_tracee);
+	}
+	PARENT_TO_CHILD("Message 1", parent_tracee, msg);
+
+	if (stopped) {
+		DPRINTF("Await for a stopped tracee PID %d\n", tracee);
+		await_stopped(tracee);
+	}
+
+	DPRINTF("Before calling PT_ATTACH for tracee %d\n", tracee);
+	SYSCALL_REQUIRE(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
+
+	DPRINTF("Wait for the stopped tracee process with %s()\n",
+	    TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(
+	    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+	validate_status_stopped(status, SIGSTOP);
+
+	DPRINTF("Resume tracee with PT_CONTINUE\n");
+	SYSCALL_REQUIRE(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
+
+	DPRINTF("Let the tracee exit now\n");
+	PARENT_TO_CHILD("Message 2", parent_tracee, msg);
+
+	DPRINTF("Wait for tracee to exit with %s()\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(
+	    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+	validate_status_exited(status, exitval_tracee);
+
+	DPRINTF("Before calling %s() for tracee\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_FAILURE(ECHILD,
+	    wpid = TWAIT_GENERIC(tracee, &status, 0));
+
+	msg_close(&parent_tracee);
+}
+
+ATF_TC(parent_attach_to_its_child);
+ATF_TC_HEAD(parent_attach_to_its_child, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Assert that tracer parent can PT_ATTACH to its child");
+}
+
+ATF_TC_BODY(parent_attach_to_its_child, tc)
+{
+
+	parent_attach_to_its_child(false);
+}
+
+ATF_TC(parent_attach_to_its_stopped_child);
+ATF_TC_HEAD(parent_attach_to_its_stopped_child, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Assert that tracer parent can PT_ATTACH to its stopped child");
+}
+
+ATF_TC_BODY(parent_attach_to_its_stopped_child, tc)
+{
+
+	parent_attach_to_its_child(true);
+}
+
+/// ----------------------------------------------------------------------------
+
+static void
+child_attach_to_its_parent(bool stopped)
+{
+	struct msg_fds parent_tracee;
+	const int exitval_tracer = 5;
+	pid_t tracer, wpid;
+	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+
+	DPRINTF("Spawn tracer\n");
+	SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
+	tracer = atf_utils_fork();
+	if (tracer == 0) {
+		/* Wait for message from the parent */
+		CHILD_FROM_PARENT("Message 1", parent_tracee, msg);
+
+		if (stopped) {
+			DPRINTF("Await for a stopped parent PID %d\n",
+			        getppid());
+			await_stopped(getppid());
+		}
+
+		DPRINTF("Attach to parent PID %d with PT_ATTACH from child\n",
+		    getppid());
+		FORKEE_ASSERT(ptrace(PT_ATTACH, getppid(), NULL, 0) != -1);
+
+		DPRINTF("Wait for the stopped parent process with %s()\n",
+		    TWAIT_FNAME);
+		FORKEE_REQUIRE_SUCCESS(
+		    wpid = TWAIT_GENERIC(getppid(), &status, 0), getppid());
+
+		forkee_status_stopped(status, SIGSTOP);
+
+		DPRINTF("Resume parent with PT_DETACH\n");
+		FORKEE_ASSERT(ptrace(PT_DETACH, getppid(), (void *)1, 0)
+		    != -1);
+
+		/* Tell parent we are ready */
+		CHILD_TO_PARENT("Message 1", parent_tracee, msg);
+
+		_exit(exitval_tracer);
+	}
+
+	DPRINTF("Wait for the tracer to become ready\n");
+	PARENT_TO_CHILD("Message 1", parent_tracee, msg);
+
+	if (stopped) {
+		DPRINTF("Stop self PID %d\n", getpid());
+		SYSCALL_REQUIRE(raise(SIGSTOP) != -1);
+	}
+
+	DPRINTF("Allow the tracer to exit now\n");
+	PARENT_FROM_CHILD("Message 1", parent_tracee, msg);
+
+	DPRINTF("Wait for tracer to exit with %s()\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(
+	    wpid = TWAIT_GENERIC(tracer, &status, 0), tracer);
+
+	validate_status_exited(status, exitval_tracer);
+
+	DPRINTF("Before calling %s() for tracer\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_FAILURE(ECHILD,
+	    wpid = TWAIT_GENERIC(tracer, &status, 0));
+
+	msg_close(&parent_tracee);
+}
+
+ATF_TC(child_attach_to_its_parent);
+ATF_TC_HEAD(child_attach_to_its_parent, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Assert that tracer child can PT_ATTACH to its parent");
+}
+
+ATF_TC_BODY(child_attach_to_its_parent, tc)
+{
+
+	child_attach_to_its_parent(false);
+}
+
+ATF_TC(child_attach_to_its_stopped_parent);
+ATF_TC_HEAD(child_attach_to_its_stopped_parent, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Assert that tracer child can PT_ATTACH to its stopped parent");
+}
+
+ATF_TC_BODY(child_attach_to_its_stopped_parent, tc)
+{
+	/*
+	 * The ATF framework (atf-run) does not tolerate raise(SIGSTOP), as
+	 * this causes a pipe (established from atf-run) to be broken.
+	 * atf-run uses this mechanism to monitor whether a test is alive.
+	 *
+	 * As a workaround spawn this test as a subprocess.
+	 */
+
+	const int exitval = 15;
+	pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+
+	SYSCALL_REQUIRE((child = fork()) != -1);
+	if (child == 0) {
+		child_attach_to_its_parent(true);
+		_exit(exitval);
+	} else {
+		DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
+		TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+		validate_status_exited(status, exitval);
+
+		DPRINTF("Before calling %s() for the exited child\n", TWAIT_FNAME);
+		TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+	}
+}
+
+/// ----------------------------------------------------------------------------
+
+#if defined(TWAIT_HAVE_PID)
+
+enum tracee_sees_its_original_parent_type {
+	TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID,
+	TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2,
+	TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS
+};
+
+static void
+tracee_sees_its_original_parent(enum tracee_sees_its_original_parent_type type)
+{
+	struct msg_fds parent_tracer, parent_tracee;
+	const int exitval_tracee = 5;
+	const int exitval_tracer = 10;
+	pid_t parent, tracee, tracer, wpid;
+	uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+	int status;
+#endif
+	/* sysctl(3) - kinfo_proc2 */
+	int name[CTL_MAXNAME];
+	struct kinfo_proc2 kp;
+	size_t len = sizeof(kp);
+	unsigned int namelen;
+
+	/* procfs - status  */
+	FILE *fp;
+	struct stat st;
+	const char *fname = "/proc/curproc/status";
+	char s_executable[MAXPATHLEN];
+	int s_pid, s_ppid;
+	int rv;
+
+	if (type == TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS) {
+		SYSCALL_REQUIRE(
+		    (rv = stat(fname, &st)) == 0 || (errno == ENOENT));
+		if (rv != 0)
+			atf_tc_skip("/proc/curproc/status not found");
+	}
+
+	DPRINTF("Spawn tracee\n");
+	SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0);
+	SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
+	tracee = atf_utils_fork();
+	if (tracee == 0) {
+		parent = getppid();
+
+		/* Emit message to the parent */
+		CHILD_TO_PARENT("tracee ready", parent_tracee, msg);
+		CHILD_FROM_PARENT("exit tracee", parent_tracee, msg);
+
+		switch (type) {
+		case TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID:
+			FORKEE_ASSERT_EQ(parent, getppid());
+			break;
+		case TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2:
+			namelen = 0;
+			name[namelen++] = CTL_KERN;
+			name[namelen++] = KERN_PROC2;
+			name[namelen++] = KERN_PROC_PID;
+			name[namelen++] = getpid();
+			name[namelen++] = len;
+			name[namelen++] = 1;
+
+			FORKEE_ASSERT_EQ(
+			    sysctl(name, namelen, &kp, &len, NULL, 0), 0);
+			FORKEE_ASSERT_EQ(parent, kp.p_ppid);
+			break;
+		case TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS:
+			/*
+			 * Format:
+			 *  EXECUTABLE PID PPID ...
+			 */
+			FORKEE_ASSERT((fp = fopen(fname, "r")) != NULL);
+			fscanf(fp, "%s %d %d", s_executable, &s_pid, &s_ppid);
+			FORKEE_ASSERT_EQ(fclose(fp), 0);
+			FORKEE_ASSERT_EQ(parent, s_ppid);
+			break;
+		}
+
+		_exit(exitval_tracee);
+	}
+	DPRINTF("Wait for child to record its parent identifier (pid)\n");
+	PARENT_FROM_CHILD("tracee ready", parent_tracee, msg);
+
+	DPRINTF("Spawn debugger\n");
+	tracer = atf_utils_fork();
+	if (tracer == 0) {
+		/* No IPC to communicate with the child */
+		DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid());
+		FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
+
+		/* Wait for tracee and assert that it was stopped w/ SIGSTOP */
+		FORKEE_REQUIRE_SUCCESS(
+		    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+		forkee_status_stopped(status, SIGSTOP);
+
+		/* Resume tracee with PT_CONTINUE */
+		FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
+
+		/* Inform parent that tracer has attached to tracee */
+		CHILD_TO_PARENT("tracer ready", parent_tracer, msg);
+
+		/* Wait for parent to tell use that tracee should have exited */
+		CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg);
+
+		/* Wait for tracee and assert that it exited */
+		FORKEE_REQUIRE_SUCCESS(
+		    wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+		forkee_status_exited(status, exitval_tracee);
+
+		DPRINTF("Before exiting of the tracer process\n");
+		_exit(exitval_tracer);
+	}
+
+	DPRINTF("Wait for the tracer to attach to the tracee\n");
+	PARENT_FROM_CHILD("tracer ready",  parent_tracer, msg);
+
+	DPRINTF("Resume the tracee and let it exit\n");
+	PARENT_TO_CHILD("exit tracee",  parent_tracee, msg);
+
+	DPRINTF("Detect that tracee is zombie\n");
+	await_zombie(tracee);
+
+	DPRINTF("Assert that there is no status about tracee - "
+	    "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(
+	    wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
+
+	DPRINTF("Tell the tracer child should have exited\n");
+	PARENT_TO_CHILD("wait for tracee exit",  parent_tracer, msg);
+
+	DPRINTF("Wait from tracer child to complete waiting for tracee\n");
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
+	    tracer);
+
+	validate_status_exited(status, exitval_tracer);
+
+	DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n",
+	    TWAIT_FNAME);
+	TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG),
+	    tracee);
+
+	validate_status_exited(status, exitval_tracee);
+
+	msg_close(&parent_tracer);
+	msg_close(&parent_tracee);
+}
+
+#define TRACEE_SEES_ITS_ORIGINAL_PARENT(test, type, descr)		\
+ATF_TC(test);								\
+ATF_TC_HEAD(test, tc)							\
+{									\
+	atf_tc_set_md_var(tc, "descr",					\
+	    "Assert that tracee sees its original parent when being traced " \
+	    "(check " descr ")");					\
+}									\
+									\
+ATF_TC_BODY(test, tc)							\
+{									\
+									\
+	tracee_sees_its_original_parent(type);				\
+}
+
+TRACEE_SEES_ITS_ORIGINAL_PARENT(
+	tracee_sees_its_original_parent_getppid,
+	TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID,
+	"getppid(2)");
+TRACEE_SEES_ITS_ORIGINAL_PARENT(
+	tracee_sees_its_original_parent_sysctl_kinfo_proc2,
+	TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2,
+	"sysctl(3) and kinfo_proc2");
+TRACEE_SEES_ITS_ORIGINAL_PARENT(
+	tracee_sees_its_original_parent_procfs_status,
+	TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS,
+	"the status file in procfs");
+#endif
+
+#define ATF_TP_ADD_TCS_PTRACE_WAIT_TOPOLOGY() \
+	ATF_TP_ADD_TC(tp, traceme_pid1_parent); \
+	ATF_TP_ADD_TC_HAVE_PID(tp, tracer_sees_terminaton_before_the_parent); \
+	ATF_TP_ADD_TC_HAVE_PID(tp, tracer_sysctl_lookup_without_duplicates); \
+	ATF_TP_ADD_TC_HAVE_PID(tp, \
+		unrelated_tracer_sees_terminaton_before_the_parent); \
+	ATF_TP_ADD_TC_HAVE_PID(tp, tracer_attach_to_unrelated_stopped_process); \
+	ATF_TP_ADD_TC(tp, parent_attach_to_its_child); \
+	ATF_TP_ADD_TC(tp, parent_attach_to_its_stopped_child); \
+	ATF_TP_ADD_TC(tp, child_attach_to_its_parent); \
+	ATF_TP_ADD_TC(tp, child_attach_to_its_stopped_parent); \
+	ATF_TP_ADD_TC_HAVE_PID(tp, \
+		tracee_sees_its_original_parent_getppid); \
+	ATF_TP_ADD_TC_HAVE_PID(tp, \
+		tracee_sees_its_original_parent_sysctl_kinfo_proc2); \
+	ATF_TP_ADD_TC_HAVE_PID(tp, \
+		tracee_sees_its_original_parent_procfs_status);

Reply via email to