Changeset View
Standalone View
sys/kern/kern_sig.c
Show First 20 Lines • Show All 354 Lines • ▼ Show 20 Lines | |||||
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 ((si->ksi_flags & KSI_PTRACE) == 0 && | ||||
(si->ksi_flags & KSI_SIGQ) == 0) { | ((si->ksi_flags & KSI_TRAP) != 0 || | ||||
(si->ksi_flags & KSI_SIGQ) == 0)) { | |||||
kib: Why did you added this condition ? What would happen in sigqueue_add() was unable to add the… | |||||
Not Done Inline ActionsA signal in that case would be dropped (unless it's SIGKILL), the same as if you tried to sigqueue() on a full signal queue. If it can't be queued, the signal can't be identified as ptrace generated. This is to prevent ptracestops from signals that themselves were genereated by ptracestop. badger: A signal in that case would be dropped (unless it's SIGKILL), the same as if you tried to… | |||||
Not Done Inline ActionsIdeally, the ptrace(2) user should be informed about this condition. I understand that this is hard. Another option would be to add something like sq_ptrace, where ptrace-requested signals can be safely added to bitmask in case memory allocation fails. kib: Ideally, the ptrace(2) user should be informed about this condition. I understand that this is… | |||||
Not Done Inline ActionsHmm. I think adding an sq_ptrace is probably correct. The only drawback I see is some added complexity. Informing the ptrace(2) user is not only hard, but I think adds a FreeBSD-only maintenance burden for debugger developers; I'm not aware of another OS that provides a mechanism for that. badger: Hmm. I think adding an sq_ptrace is probably correct. The only drawback I see is some added… | |||||
Not Done Inline ActionsThe mechanism to inform would be just ENOMEM error returned from the ptrace(2) call. I mentioned that it is hard because there is not an easy way to propagate the error back and to provide a way to correlate it with the failed call. sq_ptrace does not look that complicated. It should by copied/pasted from sq_kill everywhere except one place. kib: The mechanism to inform would be just ENOMEM error returned from the ptrace(2) call. I… | |||||
Not Done Inline ActionsRight, the propagation bit is the part where I imagined some different mechanism would need to be employed. Regardless, after looking more closely, I agree that sq_ptrace doesn't really add much complexity and seems like a better solution, so I've gone forward with it. badger: Right, the propagation bit is the part where I imagined some different mechanism would need to… | |||||
if (ret != 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; | ||||
} | } | ||||
Done Inline Actions} else if ( i.e. merge with the next line kib: ```
} else if (
```
i.e. merge with the next line | |||||
if (ret != 0) | 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); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 2,078 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 | |||||
Done Inline Actionss/interested/interesting/ jhb: s/interested/interesting/ | |||||
* 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 && P_KILLED(p)) { | |||||
Done Inline Actionsif (si != NULL && ... kib: ```
if (si != NULL && ...
``` | |||||
/* | |||||
* 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_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", | 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->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) { | |||||
Done Inline ActionsAgain, please use si != NULL way to check for non-null pointer. kib: Again, please use si != NULL way to check for non-null pointer. | |||||
/* 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) { | |||||
Done Inline Actionsif (td->td_xsig != 0) kib: 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; | |||||
Done Inline ActionsCan probably drop this blank line. jhb: Can probably drop this blank line. | |||||
ksi.ksi_flags |= KSI_PTRACE; | |||||
prop = sigprop(td->td_xsig); | |||||
td2 = sigtd(p, td->td_xsig, prop); | |||||
tdsendsignal(p, td2, td->td_xsig, &ksi); | |||||
Not Done Inline ActionsShouldn't td_xsig cleared there and in previous if () body ? kib: Shouldn't td_xsig cleared there and in previous if () body ? | |||||
Not Done Inline ActionsI don't think there is any impact to not zero it; next time it's needed it will be reset to the appropriate value before another thread can look at it. That is, I don't see any path where td_xsig is not owned by this thread exclusively except when performing signal exchange. badger: I don't think there is any impact to not zero it; next time it's needed it will be reset to the… | |||||
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 |
Why did you added this condition ? What would happen in sigqueue_add() was unable to add the ksi from ptracestop() to the queue ?