/*- * Copyright (c) 2015 John Baldwin * 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 AUTHOR 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 AUTHOR 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * A variant of ATF_REQUIRE that is suitable for use in child * processes. This only works if the parent process is tripped up by * the early exit and fails some requirement itself. */ #define CHILD_REQUIRE(exp) do { \ if (!(exp)) \ child_fail_require(__FILE__, __LINE__, \ #exp " not met"); \ } while (0) static __dead2 void child_fail_require(const char *file, int line, const char *str) { char buf[128]; snprintf(buf, sizeof(buf), "%s:%d: %s\n", file, line, str); write(2, buf, strlen(buf)); _exit(32); } static void trace_me(void) { /* Attach the parent process as a tracer of this process. */ CHILD_REQUIRE(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); /* Trigger a stop. */ raise(SIGSTOP); } static void attach_child(pid_t pid) { pid_t wpid; int status; ATF_REQUIRE(ptrace(PT_ATTACH, pid, NULL, 0) == 0); wpid = waitpid(pid, &status, 0); ATF_REQUIRE(wpid == pid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); } static void wait_for_zombie(pid_t pid) { /* * Wait for a process to exit. This is kind of gross, but * there is not a better way. */ for (;;) { struct kinfo_proc kp; size_t len; int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; mib[3] = pid; len = sizeof(kp); if (sysctl(mib, nitems(mib), &kp, &len, NULL, 0) == -1) { /* The KERN_PROC_PID sysctl fails for zombies. */ ATF_REQUIRE(errno == ESRCH); break; } usleep(5000); } } /* * Verify that a parent debugger process "sees" the exit of a debugged * process exactly once when attached via PT_TRACE_ME. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_trace_me); ATF_TC_BODY(ptrace__parent_wait_after_trace_me, tc) { pid_t child, wpid; int status; ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ trace_me(); _exit(1); } /* Parent process. */ /* The first wait() should report the stop from SIGSTOP. */ wpid = waitpid(child, &status, 0); ATF_REQUIRE(wpid == child); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* The second wait() should report the exit status. */ wpid = waitpid(child, &status, 0); ATF_REQUIRE(wpid == child); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); /* The child should no longer exist. */ wpid = waitpid(child, &status, 0); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a parent debugger process "sees" the exit of a debugged * process exactly once when attached via PT_ATTACH. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_attach); ATF_TC_BODY(ptrace__parent_wait_after_attach, tc) { pid_t child, wpid; int cpipe[2], status; char c; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ close(cpipe[0]); /* Wait for the parent to attach. */ CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == 0); _exit(1); } close(cpipe[1]); /* Parent process. */ /* Attach to the child process. */ attach_child(child); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* Signal the child to exit. */ close(cpipe[0]); /* The second wait() should report the exit status. */ wpid = waitpid(child, &status, 0); ATF_REQUIRE(wpid == child); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); /* The child should no longer exist. */ wpid = waitpid(child, &status, 0); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a parent process "sees" the exit of a debugged process only * after the debugger has seen it. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_child_debugger); ATF_TC_BODY(ptrace__parent_sees_exit_after_child_debugger, tc) { pid_t child, debugger, wpid; int cpipe[2], dpipe[2], status; char c; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ close(cpipe[0]); /* Wait for parent to be ready. */ CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c)); _exit(1); } close(cpipe[1]); ATF_REQUIRE(pipe(dpipe) == 0); ATF_REQUIRE((debugger = fork()) != -1); if (debugger == 0) { /* Debugger process. */ close(dpipe[0]); CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1); wpid = waitpid(child, &status, 0); CHILD_REQUIRE(wpid == child); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP); CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* Signal parent that debugger is attached. */ CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c)); /* Wait for parent's failed wait. */ CHILD_REQUIRE(read(dpipe[1], &c, sizeof(c)) == 0); wpid = waitpid(child, &status, 0); CHILD_REQUIRE(wpid == child); CHILD_REQUIRE(WIFEXITED(status)); CHILD_REQUIRE(WEXITSTATUS(status) == 1); _exit(0); } close(dpipe[1]); /* Parent process. */ /* Wait for the debugger to attach to the child. */ ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c)); /* Release the child. */ ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c)); ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0); close(cpipe[0]); wait_for_zombie(child); /* * This wait should return a pid of 0 to indicate no status to * report. The parent should see the child as non-exited * until the debugger sees the exit. */ wpid = waitpid(child, &status, WNOHANG); ATF_REQUIRE(wpid == 0); /* Signal the debugger to wait for the child. */ close(dpipe[0]); /* Wait for the debugger. */ wpid = waitpid(debugger, &status, 0); ATF_REQUIRE(wpid == debugger); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); /* The child process should now be ready. */ wpid = waitpid(child, &status, WNOHANG); ATF_REQUIRE(wpid == child); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); } /* * Verify that a parent process "sees" the exit of a debugged process * only after a non-direct-child debugger has seen it. In particular, * various wait() calls in the parent must avoid failing with ESRCH by * checking the parent's orphan list for the debugee. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_unrelated_debugger); ATF_TC_BODY(ptrace__parent_sees_exit_after_unrelated_debugger, tc) { pid_t child, debugger, fpid, wpid; int cpipe[2], dpipe[2], status; char c; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ close(cpipe[0]); /* Wait for parent to be ready. */ CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c)); _exit(1); } close(cpipe[1]); ATF_REQUIRE(pipe(dpipe) == 0); ATF_REQUIRE((debugger = fork()) != -1); if (debugger == 0) { /* Debugger parent. */ /* * Fork again and drop the debugger parent so that the * debugger is not a child of the main parent. */ CHILD_REQUIRE((fpid = fork()) != -1); if (fpid != 0) _exit(2); /* Debugger process. */ close(dpipe[0]); CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1); wpid = waitpid(child, &status, 0); CHILD_REQUIRE(wpid == child); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP); CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* Signal parent that debugger is attached. */ CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c)); /* Wait for parent's failed wait. */ CHILD_REQUIRE(read(dpipe[1], &c, sizeof(c)) == sizeof(c)); wpid = waitpid(child, &status, 0); CHILD_REQUIRE(wpid == child); CHILD_REQUIRE(WIFEXITED(status)); CHILD_REQUIRE(WEXITSTATUS(status) == 1); _exit(0); } close(dpipe[1]); /* Parent process. */ /* Wait for the debugger parent process to exit. */ wpid = waitpid(debugger, &status, 0); ATF_REQUIRE(wpid == debugger); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); /* A WNOHANG wait here should see the non-exited child. */ wpid = waitpid(child, &status, WNOHANG); ATF_REQUIRE(wpid == 0); /* Wait for the debugger to attach to the child. */ ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c)); /* Release the child. */ ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c)); ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0); close(cpipe[0]); wait_for_zombie(child); /* * This wait should return a pid of 0 to indicate no status to * report. The parent should see the child as non-exited * until the debugger sees the exit. */ wpid = waitpid(child, &status, WNOHANG); ATF_REQUIRE(wpid == 0); /* Signal the debugger to wait for the child. */ ATF_REQUIRE(write(dpipe[0], &c, sizeof(c)) == sizeof(c)); /* Wait for the debugger. */ ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == 0); close(dpipe[0]); /* The child process should now be ready. */ wpid = waitpid(child, &status, WNOHANG); ATF_REQUIRE(wpid == child); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); } /* * The parent process should always act the same regardless of how the * debugger is attached to it. */ static __dead2 void follow_fork_parent(bool use_vfork) { pid_t fpid, wpid; int status; if (use_vfork) CHILD_REQUIRE((fpid = vfork()) != -1); else CHILD_REQUIRE((fpid = fork()) != -1); if (fpid == 0) /* Child */ _exit(2); wpid = waitpid(fpid, &status, 0); CHILD_REQUIRE(wpid == fpid); CHILD_REQUIRE(WIFEXITED(status)); CHILD_REQUIRE(WEXITSTATUS(status) == 2); _exit(1); } /* * Helper routine for follow fork tests. This waits for two stops * that report both "sides" of a fork. It returns the pid of the new * child process. */ static pid_t handle_fork_events(pid_t parent, struct ptrace_lwpinfo *ppl) { struct ptrace_lwpinfo pl; bool fork_reported[2]; pid_t child, wpid; int i, status; fork_reported[0] = false; fork_reported[1] = false; child = -1; /* * Each process should report a fork event. The parent should * report a PL_FLAG_FORKED event, and the child should report * a PL_FLAG_CHILD event. */ for (i = 0; i < 2; i++) { wpid = wait(&status); ATF_REQUIRE(wpid > 0); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_FORKED | PL_FLAG_CHILD)) != 0); ATF_REQUIRE((pl.pl_flags & (PL_FLAG_FORKED | PL_FLAG_CHILD)) != (PL_FLAG_FORKED | PL_FLAG_CHILD)); if (pl.pl_flags & PL_FLAG_CHILD) { ATF_REQUIRE(wpid != parent); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); ATF_REQUIRE(!fork_reported[1]); if (child == -1) child = wpid; else ATF_REQUIRE(child == wpid); if (ppl != NULL) ppl[1] = pl; fork_reported[1] = true; } else { ATF_REQUIRE(wpid == parent); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(!fork_reported[0]); if (child == -1) child = pl.pl_child_pid; else ATF_REQUIRE(child == pl.pl_child_pid); if (ppl != NULL) ppl[0] = pl; fork_reported[0] = true; } } return (child); } /* * Verify that a new child process is stopped after a followed fork and * that the traced parent sees the exit of the child after the debugger * when both processes remain attached to the debugger. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_both_attached); ATF_TC_BODY(ptrace__follow_fork_both_attached, tc) { pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(false); } /* 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_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 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 = wait(&status); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); 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); } /* * Verify that a new child process is stopped after a followed fork * and that the traced parent sees the exit of the child when the new * child process is detached after it reports its fork. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_child_detached); ATF_TC_BODY(ptrace__follow_fork_child_detached, tc) { pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(false); } /* 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_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_DETACH, children[1], (caddr_t)1, 0) != -1); /* * Should not see any status from the grandchild now, only the * child. */ 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); } /* * Verify that a new child process is stopped after a followed fork * and that the traced parent sees the exit of the child when the * traced parent is detached after the fork. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_parent_detached); ATF_TC_BODY(ptrace__follow_fork_parent_detached, tc) { pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(false); } /* 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_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_DETACH, 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. * * Even though the child process is detached, it is still a * child of the debugger, so it will still report it's exit * after the grandchild. */ wpid = wait(&status); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); 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); } static void attach_fork_parent(int cpipe[2]) { pid_t fpid; close(cpipe[0]); /* Double-fork to disassociate from the debugger. */ CHILD_REQUIRE((fpid = fork()) != -1); if (fpid != 0) _exit(3); /* Send the pid of the disassociated child to the debugger. */ fpid = getpid(); CHILD_REQUIRE(write(cpipe[1], &fpid, sizeof(fpid)) == sizeof(fpid)); /* Wait for the debugger to attach. */ CHILD_REQUIRE(read(cpipe[1], &fpid, sizeof(fpid)) == 0); } /* * Verify that a new child process is stopped after a followed fork and * that the traced parent sees the exit of the child after the debugger * when both processes remain attached to the debugger. In this test * the parent that forks is not a direct child of the debugger. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_both_attached_unrelated_debugger); ATF_TC_BODY(ptrace__follow_fork_both_attached_unrelated_debugger, tc) { pid_t children[2], fpid, wpid; int cpipe[2], status; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { attach_fork_parent(cpipe); follow_fork_parent(false); } /* Parent process. */ close(cpipe[1]); /* Wait for the direct child to exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 3); /* Read the pid of the fork parent. */ ATF_REQUIRE(read(cpipe[0], &children[0], sizeof(children[0])) == sizeof(children[0])); /* Attach to the fork parent. */ attach_child(children[0]); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the fork parent ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Signal the fork parent to continue. */ close(cpipe[0]); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 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 fork parent can't exit until the child reports status, * so the child should report its exit first to the debugger. */ wpid = wait(&status); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); 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); } /* * Verify that a new child process is stopped after a followed fork * and that the traced parent sees the exit of the child when the new * child process is detached after it reports its fork. In this test * the parent that forks is not a direct child of the debugger. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_child_detached_unrelated_debugger); ATF_TC_BODY(ptrace__follow_fork_child_detached_unrelated_debugger, tc) { pid_t children[2], fpid, wpid; int cpipe[2], status; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { attach_fork_parent(cpipe); follow_fork_parent(false); } /* Parent process. */ close(cpipe[1]); /* Wait for the direct child to exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 3); /* Read the pid of the fork parent. */ ATF_REQUIRE(read(cpipe[0], &children[0], sizeof(children[0])) == sizeof(children[0])); /* Attach to the fork parent. */ attach_child(children[0]); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the fork parent ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Signal the fork parent to continue. */ close(cpipe[0]); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_DETACH, children[1], (caddr_t)1, 0) != -1); /* * Should not see any status from the child now, only the fork * parent. */ 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); } /* * Verify that a new child process is stopped after a followed fork * and that the traced parent sees the exit of the child when the * traced parent is detached after the fork. In this test the parent * that forks is not a direct child of the debugger. */ ATF_TC_WITHOUT_HEAD(ptrace__follow_fork_parent_detached_unrelated_debugger); ATF_TC_BODY(ptrace__follow_fork_parent_detached_unrelated_debugger, tc) { pid_t children[2], fpid, wpid; int cpipe[2], status; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { attach_fork_parent(cpipe); follow_fork_parent(false); } /* Parent process. */ close(cpipe[1]); /* Wait for the direct child to exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 3); /* Read the pid of the fork parent. */ ATF_REQUIRE(read(cpipe[0], &children[0], sizeof(children[0])) == sizeof(children[0])); /* Attach to the fork parent. */ attach_child(children[0]); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, children[0], NULL, 1) != -1); /* Continue the fork parent ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, children[0], (caddr_t)1, 0) != -1); /* Signal the fork parent to continue. */ close(cpipe[0]); children[1] = handle_fork_events(children[0], NULL); ATF_REQUIRE(children[1] > 0); ATF_REQUIRE(ptrace(PT_DETACH, children[0], (caddr_t)1, 0) != -1); ATF_REQUIRE(ptrace(PT_CONTINUE, children[1], (caddr_t)1, 0) != -1); /* * Should not see any status from the fork parent now, only * the child. */ wpid = wait(&status); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a child process does not see an unrelated debugger as its * parent but sees its original parent process. */ ATF_TC_WITHOUT_HEAD(ptrace__getppid); ATF_TC_BODY(ptrace__getppid, tc) { pid_t child, debugger, ppid, wpid; int cpipe[2], dpipe[2], status; char c; ATF_REQUIRE(pipe(cpipe) == 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { /* Child process. */ close(cpipe[0]); /* Wait for parent to be ready. */ CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c)); /* Report the parent PID to the parent. */ ppid = getppid(); CHILD_REQUIRE(write(cpipe[1], &ppid, sizeof(ppid)) == sizeof(ppid)); _exit(1); } close(cpipe[1]); ATF_REQUIRE(pipe(dpipe) == 0); ATF_REQUIRE((debugger = fork()) != -1); if (debugger == 0) { /* Debugger process. */ close(dpipe[0]); CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1); wpid = waitpid(child, &status, 0); CHILD_REQUIRE(wpid == child); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP); CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); /* Signal parent that debugger is attached. */ CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c)); /* Wait for traced child to exit. */ wpid = waitpid(child, &status, 0); CHILD_REQUIRE(wpid == child); CHILD_REQUIRE(WIFEXITED(status)); CHILD_REQUIRE(WEXITSTATUS(status) == 1); _exit(0); } close(dpipe[1]); /* Parent process. */ /* Wait for the debugger to attach to the child. */ ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c)); /* Release the child. */ ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c)); /* Read the parent PID from the child. */ ATF_REQUIRE(read(cpipe[0], &ppid, sizeof(ppid)) == sizeof(ppid)); close(cpipe[0]); ATF_REQUIRE(ppid == getpid()); /* Wait for the debugger. */ wpid = waitpid(debugger, &status, 0); ATF_REQUIRE(wpid == debugger); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); /* The child process should now be ready. */ wpid = waitpid(child, &status, WNOHANG); ATF_REQUIRE(wpid == child); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); } /* * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new * child process created via fork() reports the correct value. */ ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_fork); ATF_TC_BODY(ptrace__new_child_pl_syscall_code_fork, tc) { struct ptrace_lwpinfo pl[2]; pid_t children[2], fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); follow_fork_parent(false); } /* 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_FOLLOW_FORK, children[0], NULL, 1) != -1); /* 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_SCX) != 0); ATF_REQUIRE((pl[1].pl_flags & PL_FLAG_SCX) != 0); ATF_REQUIRE(pl[0].pl_syscall_code == SYS_fork); ATF_REQUIRE(pl[0].pl_syscall_code == pl[1].pl_syscall_code); ATF_REQUIRE(pl[0].pl_syscall_narg == pl[1].pl_syscall_narg); 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 = wait(&status); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); 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); } /* * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new * child process created via vfork() reports the correct value. */ ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_vfork); ATF_TC_BODY(ptrace__new_child_pl_syscall_code_vfork, tc) { struct ptrace_lwpinfo pl[2]; pid_t children[2], fpid, wpid; int 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_FOLLOW_FORK, children[0], NULL, 1) != -1); /* 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_SCX) != 0); ATF_REQUIRE((pl[1].pl_flags & PL_FLAG_SCX) != 0); ATF_REQUIRE(pl[0].pl_syscall_code == SYS_vfork); ATF_REQUIRE(pl[0].pl_syscall_code == pl[1].pl_syscall_code); ATF_REQUIRE(pl[0].pl_syscall_narg == pl[1].pl_syscall_narg); 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 = wait(&status); ATF_REQUIRE(wpid == children[1]); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); 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); } static void * simple_thread(void *arg __unused) { pthread_exit(NULL); } static __dead2 void simple_thread_main(void) { pthread_t thread; CHILD_REQUIRE(pthread_create(&thread, NULL, simple_thread, NULL) == 0); CHILD_REQUIRE(pthread_join(thread, NULL) == 0); exit(1); } /* * Verify that pl_syscall_code in struct ptrace_lwpinfo for a new * thread reports the correct value. */ ATF_TC_WITHOUT_HEAD(ptrace__new_child_pl_syscall_code_thread); ATF_TC_BODY(ptrace__new_child_pl_syscall_code_thread, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t mainlwp; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); simple_thread_main(); } /* 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_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); mainlwp = pl.pl_lwpid; /* * Continue the child ignoring the SIGSTOP and tracing all * system call exits. */ ATF_REQUIRE(ptrace(PT_TO_SCX, fpid, (caddr_t)1, 0) != -1); /* * Wait for the new thread to arrive. pthread_create() might * invoke any number of system calls. For now we just wait * for the new thread to arrive and make sure it reports a * valid system call code. If ptrace grows thread event * reporting then this test can be made more precise. */ for (;;) { wpid = waitpid(fpid, &status, 0); 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_SCX) != 0); ATF_REQUIRE(pl.pl_syscall_code != 0); if (pl.pl_lwpid != mainlwp) /* New thread seen. */ break; ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } /* Wait for the child to exit. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); for (;;) { wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); if (WIFEXITED(status)) break; ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that the expected LWP events are reported for a child thread. */ ATF_TC_WITHOUT_HEAD(ptrace__lwp_events); ATF_TC_BODY(ptrace__lwp_events, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t lwps[2]; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); simple_thread_main(); } /* 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_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); lwps[0] = pl.pl_lwpid; ATF_REQUIRE(ptrace(PT_LWP_EVENTS, wpid, NULL, 1) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The first event should be for the child thread's birth. */ wpid = waitpid(fpid, &status, 0); 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_BORN | PL_FLAG_SCX)) == (PL_FLAG_BORN | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid != lwps[0]); lwps[1] = pl.pl_lwpid; ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next event should be for the child thread's death. */ wpid = waitpid(fpid, &status, 0); 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_EXITED | PL_FLAG_SCE)) == (PL_FLAG_EXITED | PL_FLAG_SCE)); ATF_REQUIRE(pl.pl_lwpid == lwps[1]); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void * exec_thread(void *arg __unused) { execl("/usr/bin/true", "true", NULL); exit(127); } static __dead2 void exec_thread_main(void) { pthread_t thread; CHILD_REQUIRE(pthread_create(&thread, NULL, exec_thread, NULL) == 0); for (;;) sleep(60); exit(1); } /* * Verify that the expected LWP events are reported for a multithreaded * process that calls execve(2). */ ATF_TC_WITHOUT_HEAD(ptrace__lwp_events_exec); ATF_TC_BODY(ptrace__lwp_events_exec, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t lwps[2]; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exec_thread_main(); } /* 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_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); lwps[0] = pl.pl_lwpid; ATF_REQUIRE(ptrace(PT_LWP_EVENTS, wpid, NULL, 1) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The first event should be for the child thread's birth. */ wpid = waitpid(fpid, &status, 0); 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_BORN | PL_FLAG_SCX)) == (PL_FLAG_BORN | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid != lwps[0]); lwps[1] = pl.pl_lwpid; ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* * The next event should be for the main thread's death due to * single threading from execve(). */ wpid = waitpid(fpid, &status, 0); 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_EXITED | PL_FLAG_SCE)) == (PL_FLAG_EXITED)); ATF_REQUIRE(pl.pl_lwpid == lwps[0]); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next event should be for the child process's exec. */ wpid = waitpid(fpid, &status, 0); 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_EXEC | PL_FLAG_SCX)) == (PL_FLAG_EXEC | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid == lwps[1]); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void handler(int sig __unused) { } static void signal_main(void) { signal(SIGINFO, handler); raise(SIGINFO); exit(0); } /* * Verify that the expected ptrace event is reported for a signal. */ ATF_TC_WITHOUT_HEAD(ptrace__siginfo); ATF_TC_BODY(ptrace__siginfo, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); signal_main(); } /* 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_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next event should be for the SIGINFO. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGINFO); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_event == PL_EVENT_SIGNAL); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); ATF_REQUIRE(pl.pl_siginfo.si_code == SI_LWP); ATF_REQUIRE(pl.pl_siginfo.si_pid == wpid); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that the expected ptrace events are reported for PTRACE_EXEC. */ ATF_TC_WITHOUT_HEAD(ptrace__ptrace_exec_disable); ATF_TC_BODY(ptrace__ptrace_exec_disable, tc) { pid_t fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exec_thread(NULL); } /* 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); events = 0; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* Should get one event at exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } ATF_TC_WITHOUT_HEAD(ptrace__ptrace_exec_enable); ATF_TC_BODY(ptrace__ptrace_exec_enable, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exec_thread(NULL); } /* 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); events = PTRACE_EXEC; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next event should be for the child process's exec. */ wpid = waitpid(fpid, &status, 0); 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_EXEC | PL_FLAG_SCX)) == (PL_FLAG_EXEC | PL_FLAG_SCX)); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } ATF_TC_WITHOUT_HEAD(ptrace__event_mask); ATF_TC_BODY(ptrace__event_mask, tc) { pid_t fpid, wpid; int events, status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exit(0); } /* 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); /* PT_FOLLOW_FORK should toggle the state of PTRACE_FORK. */ ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, fpid, NULL, 1) != -1); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(events & PTRACE_FORK); ATF_REQUIRE(ptrace(PT_FOLLOW_FORK, fpid, NULL, 0) != -1); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(!(events & PTRACE_FORK)); /* PT_LWP_EVENTS should toggle the state of PTRACE_LWP. */ ATF_REQUIRE(ptrace(PT_LWP_EVENTS, fpid, NULL, 1) != -1); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(events & PTRACE_LWP); ATF_REQUIRE(ptrace(PT_LWP_EVENTS, fpid, NULL, 0) != -1); ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, fpid, (caddr_t)&events, sizeof(events)) == 0); ATF_REQUIRE(!(events & PTRACE_LWP)); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* Should get one event at exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); wpid = wait(&status); ATF_REQUIRE(wpid == -1); 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); } /* * XXX: There's nothing inherently platform specific about this test, however a * userspace visible breakpoint() is a prerequisite. */ #if defined(__amd64__) || defined(__i386__) || defined(__sparc64__) /* * Verify that no more events are reported after PT_KILL except for the * process exit when stopped due to a breakpoint trap. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_breakpoint); ATF_TC_BODY(ptrace__PT_KILL_breakpoint, tc) { pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); breakpoint(); exit(1); } /* 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); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report hitting the breakpoint. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGTRAP); /* Kill the child process. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } #endif /* defined(__amd64__) || defined(__i386__) || defined(__sparc64__) */ /* * Verify that no more events are reported after PT_KILL except for the * process exit when stopped inside of a system call. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_system_call); ATF_TC_BODY(ptrace__PT_KILL_system_call, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* 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); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); 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_SCE); /* Kill the child process. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that no more events are reported after PT_KILL except for the * process exit when killing a multithreaded process. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_threads); ATF_TC_BODY(ptrace__PT_KILL_threads, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; lwpid_t main_lwp; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); simple_thread_main(); } /* 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_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); main_lwp = pl.pl_lwpid; ATF_REQUIRE(ptrace(PT_LWP_EVENTS, wpid, NULL, 1) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The first event should be for the child thread's birth. */ wpid = waitpid(fpid, &status, 0); 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_BORN | PL_FLAG_SCX)) == (PL_FLAG_BORN | PL_FLAG_SCX)); ATF_REQUIRE(pl.pl_lwpid != main_lwp); /* Kill the child process. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void * mask_usr1_thread(void *arg) { pthread_barrier_t *pbarrier; sigset_t sigmask; pbarrier = (pthread_barrier_t*)arg; sigemptyset(&sigmask); sigaddset(&sigmask, SIGUSR1); CHILD_REQUIRE(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == 0); /* Sync up with other thread after sigmask updated. */ pthread_barrier_wait(pbarrier); for (;;) sleep(60); return (NULL); } /* * Verify that the SIGKILL from PT_KILL takes priority over other signals * and prevents spurious stops due to those other signals. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_competing_signal); ATF_TC_BODY(ptrace__PT_KILL_competing_signal, tc) { pid_t fpid, wpid; int status; cpuset_t setmask; pthread_t t; pthread_barrier_t barrier; struct sched_param sched_param; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { /* Bind to one CPU so only one thread at a time will run. */ CPU_ZERO(&setmask); CPU_SET(0, &setmask); cpusetid_t setid; CHILD_REQUIRE(cpuset(&setid) == 0); CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET, setid, sizeof(setmask), &setmask) == 0); CHILD_REQUIRE(pthread_barrier_init(&barrier, NULL, 2) == 0); CHILD_REQUIRE(pthread_create(&t, NULL, mask_usr1_thread, (void*)&barrier) == 0); /* * Give the main thread higher priority. The test always * assumes that, if both threads are able to run, the main * thread runs first. */ sched_param.sched_priority = (sched_get_priority_max(SCHED_FIFO) + sched_get_priority_min(SCHED_FIFO)) / 2; CHILD_REQUIRE(pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched_param) == 0); sched_param.sched_priority -= RQ_PPQ; CHILD_REQUIRE(pthread_setschedparam(t, SCHED_FIFO, &sched_param) == 0); sigset_t sigmask; sigemptyset(&sigmask); sigaddset(&sigmask, SIGUSR2); CHILD_REQUIRE(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == 0); /* Sync up with other thread after sigmask updated. */ pthread_barrier_wait(&barrier); trace_me(); for (;;) sleep(60); exit(1); } /* 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); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* Send a signal that only the second thread can handle. */ ATF_REQUIRE(kill(fpid, SIGUSR2) == 0); /* The second wait() should report the SIGUSR2. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGUSR2); /* Send a signal that only the first thread can handle. */ ATF_REQUIRE(kill(fpid, SIGUSR1) == 0); /* Replace the SIGUSR2 with a kill. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL (not the SIGUSR signal). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that the SIGKILL from PT_KILL takes priority over other stop events * and prevents spurious stops caused by those events. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_competing_stop); ATF_TC_BODY(ptrace__PT_KILL_competing_stop, tc) { pid_t fpid, wpid; int status; cpuset_t setmask; pthread_t t; pthread_barrier_t barrier; lwpid_t main_lwp; struct ptrace_lwpinfo pl; struct sched_param sched_param; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); /* Bind to one CPU so only one thread at a time will run. */ CPU_ZERO(&setmask); CPU_SET(0, &setmask); cpusetid_t setid; CHILD_REQUIRE(cpuset(&setid) == 0); CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET, setid, sizeof(setmask), &setmask) == 0); CHILD_REQUIRE(pthread_barrier_init(&barrier, NULL, 2) == 0); CHILD_REQUIRE(pthread_create(&t, NULL, mask_usr1_thread, (void*)&barrier) == 0); /* * Give the main thread higher priority. The test always * assumes that, if both threads are able to run, the main * thread runs first. */ sched_param.sched_priority = (sched_get_priority_max(SCHED_FIFO) + sched_get_priority_min(SCHED_FIFO)) / 2; CHILD_REQUIRE(pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched_param) == 0); sched_param.sched_priority -= RQ_PPQ; CHILD_REQUIRE(pthread_setschedparam(t, SCHED_FIFO, &sched_param) == 0); sigset_t sigmask; sigemptyset(&sigmask); sigaddset(&sigmask, SIGUSR2); CHILD_REQUIRE(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == 0); /* Sync up with other thread after sigmask updated. */ pthread_barrier_wait(&barrier); /* Sync up with the test before doing the getpid(). */ raise(SIGSTOP); getpid(); exit(1); } /* 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_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); main_lwp = pl.pl_lwpid; /* Continue the child ignoring the SIGSTOP and tracing system calls. */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* * Continue until child is done with setup, which is indicated with * SIGSTOP. Ignore system calls in the meantime. */ for (;;) { wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); if (WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); } else { ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); break; } ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); } /* Proceed, allowing main thread to hit syscall entry for getpid(). */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); wpid = waitpid(fpid, &status, 0); 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_lwpid == main_lwp); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCE); /* Prevent the main thread from hitting its syscall exit for now. */ ATF_REQUIRE(ptrace(PT_SUSPEND, main_lwp, 0, 0) == 0); /* * Proceed, allowing second thread to hit syscall exit for * pthread_barrier_wait(). */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); wpid = waitpid(fpid, &status, 0); 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_lwpid != main_lwp); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SCX); /* Send a signal that only the second thread can handle. */ ATF_REQUIRE(kill(fpid, SIGUSR2) == 0); ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* The next wait() should report the SIGUSR2. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGUSR2); /* Allow the main thread to try to finish its system call. */ ATF_REQUIRE(ptrace(PT_RESUME, main_lwp, 0, 0) == 0); /* * At this point, the main thread is in the middle of a system call and * has been resumed. The second thread has taken a SIGUSR2 which will * be replaced with a SIGKILL below. The main thread will get to run * first. It should notice the kill request (even though the signal * replacement occurred in the other thread) and exit accordingly. It * should not stop for the system call exit event. */ /* Replace the SIGUSR2 with a kill. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL (not a syscall exit). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void sigusr1_handler(int sig) { CHILD_REQUIRE(sig == SIGUSR1); _exit(2); } /* * Verify that even if the signal queue is full for a child process, * a PT_KILL will kill the process. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_KILL_with_signal_full_sigqueue); ATF_TC_BODY(ptrace__PT_KILL_with_signal_full_sigqueue, tc) { pid_t fpid, wpid; int status; int max_pending_per_proc; size_t len; int i; ATF_REQUIRE(signal(SIGUSR1, sigusr1_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exit(1); } /* 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); len = sizeof(max_pending_per_proc); ATF_REQUIRE(sysctlbyname("kern.sigqueue.max_pending_per_proc", &max_pending_per_proc, &len, NULL, 0) == 0); /* Fill the signal queue. */ for (i = 0; i < max_pending_per_proc; ++i) ATF_REQUIRE(kill(fpid, SIGUSR1) == 0); /* Kill the child process. */ ATF_REQUIRE(ptrace(PT_KILL, fpid, 0, 0) == 0); /* The last wait() should report the SIGKILL. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGKILL); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that when stopped at a system call entry, a signal can be * requested with PT_CONTINUE which will be delivered once the system * call is complete. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_system_call_entry); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_system_call_entry, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE(signal(SIGUSR1, sigusr1_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* 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); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); 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_SCE); /* Continue the child process with a signal. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); for (;;) { /* * The last wait() should report exit 2, i.e., a normal _exit * from the signal handler. In the meantime, catch and proceed * past any syscall stops. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } else { ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); break; } } wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void sigusr1_counting_handler(int sig) { static int counter = 0; CHILD_REQUIRE(sig == SIGUSR1); counter++; if (counter == 2) _exit(2); } /* * Verify that, when continuing from a stop at system call entry and exit, * a signal can be requested from both stops, and both will be delivered when * the system call is complete. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_system_call_entry_and_exit); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_system_call_entry_and_exit, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE(signal(SIGUSR1, sigusr1_counting_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* 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); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); 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_SCE); /* Continue the child process with a signal. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* The third wait() should report a system call exit for getpid(). */ wpid = waitpid(fpid, &status, 0); 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_SCX); /* Continue the child process with a signal. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); for (;;) { /* * The last wait() should report exit 2, i.e., a normal _exit * from the signal handler. In the meantime, catch and proceed * past any syscall stops. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } else { ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); break; } } wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that even if the signal queue is full for a child process, * a PT_CONTINUE with a signal will not result in loss of that signal. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_full_sigqueue); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_full_sigqueue, tc) { pid_t fpid, wpid; int status; int max_pending_per_proc; size_t len; int i; ATF_REQUIRE(signal(SIGUSR2, handler) != SIG_ERR); ATF_REQUIRE(signal(SIGUSR1, sigusr1_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); exit(1); } /* 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); len = sizeof(max_pending_per_proc); ATF_REQUIRE(sysctlbyname("kern.sigqueue.max_pending_per_proc", &max_pending_per_proc, &len, NULL, 0) == 0); /* Fill the signal queue. */ for (i = 0; i < max_pending_per_proc; ++i) ATF_REQUIRE(kill(fpid, SIGUSR2) == 0); /* Continue with signal. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); for (;;) { wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); if (WIFSTOPPED(status)) { ATF_REQUIRE(WSTOPSIG(status) == SIGUSR2); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } else { /* * The last wait() should report normal _exit from the * SIGUSR1 handler. */ ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); break; } } wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that, after stopping due to a signal, that signal can be * replaced with another signal. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_change_sig); ATF_TC_BODY(ptrace__PT_CONTINUE_change_sig, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); sleep(20); exit(1); } /* 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_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* Send a signal without ptrace. */ ATF_REQUIRE(kill(fpid, SIGINT) == 0); /* The second wait() should report a SIGINT was received. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGINT); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGINT); /* Continue the child process with a different signal. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGTERM) == 0); /* * The last wait() should report having died due to the new * signal, SIGTERM. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGTERM); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify that a signal can be passed through to the child even when there * was no true signal originally. Such cases arise when a SIGTRAP is * invented for e.g, system call stops. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_sigtrap_system_call_entry); ATF_TC_BODY(ptrace__PT_CONTINUE_with_sigtrap_system_call_entry, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* 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); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); 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_SCE); /* Continue the child process with a SIGTRAP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGTRAP) == 0); for (;;) { /* * The last wait() should report exit due to SIGTRAP. In the * meantime, catch and proceed past any syscall stops. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } else { ATF_REQUIRE(WIFSIGNALED(status)); ATF_REQUIRE(WTERMSIG(status) == SIGTRAP); break; } } wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * A mixed bag PT_CONTINUE with signal test. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_mix); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_mix, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status; ATF_REQUIRE(signal(SIGUSR1, sigusr1_counting_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); getpid(); exit(1); } /* 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); /* Continue the child ignoring the SIGSTOP and tracing system calls. */ ATF_REQUIRE(ptrace(PT_SYSCALL, fpid, (caddr_t)1, 0) == 0); /* The second wait() should report a system call entry for getpid(). */ wpid = waitpid(fpid, &status, 0); 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_SCE); /* Continue with the first SIGUSR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* The next wait() should report a system call exit for getpid(). */ wpid = waitpid(fpid, &status, 0); 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_SCX); /* Send an ABRT without ptrace. */ ATF_REQUIRE(kill(fpid, SIGABRT) == 0); /* Continue normally. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next wait() should report the SIGABRT. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGABRT); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGABRT); /* Continue, replacing the SIGABRT with another SIGUSR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); for (;;) { /* * The last wait() should report exit 2, i.e., a normal _exit * from the signal handler. In the meantime, catch and proceed * past any syscall stops. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)); ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); } else { ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 2); break; } } wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } /* * Verify a signal delivered by ptrace is noticed by kevent(2). */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_kqueue); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_kqueue, tc) { pid_t fpid, wpid; int status, kq, nevents; struct kevent kev; ATF_REQUIRE(signal(SIGUSR1, SIG_IGN) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { CHILD_REQUIRE((kq = kqueue()) > 0); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); CHILD_REQUIRE(kevent(kq, &kev, 1, NULL, 0, NULL) == 0); trace_me(); for (;;) { nevents = kevent(kq, NULL, 0, &kev, 1, NULL); if (nevents == -1 && errno == EINTR) continue; CHILD_REQUIRE(nevents > 0); CHILD_REQUIRE(kev.filter == EVFILT_SIGNAL); CHILD_REQUIRE(kev.ident == SIGUSR1); break; } exit(1); } /* 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); /* Continue with the SIGUSR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* * The last wait() should report normal exit with code 1. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static sem_t sigusr1_sem; static void sigusr1_sempost_handler(int sig __unused) { CHILD_REQUIRE(sem_post(&sigusr1_sem) == 0); } static void * signal_thread(void *arg) { int err; sigset_t sigmask; pthread_barrier_t *pbarrier = (pthread_barrier_t*)arg; /* Wait for this thread to receive a SIGUSR1. */ do { err = sem_wait(&sigusr1_sem); CHILD_REQUIRE(err == 0 || errno == EINTR); } while (err != 0 && errno == EINTR); /* Free our companion thread from the barrier. */ pthread_barrier_wait(pbarrier); /* * Swap ignore duties; the next SIGUSR1 should go to the * other thread. */ CHILD_REQUIRE(sigemptyset(&sigmask) == 0); CHILD_REQUIRE(sigaddset(&sigmask, SIGUSR1) == 0); CHILD_REQUIRE(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == 0); /* Sync up threads after swapping signal masks. */ pthread_barrier_wait(pbarrier); /* Wait until our companion has received its SIGUSR1. */ pthread_barrier_wait(pbarrier); return (NULL); } /* * Verify that if ptrace stops due to a signal but continues with * a different signal that the new signal is routed to a thread * that can accept it, and that that thread is awakened by the signal * in a timely manner. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_CONTINUE_with_signal_thread_sigmask); ATF_TC_BODY(ptrace__PT_CONTINUE_with_signal_thread_sigmask, tc) { pid_t fpid, wpid; int status, err; pthread_t t; sigset_t sigmask; pthread_barrier_t barrier; ATF_REQUIRE(pthread_barrier_init(&barrier, NULL, 2) == 0); ATF_REQUIRE(sem_init(&sigusr1_sem, 0, 0) == 0); ATF_REQUIRE(signal(SIGUSR1, sigusr1_sempost_handler) != SIG_ERR); ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { CHILD_REQUIRE(pthread_create(&t, NULL, signal_thread, (void*)&barrier) == 0); /* The other thread should receive the first SIGUSR1. */ CHILD_REQUIRE(sigemptyset(&sigmask) == 0); CHILD_REQUIRE(sigaddset(&sigmask, SIGUSR1) == 0); CHILD_REQUIRE(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) == 0); trace_me(); /* Wait until other thread has received its SIGUSR1. */ pthread_barrier_wait(&barrier); /* * Swap ignore duties; the next SIGUSR1 should go to this * thread. */ CHILD_REQUIRE(pthread_sigmask(SIG_UNBLOCK, &sigmask, NULL) == 0); /* Sync up threads after swapping signal masks. */ pthread_barrier_wait(&barrier); /* * Sync up with test code; we're ready for the next SIGUSR1 * now. */ raise(SIGSTOP); /* Wait for this thread to receive a SIGUSR1. */ do { err = sem_wait(&sigusr1_sem); CHILD_REQUIRE(err == 0 || errno == EINTR); } while (err != 0 && errno == EINTR); /* Free the other thread from the barrier. */ pthread_barrier_wait(&barrier); CHILD_REQUIRE(pthread_join(t, NULL) == 0); exit(1); } /* 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); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* * Send a signal without ptrace that either thread will accept (USR2, * in this case). */ ATF_REQUIRE(kill(fpid, SIGUSR2) == 0); /* The second wait() should report a SIGUSR2 was received. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGUSR2); /* Continue the child, changing the signal to USR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* The next 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); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); ATF_REQUIRE(kill(fpid, SIGUSR2) == 0); /* The next wait() should report a SIGUSR2 was received. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGUSR2); /* Continue the child, changing the signal to USR1. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, SIGUSR1) == 0); /* The last wait() should report normal exit with code 1. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 1); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void * raise_sigstop_thread(void *arg __unused) { raise(SIGSTOP); return NULL; } static void * sleep_thread(void *arg __unused) { sleep(60); return NULL; } static void terminate_with_pending_sigstop(bool sigstop_from_main_thread) { pid_t fpid, wpid; int status, i; cpuset_t setmask; cpusetid_t setid; pthread_t t; /* * Become the reaper for this process tree. We need to be able to check * that both child and grandchild have died. */ ATF_REQUIRE(procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) == 0); fpid = fork(); ATF_REQUIRE(fpid >= 0); if (fpid == 0) { fpid = fork(); CHILD_REQUIRE(fpid >= 0); if (fpid == 0) { trace_me(); /* Pin to CPU 0 to serialize thread execution. */ CPU_ZERO(&setmask); CPU_SET(0, &setmask); CHILD_REQUIRE(cpuset(&setid) == 0); CHILD_REQUIRE(cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET, setid, sizeof(setmask), &setmask) == 0); if (sigstop_from_main_thread) { /* * We expect the SIGKILL sent when our parent * dies to be delivered to the new thread. * Raise the SIGSTOP in this thread so the * threads compete. */ CHILD_REQUIRE(pthread_create(&t, NULL, sleep_thread, NULL) == 0); raise(SIGSTOP); } else { /* * We expect the SIGKILL to be delivered to * this thread. After creating the new thread, * just get off the CPU so the other thread can * raise the SIGSTOP. */ CHILD_REQUIRE(pthread_create(&t, NULL, raise_sigstop_thread, NULL) == 0); sleep(60); } exit(0); } /* First stop is trace_me() immediately after fork. */ wpid = waitpid(fpid, &status, 0); CHILD_REQUIRE(wpid == fpid); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP); CHILD_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* Second stop is from the raise(SIGSTOP). */ wpid = waitpid(fpid, &status, 0); CHILD_REQUIRE(wpid == fpid); CHILD_REQUIRE(WIFSTOPPED(status)); CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* * Terminate tracing process without detaching. Our child * should be killed. */ exit(0); } /* * We should get a normal exit from our immediate child and a SIGKILL * exit from our grandchild. The latter case is the interesting one. * Our grandchild should not have stopped due to the SIGSTOP that was * left dangling when its parent died. */ for (i = 0; i < 2; ++i) { wpid = wait(&status); if (wpid == fpid) ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 0); else ATF_REQUIRE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL); } } /* * These two tests ensure that if the tracing process exits without detaching * just after the child received a SIGSTOP, the child is cleanly killed and * doesn't go to sleep due to the SIGSTOP. The parent's death will send a * SIGKILL to the child. If the SIGKILL and the SIGSTOP are handled by * different threads, the SIGKILL must win. There are two variants of this * test, designed to catch the case where the SIGKILL is delivered to the * younger thread (the first test) and the case where the SIGKILL is delivered * to the older thread (the second test). This behavior has changed in the * past, so make no assumption. */ ATF_TC_WITHOUT_HEAD(ptrace__parent_terminate_with_pending_sigstop1); ATF_TC_BODY(ptrace__parent_terminate_with_pending_sigstop1, tc) { terminate_with_pending_sigstop(true); } ATF_TC_WITHOUT_HEAD(ptrace__parent_terminate_with_pending_sigstop2); ATF_TC_BODY(ptrace__parent_terminate_with_pending_sigstop2, tc) { terminate_with_pending_sigstop(false); } /* * Verify that after ptrace() discards a SIGKILL signal, the event mask * is not modified. */ ATF_TC_WITHOUT_HEAD(ptrace__event_mask_sigkill_discard); ATF_TC_BODY(ptrace__event_mask_sigkill_discard, tc) { struct ptrace_lwpinfo pl; pid_t fpid, wpid; int status, event_mask, new_event_mask; ATF_REQUIRE((fpid = fork()) != -1); if (fpid == 0) { trace_me(); raise(SIGSTOP); exit(0); } /* The first wait() should report the stop from trace_me(). */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); /* Set several unobtrusive event bits. */ event_mask = PTRACE_EXEC | PTRACE_FORK | PTRACE_LWP; ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, wpid, (caddr_t)&event_mask, sizeof(event_mask)) == 0); /* Send a SIGKILL without using ptrace. */ ATF_REQUIRE(kill(fpid, SIGKILL) == 0); /* Continue the child ignoring the SIGSTOP. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next stop should be due to the SIGKILL. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(wpid == fpid); ATF_REQUIRE(WIFSTOPPED(status)); ATF_REQUIRE(WSTOPSIG(status) == SIGKILL); ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1); ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI); ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGKILL); /* Continue the child ignoring the SIGKILL. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The next 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); /* Check the current event mask. It should not have changed. */ new_event_mask = 0; ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, wpid, (caddr_t)&new_event_mask, sizeof(new_event_mask)) == 0); ATF_REQUIRE(event_mask == new_event_mask); /* Continue the child to let it exit. */ ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0); /* The last event should be for the child process's exit. */ wpid = waitpid(fpid, &status, 0); ATF_REQUIRE(WIFEXITED(status)); ATF_REQUIRE(WEXITSTATUS(status) == 0); wpid = wait(&status); ATF_REQUIRE(wpid == -1); ATF_REQUIRE(errno == ECHILD); } static void * flock_thread(void *arg) { int fd; fd = *(int *)arg; (void)flock(fd, LOCK_EX); (void)flock(fd, LOCK_UN); return (NULL); } /* * Verify that PT_ATTACH will suspend threads sleeping in an SBDRY section. * We rely on the fact that the lockf implementation sets SBDRY before blocking * on a lock. This is a regression test for r318191. */ ATF_TC_WITHOUT_HEAD(ptrace__PT_ATTACH_with_SBDRY_thread); ATF_TC_BODY(ptrace__PT_ATTACH_with_SBDRY_thread, tc) { pthread_barrier_t barrier; pthread_barrierattr_t battr; char tmpfile[64]; pid_t child, wpid; int error, fd, i, status; ATF_REQUIRE(pthread_barrierattr_init(&battr) == 0); ATF_REQUIRE(pthread_barrierattr_setpshared(&battr, PTHREAD_PROCESS_SHARED) == 0); ATF_REQUIRE(pthread_barrier_init(&barrier, &battr, 2) == 0); (void)snprintf(tmpfile, sizeof(tmpfile), "./ptrace.XXXXXX"); fd = mkstemp(tmpfile); ATF_REQUIRE(fd >= 0); ATF_REQUIRE((child = fork()) != -1); if (child == 0) { pthread_t t[2]; int error, cfd; error = pthread_barrier_wait(&barrier); if (error != 0 && error != PTHREAD_BARRIER_SERIAL_THREAD) _exit(1); cfd = open(tmpfile, O_RDONLY); if (cfd < 0) _exit(1); /* * We want at least two threads blocked on the file lock since * the SIGSTOP from PT_ATTACH may kick one of them out of * sleep. */ if (pthread_create(&t[0], NULL, flock_thread, &cfd) != 0) _exit(1); if (pthread_create(&t[1], NULL, flock_thread, &cfd) != 0) _exit(1); if (pthread_join(t[0], NULL) != 0) _exit(1); if (pthread_join(t[1], NULL) != 0) _exit(1); _exit(0); } ATF_REQUIRE(flock(fd, LOCK_EX) == 0); error = pthread_barrier_wait(&barrier); ATF_REQUIRE(error == 0 || error == PTHREAD_BARRIER_SERIAL_THREAD); /* * Give the child some time to block. Is there a better way to do this? */ sleep(1); /* * Attach and give the child 3 seconds to stop. */ ATF_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) == 0); for (i = 0; i < 3; i++) { wpid = waitpid(child, &status, WNOHANG); if (wpid == child && WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) break; sleep(1); } ATF_REQUIRE_MSG(i < 3, "failed to stop child process after PT_ATTACH"); ATF_REQUIRE(ptrace(PT_DETACH, child, NULL, 0) == 0); ATF_REQUIRE(flock(fd, LOCK_UN) == 0); ATF_REQUIRE(unlink(tmpfile) == 0); ATF_REQUIRE(close(fd) == 0); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_trace_me); ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_attach); ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_child_debugger); ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__follow_fork_both_attached); ATF_TP_ADD_TC(tp, ptrace__follow_fork_child_detached); ATF_TP_ADD_TC(tp, ptrace__follow_fork_parent_detached); ATF_TP_ADD_TC(tp, ptrace__follow_fork_both_attached_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__follow_fork_child_detached_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__follow_fork_parent_detached_unrelated_debugger); ATF_TP_ADD_TC(tp, ptrace__getppid); ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_fork); ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_vfork); ATF_TP_ADD_TC(tp, ptrace__new_child_pl_syscall_code_thread); ATF_TP_ADD_TC(tp, ptrace__lwp_events); ATF_TP_ADD_TC(tp, ptrace__lwp_events_exec); ATF_TP_ADD_TC(tp, ptrace__siginfo); 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); #if defined(__amd64__) || defined(__i386__) || defined(__sparc64__) ATF_TP_ADD_TC(tp, ptrace__PT_KILL_breakpoint); #endif ATF_TP_ADD_TC(tp, ptrace__PT_KILL_system_call); ATF_TP_ADD_TC(tp, ptrace__PT_KILL_threads); ATF_TP_ADD_TC(tp, ptrace__PT_KILL_competing_signal); ATF_TP_ADD_TC(tp, ptrace__PT_KILL_competing_stop); ATF_TP_ADD_TC(tp, ptrace__PT_KILL_with_signal_full_sigqueue); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_system_call_entry); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_system_call_entry_and_exit); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_full_sigqueue); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_change_sig); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_sigtrap_system_call_entry); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_mix); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_kqueue); ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_thread_sigmask); ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop1); ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop2); ATF_TP_ADD_TC(tp, ptrace__event_mask_sigkill_discard); ATF_TP_ADD_TC(tp, ptrace__PT_ATTACH_with_SBDRY_thread); return (atf_no_error()); }