Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F133109250
D9260.id25114.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
38 KB
Referenced Files
None
Subscribers
None
D9260.id25114.diff
View Options
Index: sys/kern/kern_fork.c
===================================================================
--- sys/kern/kern_fork.c
+++ sys/kern/kern_fork.c
@@ -1081,7 +1081,7 @@
proc_reparent(p, dbg);
sx_xunlock(&proctree_lock);
td->td_dbgflags |= TDB_CHILD | TDB_SCX | TDB_FSTP;
- ptracestop(td, SIGSTOP);
+ ptracestop(td, SIGSTOP, NULL);
td->td_dbgflags &= ~(TDB_CHILD | TDB_SCX);
} else {
/*
@@ -1102,7 +1102,7 @@
_STOPEVENT(p, S_SCX, td->td_dbg_sc_code);
if ((p->p_ptevents & PTRACE_SCX) != 0 ||
(td->td_dbgflags & TDB_BORN) != 0)
- ptracestop(td, SIGTRAP);
+ ptracestop(td, SIGTRAP, NULL);
td->td_dbgflags &= ~(TDB_SCX | TDB_BORN);
PROC_UNLOCK(p);
}
Index: sys/kern/kern_sig.c
===================================================================
--- sys/kern/kern_sig.c
+++ sys/kern/kern_sig.c
@@ -360,6 +360,10 @@
KASSERT(sq->sq_flags & SQ_INIT, ("sigqueue not inited"));
+ /*
+ * SIGKILL/SIGSTOP cannot be caught or masked, so take the fast path
+ * for these signals.
+ */
if (signo == SIGKILL || signo == SIGSTOP || si == NULL) {
SIGADDSET(sq->sq_kill, signo);
goto out_set_bit;
@@ -398,8 +402,9 @@
ksi->ksi_sigq = sq;
}
- if ((si->ksi_flags & KSI_TRAP) != 0 ||
- (si->ksi_flags & KSI_SIGQ) == 0) {
+ if ((si->ksi_flags & KSI_PTRACE) == 0 &&
+ ((si->ksi_flags & KSI_TRAP) != 0 ||
+ (si->ksi_flags & KSI_SIGQ) == 0)) {
if (ret != 0)
SIGADDSET(sq->sq_kill, signo);
ret = 0;
@@ -2500,69 +2505,111 @@
return (wakeup_swapper);
}
+/*
+ * Stop the process for an event deemed interesting to the debugger. If si is
+ * non-NULL, this is a signal exchange; the new signal requested by the
+ * debugger will be returned for handling. If si is NULL, this is some other
+ * type of interesting event. The debugger may request a signal be delivered in
+ * that case as well, however it will be deferred until it can be handled.
+ */
int
-ptracestop(struct thread *td, int sig)
+ptracestop(struct thread *td, int sig, ksiginfo_t *si)
{
struct proc *p = td->td_proc;
+ struct thread *td2;
+ ksiginfo_t ksi;
+ int prop;
PROC_LOCK_ASSERT(p, MA_OWNED);
KASSERT(!(p->p_flag & P_WEXIT), ("Stopping exiting process"));
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK,
&p->p_mtx.lock_object, "Stopping for traced signal");
- td->td_dbgflags |= TDB_XSIG;
td->td_xsig = sig;
- CTR4(KTR_PTRACE, "ptracestop: tid %d (pid %d) flags %#x sig %d",
- td->td_tid, p->p_pid, td->td_dbgflags, sig);
- PROC_SLOCK(p);
- while ((p->p_flag & P_TRACED) && (td->td_dbgflags & TDB_XSIG)) {
- if (p->p_flag & P_SINGLE_EXIT &&
- !(td->td_dbgflags & TDB_EXIT)) {
- /*
- * Ignore ptrace stops except for thread exit
- * events when the process exits.
- */
- td->td_dbgflags &= ~TDB_XSIG;
- PROC_SUNLOCK(p);
- return (sig);
- }
+ if (si && P_KILLED(p)) {
/*
- * Make wait(2) work. Ensure that right after the
- * attach, the thread which was decided to become the
- * leader of attach gets reported to the waiter.
- * Otherwise, just avoid overwriting another thread's
- * assignment to p_xthread. If another thread has
- * already set p_xthread, the current thread will get
- * a chance to report itself upon the next iteration.
+ * Ensure that, if we've been PT_KILLed, the exit status
+ * reflects that. Another thread may also be in ptracestop(),
+ * having just received the SIGKILL, but this thread was
+ * unsuspended first.
*/
- if ((td->td_dbgflags & TDB_FSTP) != 0 ||
- ((p->p_flag2 & P2_PTRACE_FSTP) == 0 &&
- p->p_xthread == NULL)) {
- p->p_xsig = sig;
- p->p_xthread = td;
- td->td_dbgflags &= ~TDB_FSTP;
- p->p_flag2 &= ~P2_PTRACE_FSTP;
- p->p_flag |= P_STOPPED_SIG | P_STOPPED_TRACE;
- sig_suspend_threads(td, p, 0);
- }
- if ((td->td_dbgflags & TDB_STOPATFORK) != 0) {
- td->td_dbgflags &= ~TDB_STOPATFORK;
- cv_broadcast(&p->p_dbgwait);
- }
+ td->td_xsig = SIGKILL;
+ } else if (si == NULL || (si->ksi_flags & KSI_PTRACE) == 0) {
+ td->td_dbgflags |= TDB_XSIG;
+ CTR4(KTR_PTRACE, "ptracestop: tid %d (pid %d) flags %#x sig %d",
+ td->td_tid, p->p_pid, td->td_dbgflags, sig);
+ PROC_SLOCK(p);
+ while ((p->p_flag & P_TRACED) && (td->td_dbgflags & TDB_XSIG)) {
+ if (p->p_flag & P_SINGLE_EXIT &&
+ !(td->td_dbgflags & TDB_EXIT)) {
+ /*
+ * Ignore ptrace stops except for thread exit
+ * events when the process exits.
+ */
+ td->td_dbgflags &= ~TDB_XSIG;
+ PROC_SUNLOCK(p);
+ return (0);
+ }
+
+ /*
+ * Make wait(2) work. Ensure that right after the
+ * attach, the thread which was decided to become the
+ * leader of attach gets reported to the waiter.
+ * Otherwise, just avoid overwriting another thread's
+ * assignment to p_xthread. If another thread has
+ * already set p_xthread, the current thread will get
+ * a chance to report itself upon the next iteration.
+ */
+ if ((td->td_dbgflags & TDB_FSTP) != 0 ||
+ ((p->p_flag2 & P2_PTRACE_FSTP) == 0 &&
+ p->p_xthread == NULL)) {
+ p->p_xsig = sig;
+ p->p_xthread = td;
+ td->td_dbgflags &= ~TDB_FSTP;
+ p->p_flag2 &= ~P2_PTRACE_FSTP;
+ p->p_flag |= P_STOPPED_SIG | P_STOPPED_TRACE;
+ sig_suspend_threads(td, p, 0);
+ }
+ if ((td->td_dbgflags & TDB_STOPATFORK) != 0) {
+ td->td_dbgflags &= ~TDB_STOPATFORK;
+ cv_broadcast(&p->p_dbgwait);
+ }
stopme:
- thread_suspend_switch(td, p);
- if (p->p_xthread == td)
- p->p_xthread = NULL;
- if (!(p->p_flag & P_TRACED))
- break;
- if (td->td_dbgflags & TDB_SUSPEND) {
- if (p->p_flag & P_SINGLE_EXIT)
+ thread_suspend_switch(td, p);
+ if (p->p_xthread == td)
+ p->p_xthread = NULL;
+ if (!(p->p_flag & P_TRACED))
break;
- goto stopme;
+ if (td->td_dbgflags & TDB_SUSPEND) {
+ if (p->p_flag & P_SINGLE_EXIT)
+ break;
+ goto stopme;
+ }
}
+ PROC_SUNLOCK(p);
}
- PROC_SUNLOCK(p);
+
+ if (si && sig == td->td_xsig) {
+ /* Parent wants us to take the original signal unchanged. */
+ si->ksi_flags |= KSI_HEAD;
+ if (sigqueue_add(&td->td_sigqueue, sig, si) != 0)
+ si->ksi_signo = 0;
+ } else if (td->td_xsig) {
+ /*
+ * If parent wants us to take a new signal, then it will leave
+ * it in td->td_xsig; otherwise we just look for signals again.
+ */
+ ksiginfo_init(&ksi);
+ ksi.ksi_signo = td->td_xsig;
+ ksi.ksi_flags |= KSI_PTRACE;
+ prop = sigprop(td->td_xsig);
+ td2 = sigtd(p, td->td_xsig, prop);
+ tdsendsignal(p, td2, td->td_xsig, &ksi);
+ if (td != td2)
+ return (0);
+ }
+
return (td->td_xsig);
}
@@ -2720,7 +2767,7 @@
struct sigacts *ps;
struct sigqueue *queue;
sigset_t sigpending;
- int sig, prop, newsig;
+ int sig, prop;
p = td->td_proc;
ps = p->p_sigacts;
@@ -2783,47 +2830,18 @@
}
mtx_unlock(&ps->ps_mtx);
- newsig = ptracestop(td, sig);
+ sig = ptracestop(td, sig, &td->td_dbgksi);
mtx_lock(&ps->ps_mtx);
- if (sig != newsig) {
-
- /*
- * If parent wants us to take the signal,
- * then it will leave it in p->p_xsig;
- * otherwise we just look for signals again.
- */
- if (newsig == 0)
- continue;
- sig = newsig;
-
- /*
- * Put the new signal into td_sigqueue. If the
- * signal is being masked, look for other
- * signals.
- */
- sigqueue_add(queue, sig, NULL);
- if (SIGISMEMBER(td->td_sigmask, sig))
- continue;
- signotify(td);
- } else {
- if (td->td_dbgksi.ksi_signo != 0) {
- td->td_dbgksi.ksi_flags |= KSI_HEAD;
- if (sigqueue_add(&td->td_sigqueue, sig,
- &td->td_dbgksi) != 0)
- td->td_dbgksi.ksi_signo = 0;
- }
- if (td->td_dbgksi.ksi_signo == 0)
- sigqueue_add(&td->td_sigqueue, sig,
- NULL);
- }
-
- /*
+ /*
+ * Keep looking if the debugger discarded the signal
+ * or replaced it with a masked signal.
+ *
* If the traced bit got turned off, go back up
* to the top to rescan signals. This ensures
* that p_sig* and p_sigact are consistent.
*/
- if ((p->p_flag & P_TRACED) == 0)
+ if (sig == 0 || (p->p_flag & P_TRACED) == 0)
continue;
}
Index: sys/kern/kern_thr.c
===================================================================
--- sys/kern/kern_thr.c
+++ sys/kern/kern_thr.c
@@ -356,7 +356,7 @@
p->p_pendingexits++;
td->td_dbgflags |= TDB_EXIT;
if (p->p_ptevents & PTRACE_LWP)
- ptracestop(td, SIGTRAP);
+ ptracestop(td, SIGTRAP, NULL);
PROC_UNLOCK(p);
tidhash_remove(td);
PROC_LOCK(p);
Index: sys/kern/subr_syscall.c
===================================================================
--- sys/kern/subr_syscall.c
+++ sys/kern/subr_syscall.c
@@ -88,7 +88,7 @@
td->td_dbg_sc_code = sa->code;
td->td_dbg_sc_narg = sa->narg;
if (p->p_ptevents & PTRACE_SCE)
- ptracestop((td), SIGTRAP);
+ ptracestop((td), SIGTRAP, NULL);
PROC_UNLOCK(p);
}
if (td->td_dbgflags & TDB_USERWR) {
@@ -222,7 +222,7 @@
if (traced &&
((td->td_dbgflags & (TDB_FORK | TDB_EXEC)) != 0 ||
(p->p_ptevents & PTRACE_SCX) != 0))
- ptracestop(td, SIGTRAP);
+ ptracestop(td, SIGTRAP, NULL);
td->td_dbgflags &= ~(TDB_SCX | TDB_EXEC | TDB_FORK);
PROC_UNLOCK(p);
}
@@ -259,7 +259,7 @@
if (td->td_dbgflags & TDB_VFORK) {
PROC_LOCK(p);
if (p->p_ptevents & PTRACE_VFORK)
- ptracestop(td, SIGTRAP);
+ ptracestop(td, SIGTRAP, NULL);
td->td_dbgflags &= ~TDB_VFORK;
PROC_UNLOCK(p);
}
Index: sys/kern/sys_process.c
===================================================================
--- sys/kern/sys_process.c
+++ sys/kern/sys_process.c
@@ -1125,6 +1125,16 @@
td2->td_dbgflags &= ~TDB_XSIG;
td2->td_xsig = data;
+ /*
+ * P_WKILLED is insurance that a PT_KILL always works
+ * immediately, even if another thread is unsuspended
+ * first and attempts to handle a different signal or if
+ * the POSIX.1b style signal queue cannot accommodate
+ * any new signals.
+ */
+ if (data == SIGKILL)
+ p->p_flag |= P_WKILLED;
+
if (req == PT_DETACH) {
FOREACH_THREAD_IN_PROC(p, td3)
td3->td_dbgflags &= ~TDB_SUSPEND;
Index: sys/sys/signalvar.h
===================================================================
--- sys/sys/signalvar.h
+++ sys/sys/signalvar.h
@@ -237,7 +237,8 @@
#define KSI_INS 0x04 /* Directly insert ksi, not the copy */
#define KSI_SIGQ 0x08 /* Generated by sigqueue, might ret EGAIN. */
#define KSI_HEAD 0x10 /* Insert into head, not tail. */
-#define KSI_COPYMASK (KSI_TRAP|KSI_SIGQ)
+#define KSI_PTRACE 0x20 /* Generated by ptrace. */
+#define KSI_COPYMASK (KSI_TRAP|KSI_SIGQ|KSI_PTRACE)
#define KSI_ONQ(ksi) ((ksi)->ksi_sigq != NULL)
@@ -370,7 +371,7 @@
void pgsignal(struct pgrp *pgrp, int sig, int checkctty, ksiginfo_t *ksi);
int postsig(int sig);
void kern_psignal(struct proc *p, int sig);
-int ptracestop(struct thread *td, int sig);
+int ptracestop(struct thread *td, int sig, ksiginfo_t *si);
void sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *retmask);
struct sigacts *sigacts_alloc(void);
void sigacts_copy(struct sigacts *dest, struct sigacts *src);
Index: tests/sys/kern/Makefile
===================================================================
--- tests/sys/kern/Makefile
+++ tests/sys/kern/Makefile
@@ -8,6 +8,7 @@
ATF_TESTS_C+= kern_copyin
ATF_TESTS_C+= kern_descrip_test
ATF_TESTS_C+= ptrace_test
+TEST_METADATA.ptrace_test+= timeout="15"
ATF_TESTS_C+= reaper
PLAIN_TESTS_C+= subr_unit_test
ATF_TESTS_C+= unix_seqpacket_test
Index: tests/sys/kern/ptrace_test.c
===================================================================
--- tests/sys/kern/ptrace_test.c
+++ tests/sys/kern/ptrace_test.c
@@ -28,6 +28,9 @@
__FBSDID("$FreeBSD$");
#include <sys/types.h>
+#include <sys/cpuset.h>
+#include <sys/event.h>
+#include <sys/time.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <sys/sysctl.h>
@@ -35,6 +38,7 @@
#include <sys/wait.h>
#include <errno.h>
#include <pthread.h>
+#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -1673,6 +1677,905 @@
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;
+
+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)
{
@@ -1700,6 +2603,19 @@
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);
+ 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());
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Oct 24, 1:33 AM (9 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
24111932
Default Alt Text
D9260.id25114.diff (38 KB)
Attached To
Mode
D9260: Defer ptracestop signals that cannot be immediately delivered
Attached
Detach File
Event Timeline
Log In to Comment