Print this page
    
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/fs/proc/prargv.c
          +++ new/usr/src/uts/common/fs/proc/prargv.c
   1    1  /*
   2    2   * This file and its contents are supplied under the terms of the
   3    3   * Common Development and Distribution License ("CDDL"), version 1.0.
   4    4   * You may only use this file in accordance with the terms of version
   5    5   * 1.0 of the CDDL.
   6    6   *
   7    7   * A full copy of the text of the CDDL should have accompanied this
   8    8   * source.  A copy of the CDDL is also available via the Internet at
   9    9   * http://www.illumos.org/license/CDDL.
  10   10   */
  11   11  
  12   12  /*
  13   13   * Copyright 2015, Joyent, Inc.
  14   14   */
  15   15  
  16   16  #include <sys/types.h>
  17   17  #include <sys/sunddi.h>
  18   18  #include <sys/proc.h>
  19   19  #include <sys/procfs.h>
  20   20  #include <sys/sysmacros.h>
  21   21  #include <vm/as.h>
  22   22  
  23   23  /*
  24   24   * Safely read a contiguous region of memory from 'addr' in the address space
  25   25   * of a particular process into the supplied kernel buffer (*buf, sz).
  26   26   * Partially mapped regions will result in a partial read terminating at the
  27   27   * first hole in the address space.  The number of bytes actually read is
  28   28   * returned to the caller via 'rdsz'.
  29   29   */
  30   30  int
  31   31  prreadbuf(proc_t *p, uintptr_t ustart, uint8_t *buf, size_t sz, size_t *rdsz)
  32   32  {
  33   33          int error = 0;
  34   34          size_t rem = sz;
  35   35          off_t pos = 0;
  36   36  
  37   37          if (rdsz != NULL)
  38   38                  *rdsz = 0;
  39   39  
  40   40          while (rem != 0) {
  41   41                  uintptr_t addr = ustart + pos;
  42   42                  size_t len = MIN(rem, PAGESIZE - (addr & PAGEOFFSET));
  43   43  
  44   44                  if ((error = uread(p, buf + pos, len, addr)) != 0) {
  45   45                          if (error == ENXIO) {
  46   46                                  /*
  47   47                                   * ENXIO from uread() indicates that the page
  48   48                                   * does not exist.  This will simply be a
  49   49                                   * partial read.
  50   50                                   */
  51   51                                  error = 0;
  52   52                          }
  53   53                          break;
  54   54                  }
  55   55  
  56   56                  rem -= len;
  57   57                  pos += len;
  58   58          }
  59   59  
  60   60          if (rdsz != NULL)
  61   61                  *rdsz = pos;
  62   62  
  63   63          return (error);
  64   64  }
  65   65  
  66   66  /*
  67   67   * Attempt to read the argument vector (argv) from this process.  The caller
  68   68   * must hold the p_lock mutex, and have marked the process P_PR_LOCK (e.g. via
  69   69   * prlock or lx_prlock).
  70   70   *
  71   71   * The caller must provide a buffer (buf, buflen).  We will concatenate each
  72   72   * argument string (including the NUL terminator) into this buffer.  The number
  73   73   * of characters written to this buffer (including the final NUL terminator)
  74   74   * will be stored in 'slen'.
  75   75   */
  76   76  int
  77   77  prreadargv(proc_t *p, char *buf, size_t bufsz, size_t *slen)
  78   78  {
  79   79          int error;
  80   80          user_t *up;
  81   81          struct as *as;
  82   82          size_t pos = 0;
  83   83          caddr_t *argv = NULL;
  84   84          size_t argvsz = 0;
  85   85          int i;
  86   86  
  87   87          VERIFY(MUTEX_HELD(&p->p_lock));
  88   88          VERIFY(p->p_proc_flag & P_PR_LOCK);
  89   89  
  90   90          up = PTOU(p);
  91   91          as = p->p_as;
  92   92  
  93   93          if ((p->p_flag & SSYS) || as == &kas || up->u_argv == NULL) {
  94   94                  /*
  95   95                   * Return the regular psargs string to the caller.
  96   96                   */
  97   97                  bcopy(up->u_psargs, buf, MIN(bufsz, sizeof (up->u_psargs)));
  98   98                  buf[bufsz - 1] = '\0';
  99   99                  *slen = strlen(buf) + 1;
 100  100  
 101  101                  return (0);
 102  102          }
 103  103  
 104  104          /*
 105  105           * Allocate space to store argv array.
 106  106           */
 107  107          argvsz = up->u_argc * (p->p_model == DATAMODEL_ILP32 ?
 108  108              sizeof (caddr32_t) : sizeof (caddr_t));
 109  109          argv = kmem_alloc(argvsz, KM_SLEEP);
 110  110  
 111  111          /*
 112  112           * Extract the argv array from the target process.  Drop p_lock
 113  113           * while we do I/O to avoid deadlock with the clock thread.
 114  114           */
 115  115          mutex_exit(&p->p_lock);
 116  116          if ((error = prreadbuf(p, up->u_argv, (uint8_t *)argv, argvsz,
 117  117              NULL)) != 0) {
 118  118                  kmem_free(argv, argvsz);
 119  119                  mutex_enter(&p->p_lock);
 120  120                  VERIFY(p->p_proc_flag & P_PR_LOCK);
 121  121                  return (-1);
 122  122          }
 123  123  
 124  124          /*
 125  125           * Read each argument string from the pointers in the argv array.
 126  126           */
 127  127          pos = 0;
 128  128          for (i = 0; i < up->u_argc; i++) {
 129  129                  size_t rdsz, trysz;
 130  130                  uintptr_t arg;
 131  131                  off_t j;
 132  132                  boolean_t found_nul;
 133  133                  boolean_t do_retry = B_TRUE;
 134  134  
 135  135  #ifdef  _SYSCALL32_IMPL
 136  136                  if (p->p_model == DATAMODEL_ILP32) {
 137  137                          arg = (uintptr_t)((caddr32_t *)argv)[i];
 138  138                  } else {
 139  139                          arg = (uintptr_t)argv[i];
 140  140                  }
 141  141  #else
 142  142                  arg = (uintptr_t)argv[i];
 143  143  #endif
 144  144  
 145  145                  /*
 146  146                   * Stop trying to read arguments if we reach a NULL
 147  147                   * pointer in the vector.
 148  148                   */
 149  149                  if (arg == NULL)
 150  150                          break;
 151  151  
 152  152                  /*
 153  153                   * Stop reading if we have read the maximum length
 154  154                   * we can return to the user.
 155  155                   */
 156  156                  if (pos >= bufsz)
 157  157                          break;
 158  158  
 159  159                  /*
 160  160                   * Initially we try a short read, on the assumption that
 161  161                   * most individual argument strings are less than 80
 162  162                   * characters long.
 163  163                   */
 164  164                  if ((trysz = MIN(80, bufsz - pos - 1)) < 80) {
 165  165                          /*
 166  166                           * We don't have room in the target buffer for even
 167  167                           * an entire short read, so there is no need to retry
 168  168                           * with a longer read.
 169  169                           */
 170  170                          do_retry = B_FALSE;
 171  171                  }
 172  172  
 173  173  retry:
 174  174                  /*
 175  175                   * Read string data for this argument.  Leave room
 176  176                   * in the buffer for a final NUL terminator.
 177  177                   */
 178  178                  if ((error = prreadbuf(p, arg, (uint8_t *)&buf[pos], trysz,
 179  179                      &rdsz)) != 0) {
 180  180                          /*
 181  181                           * There was a problem reading this string
 182  182                           * from the process.  Give up.
 183  183                           */
 184  184                          break;
 185  185                  }
 186  186  
 187  187                  /*
 188  188                   * Find the NUL terminator.
 189  189                   */
 190  190                  found_nul = B_FALSE;
 191  191                  for (j = 0; j < rdsz; j++) {
 192  192                          if (buf[pos + j] == '\0') {
 193  193                                  found_nul = B_TRUE;
 194  194                                  break;
 195  195                          }
 196  196                  }
 197  197  
 198  198                  if (!found_nul && do_retry) {
 199  199                          /*
 200  200                           * We did not find a NUL terminator, but this
 201  201                           * was a first pass short read.  Try once more
 202  202                           * with feeling.
 203  203                           */
 204  204                          trysz = bufsz - pos - 1;
 205  205                          do_retry = B_FALSE;
 206  206                          goto retry;
 207  207                  }
 208  208  
 209  209                  /*
 210  210                   * Commit the string we read to the buffer.
 211  211                   */
 212  212                  pos += j + 1;
 213  213                  if (!found_nul && pos < bufsz) {
 214  214                          /*
 215  215                           * A NUL terminator was not found; add one.
 216  216                           */
 217  217                          buf[pos++] = '\0';
 218  218                  }
 219  219          }
 220  220  
 221  221          /*
 222  222           * Ensure the entire string is NUL-terminated.
 223  223           */
 224  224          buf[bufsz - 1] = '\0';
 225  225  
 226  226          mutex_enter(&p->p_lock);
 227  227          VERIFY(p->p_proc_flag & P_PR_LOCK);
 228  228          kmem_free(argv, argvsz);
 229  229  
 230  230          /*
 231  231           * If the operation was a success, return the copied string length
 232  232           * to the caller.
 233  233           */
 234  234          *slen = (error == 0) ? pos : 0;
 235  235  
 236  236          return (error);
 237  237  }
 238  238  
 239  239  /*
 240  240   * Similar to prreadargv except reads the env vector. This is slightly more
 241  241   * complex because there is no count for the env vector that corresponds to
 242  242   * u_argc.
 243  243   */
 244  244  int
 245  245  prreadenvv(proc_t *p, char *buf, size_t bufsz, size_t *slen)
 246  246  {
 247  247          int error;
 248  248          user_t *up;
 249  249          struct as *as;
 250  250          size_t pos = 0;
 251  251          caddr_t *envp = NULL;
 252  252          uintptr_t tmpp = NULL;
 253  253          size_t envpsz = 0, rdsz = 0;
 254  254          int i;
 255  255          int cnt, bound;
 256  256  
 257  257          VERIFY(MUTEX_HELD(&p->p_lock));
 258  258          VERIFY(p->p_proc_flag & P_PR_LOCK);
 259  259  
 260  260          up = PTOU(p);
 261  261          as = p->p_as;
 262  262  
 263  263          if ((p->p_flag & SSYS) || as == &kas || up->u_envp == NULL) {
 264  264                  /*
 265  265                   * Return empty string.
 266  266                   */
 267  267                  buf[0] = '\0';
 268  268                  *slen = 1;
 269  269  
 270  270                  return (0);
 271  271          }
 272  272  
 273  273          /*
 274  274           * Drop p_lock while we do I/O to avoid deadlock with the clock thread.
 275  275           */
 276  276          mutex_exit(&p->p_lock);
 277  277  
 278  278          /*
 279  279           * We first have to count how many env entries we have. This is
 280  280           * somewhat painful. We extract the env entries from the target process
 281  281           * one entry at a time. Stop trying to read env entries if we reach a
 282  282           * NULL pointer in the vector or hit our upper bound (which we take
 283  283           * as the bufsz/4) to ensure we don't run off.
 284  284           */
 285  285          rdsz = (p->p_model == DATAMODEL_ILP32 ?
 286  286              sizeof (caddr32_t) : sizeof (caddr_t));
 287  287          bound = (int)(bufsz / 4);
 288  288          for (cnt = 0, tmpp = up->u_envp; cnt < bound; cnt++, tmpp += rdsz) {
 289  289                  caddr_t tmp = NULL;
 290  290  
 291  291                  if ((error = prreadbuf(p, tmpp, (uint8_t *)&tmp, rdsz,
 292  292                      NULL)) != 0) {
 293  293                          mutex_enter(&p->p_lock);
 294  294                          VERIFY(p->p_proc_flag & P_PR_LOCK);
 295  295                          return (-1);
 296  296                  }
 297  297  
 298  298                  if (tmp == NULL)
 299  299                          break;
 300  300          }
 301  301          if (cnt == 0) {
 302  302                  /* Return empty string. */
 303  303                  buf[0] = '\0';
 304  304                  *slen = 1;
 305  305                  mutex_enter(&p->p_lock);
 306  306                  VERIFY(p->p_proc_flag & P_PR_LOCK);
 307  307                  return (0);
 308  308          }
 309  309  
 310  310          /*
 311  311           * Allocate space to store env array.
 312  312           */
 313  313          envpsz = cnt * (p->p_model == DATAMODEL_ILP32 ?
 314  314              sizeof (caddr32_t) : sizeof (caddr_t));
 315  315          envp = kmem_alloc(envpsz, KM_SLEEP);
 316  316  
 317  317          /*
 318  318           * Extract the env array from the target process.
 319  319           */
 320  320          if ((error = prreadbuf(p, up->u_envp, (uint8_t *)envp, envpsz,
 321  321              NULL)) != 0) {
 322  322                  kmem_free(envp, envpsz);
 323  323                  mutex_enter(&p->p_lock);
 324  324                  VERIFY(p->p_proc_flag & P_PR_LOCK);
 325  325                  return (-1);
 326  326          }
 327  327  
 328  328          /*
 329  329           * Read each env string from the pointers in the env array.
 330  330           */
 331  331          pos = 0;
 332  332          for (i = 0; i < cnt; i++) {
 333  333                  size_t rdsz, trysz;
 334  334                  uintptr_t ev;
 335  335                  off_t j;
 336  336                  boolean_t found_nul;
 337  337                  boolean_t do_retry = B_TRUE;
 338  338  
 339  339  #ifdef  _SYSCALL32_IMPL
 340  340                  if (p->p_model == DATAMODEL_ILP32) {
 341  341                          ev = (uintptr_t)((caddr32_t *)envp)[i];
 342  342                  } else {
 343  343                          ev = (uintptr_t)envp[i];
 344  344                  }
 345  345  #else
 346  346                  ev = (uintptr_t)envp[i];
 347  347  #endif
 348  348  
 349  349                  /*
 350  350                   * Stop trying to read env entries if we reach a NULL
 351  351                   * pointer in the vector.
 352  352                   */
 353  353                  if (ev == NULL)
 354  354                          break;
 355  355  
 356  356                  /*
 357  357                   * Stop reading if we have read the maximum length
 358  358                   * we can return to the user.
 359  359                   */
 360  360                  if (pos >= bufsz)
 361  361                          break;
 362  362  
 363  363                  /*
 364  364                   * Initially we try a short read, on the assumption that
 365  365                   * most individual env strings are less than 80
 366  366                   * characters long.
 367  367                   */
 368  368                  if ((trysz = MIN(80, bufsz - pos - 1)) < 80) {
 369  369                          /*
 370  370                           * We don't have room in the target buffer for even
 371  371                           * an entire short read, so there is no need to retry
 372  372                           * with a longer read.
 373  373                           */
 374  374                          do_retry = B_FALSE;
 375  375                  }
 376  376  
 377  377  retry:
 378  378                  /*
 379  379                   * Read string data for this env var.  Leave room
 380  380                   * in the buffer for a final NUL terminator.
 381  381                   */
 382  382                  if ((error = prreadbuf(p, ev, (uint8_t *)&buf[pos], trysz,
 383  383                      &rdsz)) != 0) {
 384  384                          /*
 385  385                           * There was a problem reading this string
 386  386                           * from the process.  Give up.
 387  387                           */
 388  388                          break;
 389  389                  }
 390  390  
 391  391                  /*
 392  392                   * Find the NUL terminator.
 393  393                   */
 394  394                  found_nul = B_FALSE;
 395  395                  for (j = 0; j < rdsz; j++) {
 396  396                          if (buf[pos + j] == '\0') {
 397  397                                  found_nul = B_TRUE;
 398  398                                  break;
 399  399                          }
 400  400                  }
 401  401  
 402  402                  if (!found_nul && do_retry) {
 403  403                          /*
 404  404                           * We did not find a NUL terminator, but this
 405  405                           * was a first pass short read.  Try once more
 406  406                           * with feeling.
 407  407                           */
 408  408                          trysz = bufsz - pos - 1;
 409  409                          do_retry = B_FALSE;
 410  410                          goto retry;
 411  411                  }
 412  412  
 413  413                  /*
 414  414                   * Commit the string we read to the buffer.
 415  415                   */
 416  416                  pos += j + 1;
 417  417                  if (!found_nul && pos < bufsz) {
 418  418                          /*
 419  419                           * A NUL terminator was not found; add one.
 420  420                           */
 421  421                          buf[pos++] = '\0';
 422  422                  }
 423  423          }
 424  424  
 425  425          /*
 426  426           * Ensure the entire string is NUL-terminated.
 427  427           */
 428  428          buf[bufsz - 1] = '\0';
 429  429  
 430  430          mutex_enter(&p->p_lock);
 431  431          VERIFY(p->p_proc_flag & P_PR_LOCK);
 432  432          kmem_free(envp, envpsz);
 433  433  
 434  434          /*
 435  435           * If the operation was a success, return the copied string length
 436  436           * to the caller.
 437  437           */
 438  438          *slen = (error == 0) ? pos : 0;
 439  439  
 440  440          return (error);
 441  441  }
  
    | 
      ↓ open down ↓ | 
    441 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX