summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_sig.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2010-01-20 11:58:04 +0000
committerkib <kib@FreeBSD.org>2010-01-20 11:58:04 +0000
commit604a9a1db3621cd6990600b933160428c7686718 (patch)
treee237925ff1e98c6d0c441842e473cc50601918c8 /sys/kern/kern_sig.c
parent428cfaa66eaa12399ad521d9b0e272afc46ff702 (diff)
downloadFreeBSD-src-604a9a1db3621cd6990600b933160428c7686718.zip
FreeBSD-src-604a9a1db3621cd6990600b933160428c7686718.tar.gz
When traced process is about to receive the signal, the process is
stopped and debugger may modify or drop the signal. After the changes to keep process-targeted signals on the process sigqueue, another thread may note the old signal on the queue and act before the thread removes changed or dropped signal from the process queue. Since process is traced, it usually gets stopped. Or, if the same signal is delivered while process was stopped, the thread may erronously remove it, intending to remove the original signal. Remove the signal from the queue before notifying the debugger. Restore the siginfo to the head of sigqueue when signal is allowed to be delivered to the debugee, using newly introduced KSI_HEAD ksiginfo_t flag. This preserves required order of delivery. Always restore the unchanged signal on the curthread sigqueue, not to the process queue, since the thread is about to get it anyway, because sigmask cannot be changed. Handle failure of reinserting the siginfo into the queue by falling back to sq_kill method, calling sigqueue_add with NULL ksi. If debugger changed the signal to be delivered, use sigqueue_add() with NULL ksi instead of only setting sq_signals bit. Reported by: Gardner Bell <gbell72 rogers com> Analyzed and first version of fix by: Tijl Coosemans <tijl coosemans org> PR: 142757 Reviewed by: davidxu MFC after: 2 weeks
Diffstat (limited to 'sys/kern/kern_sig.c')
-rw-r--r--sys/kern/kern_sig.c45
1 files changed, 30 insertions, 15 deletions
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 1c21bc5..c1315e7 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -357,7 +357,10 @@ sigqueue_add(sigqueue_t *sq, int signo, ksiginfo_t *si)
/* directly insert the ksi, don't copy it */
if (si->ksi_flags & KSI_INS) {
- TAILQ_INSERT_TAIL(&sq->sq_list, si, ksi_link);
+ if (si->ksi_flags & KSI_HEAD)
+ TAILQ_INSERT_HEAD(&sq->sq_list, si, ksi_link);
+ else
+ TAILQ_INSERT_TAIL(&sq->sq_list, si, ksi_link);
si->ksi_sigq = sq;
goto out_set_bit;
}
@@ -378,7 +381,10 @@ sigqueue_add(sigqueue_t *sq, int signo, ksiginfo_t *si)
p->p_pendingcnt++;
ksiginfo_copy(si, ksi);
ksi->ksi_signo = signo;
- TAILQ_INSERT_TAIL(&sq->sq_list, ksi, ksi_link);
+ if (si->ksi_flags & KSI_HEAD)
+ TAILQ_INSERT_HEAD(&sq->sq_list, ksi, ksi_link);
+ else
+ TAILQ_INSERT_TAIL(&sq->sq_list, ksi, ksi_link);
ksi->ksi_sigq = sq;
}
@@ -2492,6 +2498,7 @@ issignal(struct thread *td, int stop_allowed)
struct sigacts *ps;
struct sigqueue *queue;
sigset_t sigpending;
+ ksiginfo_t ksi;
int sig, prop, newsig;
p = td->td_proc;
@@ -2529,24 +2536,22 @@ issignal(struct thread *td, int stop_allowed)
if (p->p_flag & P_TRACED && (p->p_flag & P_PPWAIT) == 0) {
/*
* If traced, always stop.
+ * Remove old signal from queue before the stop.
+ * XXX shrug off debugger, it causes siginfo to
+ * be thrown away.
*/
+ queue = &td->td_sigqueue;
+ ksi.ksi_signo = 0;
+ if (sigqueue_get(queue, sig, &ksi) == 0) {
+ queue = &p->p_sigqueue;
+ sigqueue_get(queue, sig, &ksi);
+ }
+
mtx_unlock(&ps->ps_mtx);
newsig = ptracestop(td, sig);
mtx_lock(&ps->ps_mtx);
if (sig != newsig) {
- ksiginfo_t ksi;
-
- queue = &td->td_sigqueue;
- /*
- * clear old signal.
- * XXX shrug off debugger, it causes siginfo to
- * be thrown away.
- */
- if (sigqueue_get(queue, sig, &ksi) == 0) {
- queue = &p->p_sigqueue;
- sigqueue_get(queue, sig, &ksi);
- }
/*
* If parent wants us to take the signal,
@@ -2561,10 +2566,20 @@ issignal(struct thread *td, int stop_allowed)
* Put the new signal into td_sigqueue. If the
* signal is being masked, look for other signals.
*/
- SIGADDSET(queue->sq_signals, sig);
+ sigqueue_add(queue, sig, NULL);
if (SIGISMEMBER(td->td_sigmask, sig))
continue;
signotify(td);
+ } else {
+ if (ksi.ksi_signo != 0) {
+ ksi.ksi_flags |= KSI_HEAD;
+ if (sigqueue_add(&td->td_sigqueue, sig,
+ &ksi) != 0)
+ ksi.ksi_signo = 0;
+ }
+ if (ksi.ksi_signo == 0)
+ sigqueue_add(&td->td_sigqueue, sig,
+ NULL);
}
/*
OpenPOWER on IntegriCloud