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,11 +20,11 @@
  */
 
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
- * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
+ * Copyright 2015, Joyent, Inc.
  */
 
 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
 /*        All Rights Reserved   */
 

@@ -59,10 +59,11 @@
 #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,10 +147,25 @@
         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,16 +173,17 @@
  *      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)
+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 ... */
-            (sigismember(&p->p_ignore, 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 */
             !sigismember(&t->t_sigwait, sig))); /* and not being accepted */
 }

@@ -198,11 +215,11 @@
                         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;
+                        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,11 +312,11 @@
                                 sigdelset(&tt->t_extsig, SIGCONT);
                         } while ((tt = tt->t_forw) != p->p_tlist);
                 }
         }
 
-        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;
         }
 

@@ -495,11 +512,11 @@
 
                 for (sig = 1; sig < NSIG; sig++) {
                         if (sigismember(&set, sig) &&
                             (tracing(p, sig) ||
                             sigismember(&t->t_sigwait, sig) ||
-                            !sigismember(&p->p_ignore, 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,10 +638,25 @@
                         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,11 +686,11 @@
                 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) &&
+                            (!sig_ignorable(p, lwp, sig) &&
                             !isjobstop(sig))) {
                                 if (p->p_flag & (SEXITLWPS|SKILLED)) {
                                         sig = SIGKILL;
                                         ext = (p->p_flag & SEXTKILLED) != 0;
                                 }

@@ -706,11 +738,11 @@
                 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)) {
+                                    !sig_ignorable(p, lwp, sig)) {
                                         if (sigismember(&t->t_extsig, sig))
                                                 ext = 1;
                                         break;
                                 }
                                 sigdelset(&t->t_sig, sig);

@@ -720,11 +752,11 @@
                                 if (sig == SIGCLD)
                                         sigcld_found = 1;
                                 toproc = 1;
                                 if (tracing(p, sig) ||
                                     sigismember(&t->t_sigwait, sig) ||
-                                    !sigismember(&p->p_ignore, sig)) {
+                                    !sig_ignorable(p, lwp, sig)) {
                                         if (sigismember(&p->p_extsig, sig))
                                                 ext = 1;
                                         break;
                                 }
                                 sigdelset(&p->p_sig, sig);

@@ -952,10 +984,20 @@
                         }
                         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,11 +1105,11 @@
                          */
                         cv_broadcast(&p->p_holdlwps);
                 }
         }
 
-        if (why != PR_JOBCONTROL && why != PR_CHECKPOINT) {
+        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,10 +1211,17 @@
          * 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,11 +1239,11 @@
                     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;
+                        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,11 +1374,11 @@
         /*
          * 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) ||
+        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,13 +1818,16 @@
                 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.
+                         * Use KM_NOSLEEP to avoid deadlock. The child procs
+                         * initpid can be 1 for zlogin.
                          */
-                        ASSERT(pp == proc_init);
+                        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,10 +1854,19 @@
         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,11 +2174,11 @@
         int sig = sigqp->sq_info.si_signo;
 
         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);
 
         sigtoproc(p, t, sig);

@@ -2139,11 +2200,11 @@
          * 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) &&
+        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));