Print this page
    
OS-4716 lxbrand native system(3C) broken
Reviewed by: jerry.jelinek@joyent.com
Reviewed by: patrick.mooney@joyent.com
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/lib/libc/port/stdio/system.c
          +++ new/usr/src/lib/libc/port/stdio/system.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  
    | 
      ↓ open down ↓ | 
    14 lines elided | 
    
      ↑ open up ↑ | 
  
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24   24   * Use is subject to license terms.
       25 + * Copyright 2016 Joyent, Inc.
  25   26   */
  26   27  
  27   28  /*      Copyright (c) 1988 AT&T */
  28   29  /*        All Rights Reserved   */
  29   30  
  30   31  #include "lint.h"
  31   32  #include "mtlib.h"
  32   33  #include <sys/types.h>
  33   34  #include <sys/wait.h>
  34   35  #include <signal.h>
  35   36  #include <stdlib.h>
  
    | 
      ↓ open down ↓ | 
    1 lines elided | 
    
      ↑ open up ↑ | 
  
  36   37  #include <wait.h>
  37   38  #include <sys/stat.h>
  38   39  #include <unistd.h>
  39   40  #include <memory.h>
  40   41  #include <thread.h>
  41   42  #include <pthread.h>
  42   43  #include <errno.h>
  43   44  #include <synch.h>
  44   45  #include <spawn.h>
  45   46  #include <paths.h>
       47 +#include <zone.h>
  46   48  #include "libc.h"
  47   49  
  48   50  extern const char **_environ;
  49   51  
  50   52  extern int __xpg4;      /* defined in _xpg4.c; 0 if not xpg4-compiled program */
  51   53  extern const sigset_t maskset;          /* all maskable signals */
  52   54  
  53   55  static mutex_t sys_lock = DEFAULTMUTEX; /* protects the following */
  54   56  static uint_t sys_count = 0;            /* number of threads in system() */
  55   57  static struct sigaction sys_ibuf;       /* saved SIGINT sigaction */
  56   58  static struct sigaction sys_qbuf;       /* saved SIGQUIT sigaction */
  57   59  static struct sigaction ignore = {0, {SIG_IGN}, {0}};
  58   60  
  59   61  /*
  60   62   * Things needed by the cancellation cleanup handler.
  61   63   */
  62   64  typedef struct {
  63   65          sigset_t        savemask;       /* saved signal mask */
  64   66          pid_t           pid;            /* if nonzero, the child's pid */
  65   67  } cleanup_t;
  66   68  
  67   69  /*
  68   70   * Daemon thread whose sole function is to reap an abandoned child.
  69   71   * Also invoked from pclose() (see port/stdio/popen.c).
  70   72   */
  71   73  void *
  72   74  reapchild(void *arg)
  73   75  {
  74   76          pid_t pid = (pid_t)(uintptr_t)arg;
  75   77          int cancel_state;
  76   78  
  77   79          (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
  78   80          while (waitpid(pid, NULL, 0) == -1) {
  79   81                  if (errno != EINTR)
  80   82                          break;
  81   83          }
  82   84          (void) pthread_setcancelstate(cancel_state, NULL);
  83   85          return (NULL);
  84   86  }
  85   87  
  86   88  /*
  87   89   * Cancellation cleanup handler.
  88   90   * If we were cancelled in waitpid(), create a daemon thread to
  89   91   * reap our abandoned child.  No other thread can do this for us.
  90   92   * It would be better if there were a system call to disinherit
  91   93   * a child process (give it to init, just as though we exited).
  92   94   */
  93   95  static void
  94   96  cleanup(void *arg)
  95   97  {
  96   98          cleanup_t *cup = arg;
  97   99  
  98  100          if (cup->pid != 0) {    /* we were cancelled; abandoning our pid */
  99  101                  (void) thr_sigsetmask(SIG_SETMASK, &maskset, NULL);
 100  102                  (void) thr_create(NULL, 0,
 101  103                      reapchild, (void *)(uintptr_t)cup->pid,
 102  104                      THR_DAEMON, NULL);
 103  105          }
 104  106  
 105  107          lmutex_lock(&sys_lock);
 106  108          if (--sys_count == 0) {         /* leaving system() */
 107  109                  /*
 108  110                   * There are no remaining threads in system(), so
 109  111                   * restore the SIGINT and SIGQUIT signal actions.
 110  112                   */
 111  113                  (void) sigaction(SIGINT, &sys_ibuf, NULL);
 112  114                  (void) sigaction(SIGQUIT, &sys_qbuf, NULL);
 113  115          }
 114  116          lmutex_unlock(&sys_lock);
 115  117  
 116  118          (void) thr_sigsetmask(SIG_SETMASK, &cup->savemask, NULL);
 117  119  }
  
    | 
      ↓ open down ↓ | 
    62 lines elided | 
    
      ↑ open up ↑ | 
  
 118  120  
 119  121  int
 120  122  system(const char *cmd)
 121  123  {
 122  124          cleanup_t cu;
 123  125          pid_t w;
 124  126          int status;
 125  127          int error;
 126  128          sigset_t mask;
 127  129          struct stat64 buf;
 128      -        const char *shpath = _PATH_BSHELL;
      130 +        char shpath[MAXPATHLEN];
      131 +        const char *zroot = zone_get_nroot();
 129  132          char *argv[4];
 130  133          posix_spawnattr_t attr;
 131  134          static const char *shell = "sh";
 132  135  
      136 +        /*
      137 +         * If executing in brand use native root.
      138 +         */
      139 +        (void) snprintf(shpath, sizeof (shpath), "%s%s",
      140 +            zroot != NULL ? zroot : "", _PATH_BSHELL);
      141 +
 133  142          if (cmd == NULL) {
 134  143                  if (stat64(shpath, &buf) != 0) {
 135  144                          return (0);
 136  145                  } else if (getuid() == buf.st_uid) {
 137  146                          /* exec for user */
 138  147                          if ((buf.st_mode & 0100) == 0)
 139  148                                  return (0);
 140  149                  } else if (getgid() == buf.st_gid) {
 141  150                          /* exec for group */
 142  151                          if ((buf.st_mode & 0010) == 0)
 143  152                                  return (0);
 144  153                  } else if ((buf.st_mode & 0001) == 0) { /* exec for others */
 145  154                          return (0);
 146  155                  }
 147  156                  return (1);
 148  157          }
 149  158  
 150  159          /*
 151  160           * Initialize the posix_spawn() attributes structure.
 152  161           *
 153  162           * The setting of POSIX_SPAWN_WAITPID_NP ensures that no
 154  163           * wait-for-multiple wait() operation will reap our child
 155  164           * and that the child will not be automatically reaped due
 156  165           * to the disposition of SIGCHLD being set to be ignored.
 157  166           * Only a specific wait for the specific pid will be able
 158  167           * to reap the child.  Since no other thread knows the pid
 159  168           * of our child, this should be safe enough.
 160  169           *
 161  170           * The POSIX_SPAWN_NOEXECERR_NP flag tells posix_spawn() not
 162  171           * to fail if the shell cannot be executed, but rather cause
 163  172           * a child to be created that simply performs _exit(127).
 164  173           * This is in order to satisfy the Posix requirement on system():
 165  174           *      The system function shall behave as if a child process were
 166  175           *      created using fork(), and the child process invoked the sh
 167  176           *      utility using execl().  If some error prevents the command
 168  177           *      language interpreter from executing after the child process
 169  178           *      is created, the return value from system() shall be as if
 170  179           *      the command language interpreter had terminated using
 171  180           *      exit(127) or _exit(127).
 172  181           */
 173  182          error = posix_spawnattr_init(&attr);
 174  183          if (error == 0)
 175  184                  error = posix_spawnattr_setflags(&attr,
 176  185                      POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF |
 177  186                      POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP |
 178  187                      POSIX_SPAWN_NOEXECERR_NP);
 179  188  
 180  189          /*
 181  190           * The POSIX spec for system() requires us to block SIGCHLD,
 182  191           * the rationale being that the process's signal handler for
 183  192           * SIGCHLD, if any, should not be called when our child exits.
 184  193           * This doesn't work for a multithreaded process because some
 185  194           * other thread could receive the SIGCHLD.
 186  195           *
 187  196           * The above setting of POSIX_SPAWN_NOSIGCHLD_NP ensures that no
 188  197           * SIGCHLD signal will be posted for our child when it exits, so
 189  198           * we don't have to block SIGCHLD to meet the intent of the spec.
 190  199           * We block SIGCHLD anyway, just because the spec requires it.
 191  200           */
 192  201          (void) sigemptyset(&mask);
 193  202          (void) sigaddset(&mask, SIGCHLD);
 194  203          (void) thr_sigsetmask(SIG_BLOCK, &mask, &cu.savemask);
 195  204          /*
 196  205           * Tell posix_spawn() to restore the signal mask in the child.
 197  206           */
 198  207          if (error == 0)
 199  208                  error = posix_spawnattr_setsigmask(&attr, &cu.savemask);
 200  209  
 201  210          /*
 202  211           * We are required to set the disposition of SIGINT and SIGQUIT
 203  212           * to be ignored for the duration of the system() operation.
 204  213           *
 205  214           * We allow more than one thread to call system() concurrently by
 206  215           * keeping a count of such threads.  The signal actions are set
 207  216           * to SIG_IGN when the first thread calls system().  They are
 208  217           * restored in cleanup() when the last thread exits system().
 209  218           *
 210  219           * However, system() is still MT-unsafe because sigaction() has
 211  220           * a process-wide effect and some other thread may also be
 212  221           * setting the signal actions for SIGINT or SIGQUIT.
 213  222           */
 214  223          lmutex_lock(&sys_lock);
 215  224          if (sys_count++ == 0) {
 216  225                  (void) sigaction(SIGINT, &ignore, &sys_ibuf);
 217  226                  (void) sigaction(SIGQUIT, &ignore, &sys_qbuf);
 218  227          }
 219  228          lmutex_unlock(&sys_lock);
 220  229  
 221  230          /*
 222  231           * If SIGINT and SIGQUIT were not already SIG_IGN, tell
 223  232           * posix_spawn() to make them SIG_DFL in the child,
 224  233           * else leave them as SIG_IGN in the child.
 225  234           */
 226  235          (void) sigemptyset(&mask);
 227  236          if (sys_ibuf.sa_handler != SIG_IGN)
 228  237                  (void) sigaddset(&mask, SIGINT);
 229  238          if (sys_qbuf.sa_handler != SIG_IGN)
 230  239                  (void) sigaddset(&mask, SIGQUIT);
 231  240          if (error == 0)
 232  241                  error = posix_spawnattr_setsigdefault(&attr, &mask);
 233  242  
 234  243          argv[0] = (char *)shell;
 235  244          argv[1] = "-c";
 236  245          argv[2] = (char *)cmd;
 237  246          argv[3] = NULL;
 238  247          if (error == 0)
 239  248                  error = posix_spawn(&cu.pid, shpath, NULL, &attr,
 240  249                      (char *const *)argv, (char *const *)_environ);
 241  250  
 242  251          (void) posix_spawnattr_destroy(&attr);
 243  252  
 244  253          if (error) {
 245  254                  errno = error;
 246  255                  status = -1;
 247  256          } else {
 248  257                  /*
 249  258                   * system() is a cancellation point and so is waitpid().
 250  259                   */
 251  260                  pthread_cleanup_push(cleanup, &cu);
 252  261                  do {
 253  262                          w = waitpid(cu.pid, &status, 0);
 254  263                  } while (w == -1 && errno == EINTR);
 255  264                  pthread_cleanup_pop(0);
 256  265                  if (w == -1)
 257  266                          status = -1;
 258  267          }
 259  268          error = errno;
 260  269          cu.pid = 0;
 261  270          cleanup(&cu);
 262  271          errno = error;
 263  272  
 264  273          return (status);
 265  274  }
  
    | 
      ↓ open down ↓ | 
    123 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX