aboutsummaryrefslogtreecommitdiff
path: root/tests/sys/kern/ptrace_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sys/kern/ptrace_test.c')
-rw-r--r--tests/sys/kern/ptrace_test.c126
1 files changed, 126 insertions, 0 deletions
diff --git a/tests/sys/kern/ptrace_test.c b/tests/sys/kern/ptrace_test.c
index be978edf06d4..dede0f84f4aa 100644
--- a/tests/sys/kern/ptrace_test.c
+++ b/tests/sys/kern/ptrace_test.c
@@ -1549,6 +1549,130 @@ ATF_TC_BODY(ptrace__event_mask, tc)
ATF_REQUIRE(errno == ECHILD);
}
+/*
+ * Verify that the expected ptrace events are reported for PTRACE_VFORK.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__ptrace_vfork);
+ATF_TC_BODY(ptrace__ptrace_vfork, tc)
+{
+ struct ptrace_lwpinfo pl;
+ pid_t fpid, wpid;
+ int events, status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ follow_fork_parent(true);
+ }
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(fpid, &status, 0);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+ events |= PTRACE_VFORK;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events,
+ sizeof(events)) == 0);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) != -1);
+
+ /* The next event should report the end of the vfork. */
+ wpid = wait(&status);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP);
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
+ ATF_REQUIRE((pl.pl_flags & PL_FLAG_VFORK_DONE) != 0);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) != -1);
+
+ wpid = wait(&status);
+ ATF_REQUIRE(wpid == fpid);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 1);
+
+ wpid = wait(&status);
+ ATF_REQUIRE(wpid == -1);
+ ATF_REQUIRE(errno == ECHILD);
+}
+
+ATF_TC_WITHOUT_HEAD(ptrace__ptrace_vfork_follow);
+ATF_TC_BODY(ptrace__ptrace_vfork_follow, tc)
+{
+ struct ptrace_lwpinfo pl[2];
+ pid_t children[2], fpid, wpid;
+ int events, status;
+
+ ATF_REQUIRE((fpid = fork()) != -1);
+ if (fpid == 0) {
+ trace_me();
+ follow_fork_parent(true);
+ }
+
+ /* Parent process. */
+ children[0] = fpid;
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(children[0], &status, 0);
+ ATF_REQUIRE(wpid == children[0]);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
+
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, children[0], (caddr_t)&events,
+ sizeof(events)) == 0);
+ events |= PTRACE_FORK | PTRACE_VFORK;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, children[0], (caddr_t)&events,
+ sizeof(events)) == 0);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+
+ /* Wait for both halves of the fork event to get reported. */
+ children[1] = handle_fork_events(children[0], pl);
+ ATF_REQUIRE(children[1] > 0);
+
+ ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_VFORKED) != 0);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1);
+
+ /*
+ * The child can't exit until the grandchild reports status, so the
+ * grandchild should report its exit first to the debugger.
+ */
+ wpid = waitpid(children[1], &status, 0);
+ ATF_REQUIRE(wpid == children[1]);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 2);
+
+ /*
+ * The child should report it's vfork() completion before it
+ * exits.
+ */
+ wpid = wait(&status);
+ ATF_REQUIRE(wpid == children[0]);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP);
+ ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl[0], sizeof(pl[0])) !=
+ -1);
+ ATF_REQUIRE((pl[0].pl_flags & PL_FLAG_VFORK_DONE) != 0);
+
+ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1);
+
+ wpid = wait(&status);
+ ATF_REQUIRE(wpid == children[0]);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 1);
+
+ wpid = wait(&status);
+ ATF_REQUIRE(wpid == -1);
+ ATF_REQUIRE(errno == ECHILD);
+}
+
ATF_TP_ADD_TCS(tp)
{
@@ -1574,6 +1698,8 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_disable);
ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_enable);
ATF_TP_ADD_TC(tp, ptrace__event_mask);
+ ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork);
+ ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork_follow);
return (atf_no_error());
}