Changeset View
Changeset View
Standalone View
Standalone View
tests/sys/kern/ptrace_test.c
Show All 22 Lines | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/cpuset.h> | |||||
#include <sys/event.h> | |||||
#include <sys/time.h> | |||||
#include <sys/ptrace.h> | #include <sys/ptrace.h> | ||||
#include <sys/syscall.h> | #include <sys/syscall.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/user.h> | #include <sys/user.h> | ||||
#include <sys/wait.h> | #include <sys/wait.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <pthread.h> | #include <pthread.h> | ||||
#include <semaphore.h> | |||||
#include <signal.h> | #include <signal.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <atf-c.h> | #include <atf-c.h> | ||||
/* | /* | ||||
* A variant of ATF_REQUIRE that is suitable for use in child | * A variant of ATF_REQUIRE that is suitable for use in child | ||||
▲ Show 20 Lines • Show All 1,622 Lines • ▼ Show 20 Lines | ATF_TC_BODY(ptrace__ptrace_vfork_follow, tc) | ||||
ATF_REQUIRE(WIFEXITED(status)); | ATF_REQUIRE(WIFEXITED(status)); | ||||
ATF_REQUIRE(WEXITSTATUS(status) == 1); | ATF_REQUIRE(WEXITSTATUS(status) == 1); | ||||
wpid = wait(&status); | wpid = wait(&status); | ||||
ATF_REQUIRE(wpid == -1); | ATF_REQUIRE(wpid == -1); | ||||
ATF_REQUIRE(errno == ECHILD); | ATF_REQUIRE(errno == ECHILD); | ||||
} | } | ||||
/* | |||||
* 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(); | |||||
__builtin_debugtrap(); | |||||
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); | |||||
} | |||||
/* | |||||
* 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; | |||||
ATF_REQUIRE((fpid = fork()) != -1); | |||||
if (fpid == 0) { | |||||
/* | |||||
* Bind to one CPU so only one thread at a time will run. This | |||||
* test expects that the first thread created (the main thread) | |||||
* will be unsuspended first and will block the second thread | |||||
* from running. | |||||
*/ | |||||
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); | |||||
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); | |||||
} | |||||
static void | |||||
sigusr1_handler(int sig) | |||||
{ | |||||
CHILD_REQUIRE(sig == SIGUSR1); | |||||
_exit(2); | |||||
} | |||||
/* | |||||
* 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_KILL will kill the process. | |||||
*/ | |||||
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(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, 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; | |||||
jhb: Perhaps a blank line here before the function definition? | |||||
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); | |||||
} | |||||
ATF_TP_ADD_TCS(tp) | 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_trace_me); | ||||
ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_attach); | 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_child_debugger); | ||||
ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_unrelated_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_both_attached); | ||||
Show All 11 Lines | ATF_TP_ADD_TCS(tp) | ||||
ATF_TP_ADD_TC(tp, ptrace__lwp_events); | ATF_TP_ADD_TC(tp, ptrace__lwp_events); | ||||
ATF_TP_ADD_TC(tp, ptrace__lwp_events_exec); | ATF_TP_ADD_TC(tp, ptrace__lwp_events_exec); | ||||
ATF_TP_ADD_TC(tp, ptrace__siginfo); | ATF_TP_ADD_TC(tp, ptrace__siginfo); | ||||
ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_disable); | ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_disable); | ||||
ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_enable); | ATF_TP_ADD_TC(tp, ptrace__ptrace_exec_enable); | ||||
ATF_TP_ADD_TC(tp, ptrace__event_mask); | ATF_TP_ADD_TC(tp, ptrace__event_mask); | ||||
ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork); | ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork); | ||||
ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork_follow); | ATF_TP_ADD_TC(tp, ptrace__ptrace_vfork_follow); | ||||
ATF_TP_ADD_TC(tp, ptrace__PT_KILL_breakpoint); | |||||
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_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); | |||||
return (atf_no_error()); | return (atf_no_error()); | ||||
} | } |
Perhaps a blank line here before the function definition?