summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mooney <patrick.f.mooney@gmail.com>2015-06-18 21:51:33 +0000
committerPatrick Mooney <patrick.f.mooney@gmail.com>2015-06-19 15:05:58 +0000
commit2cbeab03d82845ccc79464fa94f454cbe66bd0eb (patch)
tree5b588ea6e6d5805b381706e81430cc5f3e4fbeb0
parentefd56569e91a85cef85598de30efb0403398b5f8 (diff)
downloadillumos-joyent-2cbeab03d82845ccc79464fa94f454cbe66bd0eb.tar.gz
OS-4437 lxbrand ptrace turns harmless signals deadly
Reviewed by: Joshua M. Clulow <jmc@joyent.com> Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_ptrace.c36
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_brand.h6
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_misc.h2
-rw-r--r--usr/src/uts/common/os/sig.c25
-rw-r--r--usr/src/uts/common/sys/brand.h2
5 files changed, 53 insertions, 18 deletions
diff --git a/usr/src/uts/common/brand/lx/os/lx_ptrace.c b/usr/src/uts/common/brand/lx/os/lx_ptrace.c
index 559b72d222..e6135c48f3 100644
--- a/usr/src/uts/common/brand/lx/os/lx_ptrace.c
+++ b/usr/src/uts/common/brand/lx/os/lx_ptrace.c
@@ -896,6 +896,7 @@ lx_ptrace_cont(lx_lwp_data_t *remote, lx_ptrace_cont_flags_t flags, int signo)
* may fail silently if the state machine is not aligned correctly.
*/
remote->br_ptrace_stopsig = signo;
+ remote->br_ptrace_donesig = 0;
/*
* Handle the syscall-stop flag if this is a PTRACE_SYSCALL restart:
@@ -948,6 +949,7 @@ lx_ptrace_detach(lx_ptrace_accord_t *accord, lx_lwp_data_t *remote, int signo,
* or modify the delivered signal.
*/
remote->br_ptrace_stopsig = signo;
+ remote->br_ptrace_donesig = 0;
lx_ptrace_restart_lwp(rlwp);
@@ -1620,6 +1622,7 @@ lx_ptrace_issig_stop(proc_t *p, klwp_t *lwp)
* and enter the ptrace "signal-delivery-stop" condition.
*/
lwpd->br_ptrace_stopsig = lx_sig;
+ lwpd->br_ptrace_donesig = 0;
(void) lx_ptrace_stop_common(p, lwpd, LX_PR_SIGNALLED);
mutex_enter(&p->p_lock);
@@ -1670,21 +1673,46 @@ lx_ptrace_issig_stop(proc_t *p, klwp_t *lwp)
}
}
+ lwpd->br_ptrace_donesig = lwp->lwp_cursig;
lwpd->br_ptrace_stopsig = 0;
return (0);
}
boolean_t
-lx_ptrace_sig_ignorable(proc_t *p, int sig)
+lx_ptrace_sig_ignorable(proc_t *p, klwp_t *lwp, int sig)
{
lx_proc_data_t *lxpd = ptolxproc(p);
+ /*
+ * Ignored signals and ptrace:
+ *
+ * When a process is being ptraced by another, special care is needed
+ * while handling signals. Since the tracer is interested in all
+ * signals sent to the tracee, an effort must be made to initially
+ * bypass signal ignorance logic. This allows the signal to be placed
+ * in the tracee's sigqueue to be inspected and potentially altered by
+ * the tracer.
+ *
+ * A critical detail in this procedure is how a signal is handled after
+ * tracer has completed processing for the event. If the signal would
+ * have been ignored, were it not for the initial ptrace override, then
+ * lx_ptrace_sig_ignorable must report B_TRUE when the tracee is
+ * restarted and resumes signal processing. This is done by recording
+ * the most recent tracee signal consumed by ptrace.
+ */
+
if (lxpd->l_ptrace != 0 && lx_stol_signo(sig, 0) != 0) {
/*
- * In order to preserve proper ptrace behavior when it comes to
- * signal handling, it is unacceptable to ignore any signals.
- * Doing so would bypass the logic in lx_ptrace_issig_stop.
+ * This process is being ptraced. Bypass signal ignorance for
+ * anything that maps to a valid Linux signal...
*/
+ if (lwp != NULL && lwptolxlwp(lwp)->br_ptrace_donesig == sig) {
+ /*
+ * ...Unless it is a signal which has already been
+ * processed by the tracer.
+ */
+ return (B_TRUE);
+ }
return (B_FALSE);
}
return (B_TRUE);
diff --git a/usr/src/uts/common/brand/lx/sys/lx_brand.h b/usr/src/uts/common/brand/lx/sys/lx_brand.h
index 4bd7c34e5d..8325132da0 100644
--- a/usr/src/uts/common/brand/lx/sys/lx_brand.h
+++ b/usr/src/uts/common/brand/lx/sys/lx_brand.h
@@ -558,6 +558,12 @@ struct lx_lwp_data {
ushort_t br_ptrace_whatstop; /* stop sub-reason */
int32_t br_ptrace_stopsig; /* stop signal, 0 for no signal */
+ /*
+ * Track the last (native) signal number processed by a ptrace.
+ * This allows the tracee to properly handle ignored signals after
+ * the tracer has been notified and the tracee restarted.
+ */
+ int32_t br_ptrace_donesig;
uintptr_t br_ptrace_stopucp; /* usermode ucontext_t pointer */
uint_t br_ptrace_event;
diff --git a/usr/src/uts/common/brand/lx/sys/lx_misc.h b/usr/src/uts/common/brand/lx/sys/lx_misc.h
index bd0b31fadc..74a3e7a97b 100644
--- a/usr/src/uts/common/brand/lx/sys/lx_misc.h
+++ b/usr/src/uts/common/brand/lx/sys/lx_misc.h
@@ -73,7 +73,7 @@ extern int lx_ptrace_stop_for_option(int, boolean_t, ulong_t, uintptr_t);
extern int lx_ptrace_set_clone_inherit(int, boolean_t);
extern int lx_sigcld_repost(proc_t *, sigqueue_t *);
extern int lx_ptrace_issig_stop(proc_t *, klwp_t *);
-extern boolean_t lx_ptrace_sig_ignorable(proc_t *, int);
+extern boolean_t lx_ptrace_sig_ignorable(proc_t *, klwp_t *, int);
extern int lx_helper_clone(int64_t *, int, void *, void *, void *);
extern int lx_helper_setgroups(int, gid_t *);
diff --git a/usr/src/uts/common/os/sig.c b/usr/src/uts/common/os/sig.c
index a2cfb44ee7..2f727a504e 100644
--- a/usr/src/uts/common/os/sig.c
+++ b/usr/src/uts/common/os/sig.c
@@ -155,12 +155,12 @@ signal_is_blocked(kthread_t *t, int sig)
* forbidden by any process branding.
*/
static int
-sig_ignorable(proc_t *p, int sig)
+sig_ignorable(proc_t *p, klwp_t *lwp, int sig)
{
return (sigismember(&p->p_ignore, sig) && /* sig in ignore mask */
!(PROC_IS_BRANDED(p) && /* allowed by brand */
BROP(p)->b_sig_ignorable != NULL &&
- BROP(p)->b_sig_ignorable(p, sig) == B_FALSE));
+ BROP(p)->b_sig_ignorable(p, lwp, sig) == B_FALSE));
}
@@ -176,12 +176,13 @@ sig_ignorable(proc_t *p, int sig)
* the signal is not being accepted via sigwait()
*/
static int
-sig_discardable(proc_t *p, int sig)
+sig_discardable(proc_t *p, kthread_t *tp, int sig)
{
kthread_t *t = p->p_tlist;
+ klwp_t *lwp = (tp == NULL) ? NULL : tp->t_lwp;
return (t == NULL || /* if zombie or ... */
- (sig_ignorable(p, sig) && /* signal is ignored */
+ (sig_ignorable(p, lwp, sig) && /* signal is ignored */
t->t_forw == t && /* and single-threaded */
!tracing(p, sig) && /* and no /proc tracing */
!signal_is_blocked(t, sig) && /* and signal not blocked */
@@ -314,7 +315,7 @@ sigtoproc(proc_t *p, kthread_t *t, int sig)
}
}
- if (sig_discardable(p, sig)) {
+ if (sig_discardable(p, t, sig)) {
DTRACE_PROC3(signal__discard, kthread_t *, p->p_tlist,
proc_t *, p, int, sig);
return;
@@ -514,7 +515,7 @@ issig_justlooking(void)
if (sigismember(&set, sig) &&
(tracing(p, sig) ||
sigismember(&t->t_sigwait, sig) ||
- !sig_ignorable(p, sig))) {
+ !sig_ignorable(p, lwp, sig))) {
/*
* Don't promote a signal that will stop
* the process when lwp_nostop is set.
@@ -688,7 +689,7 @@ issig_forreal(void)
lwp->lwp_cursig = 0;
lwp->lwp_extsig = 0;
if (sigismember(&t->t_sigwait, sig) ||
- (!sig_ignorable(p, sig) &&
+ (!sig_ignorable(p, lwp, sig) &&
!isjobstop(sig))) {
if (p->p_flag & (SEXITLWPS|SKILLED)) {
sig = SIGKILL;
@@ -740,7 +741,7 @@ issig_forreal(void)
toproc = 0;
if (tracing(p, sig) ||
sigismember(&t->t_sigwait, sig) ||
- !sig_ignorable(p, sig)) {
+ !sig_ignorable(p, lwp, sig)) {
if (sigismember(&t->t_extsig, sig))
ext = 1;
break;
@@ -754,7 +755,7 @@ issig_forreal(void)
toproc = 1;
if (tracing(p, sig) ||
sigismember(&t->t_sigwait, sig) ||
- !sig_ignorable(p, sig)) {
+ !sig_ignorable(p, lwp, sig)) {
if (sigismember(&p->p_extsig, sig))
ext = 1;
break;
@@ -1376,7 +1377,7 @@ psig(void)
* this signal from pending to current (we dropped p->p_lock).
* This can happen only in a multi-threaded process.
*/
- if (sig_ignorable(p, sig) ||
+ if (sig_ignorable(p, lwp, sig) ||
(func == SIG_DFL && sigismember(&stopdefault, sig))) {
lwp->lwp_cursig = 0;
lwp->lwp_extsig = 0;
@@ -2176,7 +2177,7 @@ sigaddqa(proc_t *p, kthread_t *t, sigqueue_t *sigqp)
ASSERT(MUTEX_HELD(&p->p_lock));
ASSERT(sig >= 1 && sig < NSIG);
- if (sig_discardable(p, sig))
+ if (sig_discardable(p, t, sig))
siginfofree(sigqp);
else
sigaddqins(p, t, sigqp);
@@ -2202,7 +2203,7 @@ sigaddq(proc_t *p, kthread_t *t, k_siginfo_t *infop, int km_flags)
* blocking the signal (it *could* change it's mind while
* the signal is pending) then don't bother creating one.
*/
- if (!sig_discardable(p, sig) &&
+ if (!sig_discardable(p, t, sig) &&
(sigismember(&p->p_siginfo, sig) ||
(curproc->p_ct_process != p->p_ct_process) ||
(sig == SIGCLD && SI_FROMKERNEL(infop))) &&
diff --git a/usr/src/uts/common/sys/brand.h b/usr/src/uts/common/sys/brand.h
index 0af67a1908..f753902bab 100644
--- a/usr/src/uts/common/sys/brand.h
+++ b/usr/src/uts/common/sys/brand.h
@@ -183,7 +183,7 @@ struct brand_ops {
boolean_t *, int *);
int (*b_sigcld_repost)(proc_t *, sigqueue_t *);
int (*b_issig_stop)(proc_t *, klwp_t *);
- boolean_t (*b_sig_ignorable)(proc_t *, int);
+ boolean_t (*b_sig_ignorable)(proc_t *, klwp_t *, int);
void (*b_savecontext)(ucontext_t *);
#if defined(_SYSCALL32_IMPL)
void (*b_savecontext32)(ucontext32_t *);