Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/tests/lib/libc/sys Move topology tests out of t_ptrace_wait....



details:   https://anonhg.NetBSD.org/src/rev/132c385bb59e
branches:  trunk
changeset: 932321:132c385bb59e
user:      kamil <kamil%NetBSD.org@localhost>
date:      Tue May 05 00:33:37 2020 +0000

description:
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.

diffstat:

 tests/lib/libc/sys/t_ptrace_topology_wait.h |  721 ++++++++++++++++++++++++++++
 tests/lib/libc/sys/t_ptrace_wait.c          |  704 +---------------------------
 2 files changed, 725 insertions(+), 700 deletions(-)

diffs (truncated from 1471 to 300 lines):

diff -r 1856595f2e21 -r 132c385bb59e tests/lib/libc/sys/t_ptrace_topology_wait.h
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/lib/libc/sys/t_ptrace_topology_wait.h       Tue May 05 00:33:37 2020 +0000
@@ -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);



Home | Main Index | Thread Index | Old Index