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));