Print this page
OS-4437 lxbrand ptrace turns harmless signals deadly
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
OS-3742 lxbrand add support for signalfd
OS-4382 remove obsolete brand hooks added during lx development
OS-4306 lxbrand setsockopt(IP_MULTICAST_TTL) handles optlen poorly
OS-4303 lxbrand ltp ptrace05 fails
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
OS-3820 lxbrand ptrace(2): the next generation
OS-3685 lxbrand PTRACE_O_TRACEFORK race condition
OS-3834 lxbrand 64-bit strace(1) reports 64-bit process as using x32 ABI
OS-3794 lxbrand panic on init signal death
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Bryan Cantrill <bryan@joyent.com>
OS-3642 failed assert when exiting zlogin due to OS-3140 fix
OS-3140 In LX zone 'ps fax' does not show all processes
OS-2844 lx brand should support 64-bit user-land

*** 20,30 **** */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. ! * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ --- 20,30 ---- */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. ! * Copyright 2015, Joyent, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */
*** 59,68 **** --- 59,69 ---- #include <sys/contract/process_impl.h> #include <sys/cyclic.h> #include <sys/dtrace.h> #include <sys/sdt.h> #include <sys/signalfd.h> + #include <sys/brand.h> const k_sigset_t nullsmask = {0, 0, 0}; const k_sigset_t fillset = /* MUST be contiguous */ {FILLSET0, FILLSET1, FILLSET2};
*** 146,155 **** --- 147,171 ---- return (sigismember(&t->t_hold, sig) || (schedctl_sigblock(t) && !sigismember(&cantmask, sig))); } /* + * Return true if the signal can safely be ignored. + * That is, if the signal is included in the p_ignore mask and doing so is not + * forbidden by any process branding. + */ + static int + 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, lwp, sig) == B_FALSE)); + + } + + /* * Return true if the signal can safely be discarded on generation. * That is, if there is no need for the signal on the receiving end. * The answer is true if the process is a zombie or * if all of these conditions are true: * the signal is being ignored
*** 157,172 **** * the signal is not being traced by /proc * the signal is not blocked by the process * the signal is not being accepted via sigwait() */ static int ! sig_discardable(proc_t *p, int sig) { kthread_t *t = p->p_tlist; return (t == NULL || /* if zombie or ... */ ! (sigismember(&p->p_ignore, 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 */ !sigismember(&t->t_sigwait, sig))); /* and not being accepted */ } --- 173,189 ---- * the signal is not being traced by /proc * the signal is not blocked by the process * the signal is not being accepted via sigwait() */ static int ! 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, 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 */ !sigismember(&t->t_sigwait, sig))); /* and not being accepted */ }
*** 198,208 **** rval = 1; } else if (t->t_state == TS_STOPPED && sig == SIGKILL && !(ttoproc(t)->p_proc_flag & P_PR_LOCK)) { ttoproc(t)->p_stopsig = 0; t->t_dtrace_stop = 0; ! t->t_schedflag |= TS_XSTART | TS_PSTART; setrun_locked(t); } else if (t != curthread && t->t_state == TS_ONPROC) { aston(t); /* make it do issig promptly */ if (t->t_cpu != CPU) poke_cpu(t->t_cpu->cpu_id); --- 215,225 ---- rval = 1; } else if (t->t_state == TS_STOPPED && sig == SIGKILL && !(ttoproc(t)->p_proc_flag & P_PR_LOCK)) { ttoproc(t)->p_stopsig = 0; t->t_dtrace_stop = 0; ! t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART; setrun_locked(t); } else if (t != curthread && t->t_state == TS_ONPROC) { aston(t); /* make it do issig promptly */ if (t->t_cpu != CPU) poke_cpu(t->t_cpu->cpu_id);
*** 295,305 **** sigdelset(&tt->t_extsig, SIGCONT); } while ((tt = tt->t_forw) != p->p_tlist); } } ! if (sig_discardable(p, sig)) { DTRACE_PROC3(signal__discard, kthread_t *, p->p_tlist, proc_t *, p, int, sig); return; } --- 312,322 ---- sigdelset(&tt->t_extsig, SIGCONT); } while ((tt = tt->t_forw) != p->p_tlist); } } ! if (sig_discardable(p, t, sig)) { DTRACE_PROC3(signal__discard, kthread_t *, p->p_tlist, proc_t *, p, int, sig); return; }
*** 495,505 **** for (sig = 1; sig < NSIG; sig++) { if (sigismember(&set, sig) && (tracing(p, sig) || sigismember(&t->t_sigwait, sig) || ! !sigismember(&p->p_ignore, sig))) { /* * Don't promote a signal that will stop * the process when lwp_nostop is set. */ if (!lwp->lwp_nostop || --- 512,522 ---- for (sig = 1; sig < NSIG; sig++) { if (sigismember(&set, sig) && (tracing(p, sig) || sigismember(&t->t_sigwait, sig) || ! !sig_ignorable(p, lwp, sig))) { /* * Don't promote a signal that will stop * the process when lwp_nostop is set. */ if (!lwp->lwp_nostop ||
*** 621,630 **** --- 638,662 ---- stop(PR_SUSPENDED, SUSPEND_NORMAL); continue; } /* + * Allow the brand the chance to alter (or suppress) delivery + * of this signal. + */ + if (PROC_IS_BRANDED(p) && BROP(p)->b_issig_stop != NULL) { + /* + * The brand hook will return 0 if it would like + * us to drive on, or -1 if we should restart + * the loop to check other conditions. + */ + if (BROP(p)->b_issig_stop(p, lwp) != 0) { + continue; + } + } + + /* * Honor requested stop before dealing with the * current signal; a debugger may change it. * Do not want to go back to loop here since this is a special * stop that means: make incremental progress before the next * stop. The danger is that returning to top of loop would most
*** 654,664 **** if ((sig = lwp->lwp_cursig) != 0) { ext = lwp->lwp_extsig; lwp->lwp_cursig = 0; lwp->lwp_extsig = 0; if (sigismember(&t->t_sigwait, sig) || ! (!sigismember(&p->p_ignore, sig) && !isjobstop(sig))) { if (p->p_flag & (SEXITLWPS|SKILLED)) { sig = SIGKILL; ext = (p->p_flag & SEXTKILLED) != 0; } --- 686,696 ---- if ((sig = lwp->lwp_cursig) != 0) { ext = lwp->lwp_extsig; lwp->lwp_cursig = 0; lwp->lwp_extsig = 0; if (sigismember(&t->t_sigwait, sig) || ! (!sig_ignorable(p, lwp, sig) && !isjobstop(sig))) { if (p->p_flag & (SEXITLWPS|SKILLED)) { sig = SIGKILL; ext = (p->p_flag & SEXTKILLED) != 0; }
*** 706,716 **** for (;;) { if ((sig = fsig(&t->t_sig, t)) != 0) { toproc = 0; if (tracing(p, sig) || sigismember(&t->t_sigwait, sig) || ! !sigismember(&p->p_ignore, sig)) { if (sigismember(&t->t_extsig, sig)) ext = 1; break; } sigdelset(&t->t_sig, sig); --- 738,748 ---- for (;;) { if ((sig = fsig(&t->t_sig, t)) != 0) { toproc = 0; if (tracing(p, sig) || sigismember(&t->t_sigwait, sig) || ! !sig_ignorable(p, lwp, sig)) { if (sigismember(&t->t_extsig, sig)) ext = 1; break; } sigdelset(&t->t_sig, sig);
*** 720,730 **** if (sig == SIGCLD) sigcld_found = 1; toproc = 1; if (tracing(p, sig) || sigismember(&t->t_sigwait, sig) || ! !sigismember(&p->p_ignore, sig)) { if (sigismember(&p->p_extsig, sig)) ext = 1; break; } sigdelset(&p->p_sig, sig); --- 752,762 ---- if (sig == SIGCLD) sigcld_found = 1; toproc = 1; if (tracing(p, sig) || sigismember(&t->t_sigwait, sig) || ! !sig_ignorable(p, lwp, sig)) { if (sigismember(&p->p_extsig, sig)) ext = 1; break; } sigdelset(&p->p_sig, sig);
*** 952,961 **** --- 984,1003 ---- } flags &= ~TS_CSTART; } break; + case PR_BRAND: + /* + * We have been stopped by the brand code for a brand-private + * reason. This is an asynchronous stop affecting only this + * LWP. + */ + VERIFY(PROC_IS_BRANDED(p)); + flags &= ~TS_BSTART; + break; + default: /* /proc stop */ flags &= ~TS_PSTART; /* * Do synchronous stop unless the async-stop flag is set. * If why is PR_REQUESTED and t->t_dtrace_stop flag is set,
*** 1063,1073 **** */ cv_broadcast(&p->p_holdlwps); } } ! if (why != PR_JOBCONTROL && why != PR_CHECKPOINT) { /* * Do process-level notification when all lwps are * either stopped on events of interest to /proc * or are stopped showing PR_SUSPENDED or are zombies. */ --- 1105,1115 ---- */ cv_broadcast(&p->p_holdlwps); } } ! if (why != PR_JOBCONTROL && why != PR_CHECKPOINT && why != PR_BRAND) { /* * Do process-level notification when all lwps are * either stopped on events of interest to /proc * or are stopped showing PR_SUSPENDED or are zombies. */
*** 1169,1178 **** --- 1211,1227 ---- * stopped) because we can't call mutex_enter from a stopped thread. */ if (why == PR_CHECKPOINT) del_one_utstop(); + /* + * Allow the brand to post notification of this stop condition. + */ + if (PROC_IS_BRANDED(p) && BROP(p)->b_stop_notify != NULL) { + BROP(p)->b_stop_notify(p, lwp, why, what); + } + thread_lock(t); ASSERT((t->t_schedflag & TS_ALLSTART) == 0); t->t_schedflag |= flags; t->t_whystop = (short)why; t->t_whatstop = (short)what;
*** 1190,1200 **** sigismember(&p->p_sig, SIGKILL) || (t->t_proc_flag & TP_LWPEXIT) || (p->p_flag & (SEXITLWPS|SKILLED))) { p->p_stopsig = 0; thread_lock(t); ! t->t_schedflag |= TS_XSTART | TS_PSTART; setrun_locked(t); thread_unlock_nopreempt(t); } else if (why == PR_JOBCONTROL) { if (p->p_flag & SSCONT) { /* --- 1239,1249 ---- sigismember(&p->p_sig, SIGKILL) || (t->t_proc_flag & TP_LWPEXIT) || (p->p_flag & (SEXITLWPS|SKILLED))) { p->p_stopsig = 0; thread_lock(t); ! t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART; setrun_locked(t); thread_unlock_nopreempt(t); } else if (why == PR_JOBCONTROL) { if (p->p_flag & SSCONT) { /*
*** 1325,1335 **** /* * The signal disposition could have changed since we promoted * this signal from pending to current (we dropped p->p_lock). * This can happen only in a multi-threaded process. */ ! if (sigismember(&p->p_ignore, sig) || (func == SIG_DFL && sigismember(&stopdefault, sig))) { lwp->lwp_cursig = 0; lwp->lwp_extsig = 0; if (lwp->lwp_curinfo) { siginfofree(lwp->lwp_curinfo); --- 1374,1384 ---- /* * The signal disposition could have changed since we promoted * this signal from pending to current (we dropped p->p_lock). * This can happen only in a multi-threaded process. */ ! if (sig_ignorable(p, lwp, sig) || (func == SIG_DFL && sigismember(&stopdefault, sig))) { lwp->lwp_cursig = 0; lwp->lwp_extsig = 0; if (lwp->lwp_curinfo) { siginfofree(lwp->lwp_curinfo);
*** 1769,1781 **** cp->p_pidflag &= ~CLDPEND; if (sqp == NULL) { /* * This can only happen when the parent is init. * (See call to sigcld(q, NULL) in exit().) ! * Use KM_NOSLEEP to avoid deadlock. */ ! ASSERT(pp == proc_init); winfo(cp, &info, 0); sigaddq(pp, NULL, &info, KM_NOSLEEP); } else { winfo(cp, &sqp->sq_info, 0); sigaddqa(pp, NULL, sqp); --- 1818,1833 ---- cp->p_pidflag &= ~CLDPEND; if (sqp == NULL) { /* * This can only happen when the parent is init. * (See call to sigcld(q, NULL) in exit().) ! * Use KM_NOSLEEP to avoid deadlock. The child procs ! * initpid can be 1 for zlogin. */ ! ASSERT(pp->p_pidp->pid_id == ! cp->p_zone->zone_proc_initpid || ! pp->p_pidp->pid_id == 1); winfo(cp, &info, 0); sigaddq(pp, NULL, &info, KM_NOSLEEP); } else { winfo(cp, &sqp->sq_info, 0); sigaddqa(pp, NULL, sqp);
*** 1802,1811 **** --- 1854,1872 ---- proc_t *cp; sigqueue_t *sqp; sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP); mutex_enter(&pidlock); + if (PROC_IS_BRANDED(pp) && BROP(pp)->b_sigcld_repost != NULL) { + /* + * Allow the brand to inject synthetic SIGCLD signals. + */ + if (BROP(pp)->b_sigcld_repost(pp, sqp) == 0) { + mutex_exit(&pidlock); + return; + } + } for (cp = pp->p_child; cp; cp = cp->p_sibling) { if (cp->p_pidflag & CLDPEND) { post_sigcld(cp, sqp); mutex_exit(&pidlock); return;
*** 2113,2123 **** int sig = sigqp->sq_info.si_signo; ASSERT(MUTEX_HELD(&p->p_lock)); ASSERT(sig >= 1 && sig < NSIG); ! if (sig_discardable(p, sig)) siginfofree(sigqp); else sigaddqins(p, t, sigqp); sigtoproc(p, t, sig); --- 2174,2184 ---- int sig = sigqp->sq_info.si_signo; ASSERT(MUTEX_HELD(&p->p_lock)); ASSERT(sig >= 1 && sig < NSIG); ! if (sig_discardable(p, t, sig)) siginfofree(sigqp); else sigaddqins(p, t, sigqp); sigtoproc(p, t, sig);
*** 2139,2149 **** * If the signal will be discarded by sigtoproc() or * if the process isn't requesting siginfo and it isn't * 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) && (sigismember(&p->p_siginfo, sig) || (curproc->p_ct_process != p->p_ct_process) || (sig == SIGCLD && SI_FROMKERNEL(infop))) && ((sqp = kmem_alloc(sizeof (sigqueue_t), km_flags)) != NULL)) { bcopy(infop, &sqp->sq_info, sizeof (k_siginfo_t)); --- 2200,2210 ---- * If the signal will be discarded by sigtoproc() or * if the process isn't requesting siginfo and it isn't * blocking the signal (it *could* change it's mind while * the signal is pending) then don't bother creating one. */ ! if (!sig_discardable(p, t, sig) && (sigismember(&p->p_siginfo, sig) || (curproc->p_ct_process != p->p_ct_process) || (sig == SIGCLD && SI_FROMKERNEL(infop))) && ((sqp = kmem_alloc(sizeof (sigqueue_t), km_flags)) != NULL)) { bcopy(infop, &sqp->sq_info, sizeof (k_siginfo_t));