diff options
author | Patrick Mooney <patrick.f.mooney@gmail.com> | 2015-06-18 21:51:33 +0000 |
---|---|---|
committer | Patrick Mooney <patrick.f.mooney@gmail.com> | 2015-06-19 15:05:58 +0000 |
commit | 2cbeab03d82845ccc79464fa94f454cbe66bd0eb (patch) | |
tree | 5b588ea6e6d5805b381706e81430cc5f3e4fbeb0 | |
parent | efd56569e91a85cef85598de30efb0403398b5f8 (diff) | |
download | illumos-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.c | 36 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/sys/lx_brand.h | 6 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/sys/lx_misc.h | 2 | ||||
-rw-r--r-- | usr/src/uts/common/os/sig.c | 25 | ||||
-rw-r--r-- | usr/src/uts/common/sys/brand.h | 2 |
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 *); |