Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/kern_sig.c
Show First 20 Lines • Show All 272 Lines • ▼ Show 20 Lines | ksiginfo_tryfree(ksiginfo_t *ksi) | ||||
return (0); | return (0); | ||||
} | } | ||||
void | void | ||||
sigqueue_init(sigqueue_t *list, struct proc *p) | sigqueue_init(sigqueue_t *list, struct proc *p) | ||||
{ | { | ||||
SIGEMPTYSET(list->sq_signals); | SIGEMPTYSET(list->sq_signals); | ||||
SIGEMPTYSET(list->sq_kill); | SIGEMPTYSET(list->sq_kill); | ||||
SIGEMPTYSET(list->sq_ptrace); | |||||
TAILQ_INIT(&list->sq_list); | TAILQ_INIT(&list->sq_list); | ||||
list->sq_proc = p; | list->sq_proc = p; | ||||
list->sq_flags = SQ_INIT; | list->sq_flags = SQ_INIT; | ||||
} | } | ||||
/* | /* | ||||
* Get a signal's ksiginfo. | * Get a signal's ksiginfo. | ||||
* Return: | * Return: | ||||
* 0 - signal not found | * 0 - signal not found | ||||
* others - signal number | * others - signal number | ||||
*/ | */ | ||||
static int | static int | ||||
sigqueue_get(sigqueue_t *sq, int signo, ksiginfo_t *si) | sigqueue_get(sigqueue_t *sq, int signo, ksiginfo_t *si) | ||||
{ | { | ||||
struct proc *p = sq->sq_proc; | struct proc *p = sq->sq_proc; | ||||
struct ksiginfo *ksi, *next; | struct ksiginfo *ksi, *next; | ||||
int count = 0; | int count = 0; | ||||
KASSERT(sq->sq_flags & SQ_INIT, ("sigqueue not inited")); | KASSERT(sq->sq_flags & SQ_INIT, ("sigqueue not inited")); | ||||
if (!SIGISMEMBER(sq->sq_signals, signo)) | if (!SIGISMEMBER(sq->sq_signals, signo)) | ||||
return (0); | return (0); | ||||
if (SIGISMEMBER(sq->sq_ptrace, signo)) { | |||||
count++; | |||||
SIGDELSET(sq->sq_ptrace, signo); | |||||
si->ksi_flags |= KSI_PTRACE; | |||||
} | |||||
if (SIGISMEMBER(sq->sq_kill, signo)) { | if (SIGISMEMBER(sq->sq_kill, signo)) { | ||||
count++; | count++; | ||||
if (count == 1) | |||||
SIGDELSET(sq->sq_kill, signo); | SIGDELSET(sq->sq_kill, signo); | ||||
} | } | ||||
TAILQ_FOREACH_SAFE(ksi, &sq->sq_list, ksi_link, next) { | TAILQ_FOREACH_SAFE(ksi, &sq->sq_list, ksi_link, next) { | ||||
if (ksi->ksi_signo == signo) { | if (ksi->ksi_signo == signo) { | ||||
if (count == 0) { | if (count == 0) { | ||||
TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); | TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); | ||||
ksi->ksi_sigq = NULL; | ksi->ksi_sigq = NULL; | ||||
ksiginfo_copy(ksi, si); | ksiginfo_copy(ksi, si); | ||||
Show All 27 Lines | sigqueue_take(ksiginfo_t *ksi) | ||||
if (!(ksi->ksi_flags & KSI_EXT) && p != NULL) | if (!(ksi->ksi_flags & KSI_EXT) && p != NULL) | ||||
p->p_pendingcnt--; | p->p_pendingcnt--; | ||||
for (kp = TAILQ_FIRST(&sq->sq_list); kp != NULL; | for (kp = TAILQ_FIRST(&sq->sq_list); kp != NULL; | ||||
kp = TAILQ_NEXT(kp, ksi_link)) { | kp = TAILQ_NEXT(kp, ksi_link)) { | ||||
if (kp->ksi_signo == ksi->ksi_signo) | if (kp->ksi_signo == ksi->ksi_signo) | ||||
break; | break; | ||||
} | } | ||||
if (kp == NULL && !SIGISMEMBER(sq->sq_kill, ksi->ksi_signo)) | if (kp == NULL && !SIGISMEMBER(sq->sq_kill, ksi->ksi_signo) && | ||||
!SIGISMEMBER(sq->sq_ptrace, ksi->ksi_signo)) | |||||
SIGDELSET(sq->sq_signals, ksi->ksi_signo); | SIGDELSET(sq->sq_signals, ksi->ksi_signo); | ||||
} | } | ||||
static int | static int | ||||
sigqueue_add(sigqueue_t *sq, int signo, ksiginfo_t *si) | sigqueue_add(sigqueue_t *sq, int signo, ksiginfo_t *si) | ||||
{ | { | ||||
struct proc *p = sq->sq_proc; | struct proc *p = sq->sq_proc; | ||||
struct ksiginfo *ksi; | struct ksiginfo *ksi; | ||||
int ret = 0; | int ret = 0; | ||||
KASSERT(sq->sq_flags & SQ_INIT, ("sigqueue not inited")); | 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) { | if (signo == SIGKILL || signo == SIGSTOP || si == NULL) { | ||||
SIGADDSET(sq->sq_kill, signo); | SIGADDSET(sq->sq_kill, signo); | ||||
goto out_set_bit; | goto out_set_bit; | ||||
} | } | ||||
/* directly insert the ksi, don't copy it */ | /* directly insert the ksi, don't copy it */ | ||||
if (si->ksi_flags & KSI_INS) { | if (si->ksi_flags & KSI_INS) { | ||||
if (si->ksi_flags & KSI_HEAD) | if (si->ksi_flags & KSI_HEAD) | ||||
Show All 22 Lines | if (p != NULL && p->p_pendingcnt >= max_pending_per_proc) { | ||||
ksi->ksi_signo = signo; | ksi->ksi_signo = signo; | ||||
if (si->ksi_flags & KSI_HEAD) | if (si->ksi_flags & KSI_HEAD) | ||||
TAILQ_INSERT_HEAD(&sq->sq_list, ksi, ksi_link); | TAILQ_INSERT_HEAD(&sq->sq_list, ksi, ksi_link); | ||||
else | else | ||||
TAILQ_INSERT_TAIL(&sq->sq_list, ksi, ksi_link); | TAILQ_INSERT_TAIL(&sq->sq_list, ksi, ksi_link); | ||||
ksi->ksi_sigq = sq; | ksi->ksi_sigq = sq; | ||||
} | } | ||||
if ((si->ksi_flags & KSI_TRAP) != 0 || | if (ret != 0) { | ||||
if ((si->ksi_flags & KSI_PTRACE) != 0) { | |||||
SIGADDSET(sq->sq_ptrace, signo); | |||||
ret = 0; | |||||
goto out_set_bit; | |||||
} else if ((si->ksi_flags & KSI_TRAP) != 0 || | |||||
(si->ksi_flags & KSI_SIGQ) == 0) { | (si->ksi_flags & KSI_SIGQ) == 0) { | ||||
if (ret != 0) | |||||
SIGADDSET(sq->sq_kill, signo); | SIGADDSET(sq->sq_kill, signo); | ||||
ret = 0; | ret = 0; | ||||
goto out_set_bit; | goto out_set_bit; | ||||
} | } | ||||
if (ret != 0) | |||||
return (ret); | return (ret); | ||||
} | |||||
out_set_bit: | out_set_bit: | ||||
SIGADDSET(sq->sq_signals, signo); | SIGADDSET(sq->sq_signals, signo); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
void | void | ||||
sigqueue_flush(sigqueue_t *sq) | sigqueue_flush(sigqueue_t *sq) | ||||
Show All 10 Lines | while ((ksi = TAILQ_FIRST(&sq->sq_list)) != NULL) { | ||||
TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); | TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); | ||||
ksi->ksi_sigq = NULL; | ksi->ksi_sigq = NULL; | ||||
if (ksiginfo_tryfree(ksi) && p != NULL) | if (ksiginfo_tryfree(ksi) && p != NULL) | ||||
p->p_pendingcnt--; | p->p_pendingcnt--; | ||||
} | } | ||||
SIGEMPTYSET(sq->sq_signals); | SIGEMPTYSET(sq->sq_signals); | ||||
SIGEMPTYSET(sq->sq_kill); | SIGEMPTYSET(sq->sq_kill); | ||||
SIGEMPTYSET(sq->sq_ptrace); | |||||
} | } | ||||
static void | static void | ||||
sigqueue_move_set(sigqueue_t *src, sigqueue_t *dst, const sigset_t *set) | sigqueue_move_set(sigqueue_t *src, sigqueue_t *dst, const sigset_t *set) | ||||
{ | { | ||||
sigset_t tmp; | sigset_t tmp; | ||||
struct proc *p1, *p2; | struct proc *p1, *p2; | ||||
ksiginfo_t *ksi, *next; | ksiginfo_t *ksi, *next; | ||||
Show All 16 Lines | sigqueue_move_set(sigqueue_t *src, sigqueue_t *dst, const sigset_t *set) | ||||
} | } | ||||
/* Move pending bits to target list */ | /* Move pending bits to target list */ | ||||
tmp = src->sq_kill; | tmp = src->sq_kill; | ||||
SIGSETAND(tmp, *set); | SIGSETAND(tmp, *set); | ||||
SIGSETOR(dst->sq_kill, tmp); | SIGSETOR(dst->sq_kill, tmp); | ||||
SIGSETNAND(src->sq_kill, tmp); | SIGSETNAND(src->sq_kill, tmp); | ||||
tmp = src->sq_ptrace; | |||||
SIGSETAND(tmp, *set); | |||||
SIGSETOR(dst->sq_ptrace, tmp); | |||||
SIGSETNAND(src->sq_ptrace, tmp); | |||||
tmp = src->sq_signals; | tmp = src->sq_signals; | ||||
SIGSETAND(tmp, *set); | SIGSETAND(tmp, *set); | ||||
SIGSETOR(dst->sq_signals, tmp); | SIGSETOR(dst->sq_signals, tmp); | ||||
SIGSETNAND(src->sq_signals, tmp); | SIGSETNAND(src->sq_signals, tmp); | ||||
} | } | ||||
#if 0 | #if 0 | ||||
static void | static void | ||||
Show All 20 Lines | TAILQ_FOREACH_SAFE(ksi, &sq->sq_list, ksi_link, next) { | ||||
if (SIGISMEMBER(*set, ksi->ksi_signo)) { | if (SIGISMEMBER(*set, ksi->ksi_signo)) { | ||||
TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); | TAILQ_REMOVE(&sq->sq_list, ksi, ksi_link); | ||||
ksi->ksi_sigq = NULL; | ksi->ksi_sigq = NULL; | ||||
if (ksiginfo_tryfree(ksi) && p != NULL) | if (ksiginfo_tryfree(ksi) && p != NULL) | ||||
p->p_pendingcnt--; | p->p_pendingcnt--; | ||||
} | } | ||||
} | } | ||||
SIGSETNAND(sq->sq_kill, *set); | SIGSETNAND(sq->sq_kill, *set); | ||||
SIGSETNAND(sq->sq_ptrace, *set); | |||||
SIGSETNAND(sq->sq_signals, *set); | SIGSETNAND(sq->sq_signals, *set); | ||||
} | } | ||||
void | void | ||||
sigqueue_delete(sigqueue_t *sq, int signo) | sigqueue_delete(sigqueue_t *sq, int signo) | ||||
{ | { | ||||
sigset_t set; | sigset_t set; | ||||
▲ Show 20 Lines • Show All 1,982 Lines • ▼ Show 20 Lines | #ifdef SMP | ||||
forward_signal(td2); | forward_signal(td2); | ||||
#endif | #endif | ||||
} | } | ||||
thread_unlock(td2); | thread_unlock(td2); | ||||
} | } | ||||
return (wakeup_swapper); | 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 | int | ||||
ptracestop(struct thread *td, int sig) | ptracestop(struct thread *td, int sig, ksiginfo_t *si) | ||||
{ | { | ||||
struct proc *p = td->td_proc; | struct proc *p = td->td_proc; | ||||
struct thread *td2; | |||||
ksiginfo_t ksi; | |||||
int prop; | |||||
PROC_LOCK_ASSERT(p, MA_OWNED); | PROC_LOCK_ASSERT(p, MA_OWNED); | ||||
KASSERT(!(p->p_flag & P_WEXIT), ("Stopping exiting process")); | KASSERT(!(p->p_flag & P_WEXIT), ("Stopping exiting process")); | ||||
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, | WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, | ||||
&p->p_mtx.lock_object, "Stopping for traced signal"); | &p->p_mtx.lock_object, "Stopping for traced signal"); | ||||
td->td_dbgflags |= TDB_XSIG; | |||||
td->td_xsig = sig; | td->td_xsig = sig; | ||||
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", | CTR4(KTR_PTRACE, "ptracestop: tid %d (pid %d) flags %#x sig %d", | ||||
td->td_tid, p->p_pid, td->td_dbgflags, sig); | td->td_tid, p->p_pid, td->td_dbgflags, sig); | ||||
PROC_SLOCK(p); | PROC_SLOCK(p); | ||||
while ((p->p_flag & P_TRACED) && (td->td_dbgflags & TDB_XSIG)) { | while ((p->p_flag & P_TRACED) && (td->td_dbgflags & TDB_XSIG)) { | ||||
if (P_KILLED(p)) { | |||||
/* | |||||
* 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. | |||||
*/ | |||||
td->td_dbgflags &= ~TDB_XSIG; | |||||
td->td_xsig = SIGKILL; | |||||
p->p_ptevents = 0; | |||||
break; | |||||
} | |||||
if (p->p_flag & P_SINGLE_EXIT && | if (p->p_flag & P_SINGLE_EXIT && | ||||
!(td->td_dbgflags & TDB_EXIT)) { | !(td->td_dbgflags & TDB_EXIT)) { | ||||
/* | /* | ||||
* Ignore ptrace stops except for thread exit | * Ignore ptrace stops except for thread exit | ||||
* events when the process exits. | * events when the process exits. | ||||
*/ | */ | ||||
td->td_dbgflags &= ~TDB_XSIG; | td->td_dbgflags &= ~TDB_XSIG; | ||||
PROC_SUNLOCK(p); | PROC_SUNLOCK(p); | ||||
return (sig); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Make wait(2) work. Ensure that right after the | * Make wait(2) work. Ensure that right after the | ||||
* attach, the thread which was decided to become the | * attach, the thread which was decided to become the | ||||
* leader of attach gets reported to the waiter. | * leader of attach gets reported to the waiter. | ||||
* Otherwise, just avoid overwriting another thread's | * Otherwise, just avoid overwriting another thread's | ||||
* assignment to p_xthread. If another thread has | * assignment to p_xthread. If another thread has | ||||
* already set p_xthread, the current thread will get | * already set p_xthread, the current thread will get | ||||
* a chance to report itself upon the next iteration. | * a chance to report itself upon the next iteration. | ||||
*/ | */ | ||||
if ((td->td_dbgflags & TDB_FSTP) != 0 || | if ((td->td_dbgflags & TDB_FSTP) != 0 || | ||||
((p->p_flag2 & P2_PTRACE_FSTP) == 0 && | ((p->p_flag2 & P2_PTRACE_FSTP) == 0 && | ||||
p->p_xthread == NULL)) { | p->p_xthread == NULL)) { | ||||
p->p_xsig = sig; | p->p_xsig = sig; | ||||
p->p_xthread = td; | p->p_xthread = td; | ||||
td->td_dbgflags &= ~TDB_FSTP; | td->td_dbgflags &= ~TDB_FSTP; | ||||
p->p_flag2 &= ~P2_PTRACE_FSTP; | p->p_flag2 &= ~P2_PTRACE_FSTP; | ||||
p->p_flag |= P_STOPPED_SIG | P_STOPPED_TRACE; | p->p_flag |= P_STOPPED_SIG | P_STOPPED_TRACE; | ||||
sig_suspend_threads(td, p, 0); | sig_suspend_threads(td, p, 0); | ||||
} | } | ||||
if ((td->td_dbgflags & TDB_STOPATFORK) != 0) { | if ((td->td_dbgflags & TDB_STOPATFORK) != 0) { | ||||
td->td_dbgflags &= ~TDB_STOPATFORK; | td->td_dbgflags &= ~TDB_STOPATFORK; | ||||
cv_broadcast(&p->p_dbgwait); | cv_broadcast(&p->p_dbgwait); | ||||
} | } | ||||
stopme: | stopme: | ||||
thread_suspend_switch(td, p); | thread_suspend_switch(td, p); | ||||
if (p->p_xthread == td) | if (p->p_xthread == td) | ||||
p->p_xthread = NULL; | p->p_xthread = NULL; | ||||
if (!(p->p_flag & P_TRACED)) | if (!(p->p_flag & P_TRACED)) | ||||
break; | break; | ||||
if (td->td_dbgflags & TDB_SUSPEND) { | if (td->td_dbgflags & TDB_SUSPEND) { | ||||
if (p->p_flag & P_SINGLE_EXIT) | if (p->p_flag & P_SINGLE_EXIT) | ||||
break; | break; | ||||
goto stopme; | goto stopme; | ||||
} | } | ||||
} | } | ||||
PROC_SUNLOCK(p); | PROC_SUNLOCK(p); | ||||
} | |||||
if (si != NULL && 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 != 0) { | |||||
/* | |||||
* 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); | return (td->td_xsig); | ||||
} | } | ||||
static void | static void | ||||
reschedule_signals(struct proc *p, sigset_t block, int flags) | reschedule_signals(struct proc *p, sigset_t block, int flags) | ||||
{ | { | ||||
struct sigacts *ps; | struct sigacts *ps; | ||||
struct thread *td; | struct thread *td; | ||||
▲ Show 20 Lines • Show All 141 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static int | static int | ||||
issignal(struct thread *td) | issignal(struct thread *td) | ||||
{ | { | ||||
struct proc *p; | struct proc *p; | ||||
struct sigacts *ps; | struct sigacts *ps; | ||||
struct sigqueue *queue; | struct sigqueue *queue; | ||||
sigset_t sigpending; | sigset_t sigpending; | ||||
int sig, prop, newsig; | int sig, prop; | ||||
p = td->td_proc; | p = td->td_proc; | ||||
ps = p->p_sigacts; | ps = p->p_sigacts; | ||||
mtx_assert(&ps->ps_mtx, MA_OWNED); | mtx_assert(&ps->ps_mtx, MA_OWNED); | ||||
PROC_LOCK_ASSERT(p, MA_OWNED); | PROC_LOCK_ASSERT(p, MA_OWNED); | ||||
for (;;) { | for (;;) { | ||||
int traced = (p->p_flag & P_TRACED) || (p->p_stops & S_SIG); | int traced = (p->p_flag & P_TRACED) || (p->p_stops & S_SIG); | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | if ((p->p_flag & (P_TRACED | P_PPTRACE)) == P_TRACED) { | ||||
queue = &td->td_sigqueue; | queue = &td->td_sigqueue; | ||||
td->td_dbgksi.ksi_signo = 0; | td->td_dbgksi.ksi_signo = 0; | ||||
if (sigqueue_get(queue, sig, &td->td_dbgksi) == 0) { | if (sigqueue_get(queue, sig, &td->td_dbgksi) == 0) { | ||||
queue = &p->p_sigqueue; | queue = &p->p_sigqueue; | ||||
sigqueue_get(queue, sig, &td->td_dbgksi); | sigqueue_get(queue, sig, &td->td_dbgksi); | ||||
} | } | ||||
mtx_unlock(&ps->ps_mtx); | mtx_unlock(&ps->ps_mtx); | ||||
newsig = ptracestop(td, sig); | sig = ptracestop(td, sig, &td->td_dbgksi); | ||||
mtx_lock(&ps->ps_mtx); | mtx_lock(&ps->ps_mtx); | ||||
if (sig != newsig) { | |||||
/* | /* | ||||
* If parent wants us to take the signal, | * Keep looking if the debugger discarded the signal | ||||
* then it will leave it in p->p_xsig; | * or replaced it with a masked signal. | ||||
* 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); | |||||
} | |||||
/* | |||||
* If the traced bit got turned off, go back up | * If the traced bit got turned off, go back up | ||||
* to the top to rescan signals. This ensures | * to the top to rescan signals. This ensures | ||||
* that p_sig* and p_sigact are consistent. | * 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; | continue; | ||||
} | } | ||||
prop = sigprop(sig); | prop = sigprop(sig); | ||||
/* | /* | ||||
* Decide whether the signal should be returned. | * Decide whether the signal should be returned. | ||||
* Return the signal's number, or fall through | * Return the signal's number, or fall through | ||||
▲ Show 20 Lines • Show All 842 Lines • Show Last 20 Lines |