Print this page
    
Plug sharefs zone-shutdown leaks
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/fs/sharefs/sharetab.c
          +++ new/usr/src/uts/common/fs/sharefs/sharetab.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.
  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]
  
    | 
      ↓ open down ↓ | 
    17 lines elided | 
    
      ↑ open up ↑ | 
  
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24   24   */
  25   25  
  26   26  /*
  27   27   * Copyright 2018 Nexenta Systems, Inc.
       28 + * Copyright 2019 Joyent, Inc.
  28   29   */
  29   30  
  30   31  #include <sys/types.h>
  31   32  #include <sys/types32.h>
  32   33  #include <sys/param.h>
  33   34  #include <sys/systm.h>
  34   35  #include <rpc/types.h>
  35   36  #include <sys/vfs.h>
  36   37  #include <sys/siginfo.h>
  37   38  #include <sys/proc.h>           /* for exit() declaration */
  38   39  #include <sys/kmem.h>
  39   40  #include <sys/pathname.h>
  40   41  #include <sys/debug.h>
  41   42  #include <sys/vtrace.h>
  42   43  #include <sys/cmn_err.h>
  43   44  #include <sys/atomic.h>
  44   45  #include <sys/policy.h>
  45   46  
  46   47  #include <sharefs/sharefs.h>
  47   48  
  48   49  /*
  49   50   * A macro to avoid cut-and-paste errors on getting a string field
  50   51   * from user-land.
  51   52   */
  52   53  #define SHARETAB_COPYIN(field)                                          \
  53   54          if (copyinstr(STRUCT_FGETP(u_sh, sh_##field),                   \
  54   55              buf,                                                        \
  55   56              bufsz + 1,  /* Add one for extra NUL */                     \
  56   57              &len)) {                                                    \
  57   58                  error = EFAULT;                                         \
  58   59                  goto cleanup;                                           \
  59   60          }                                                               \
  60   61          /* Need to remove 1 because copyinstr() counts the NUL */       \
  61   62          len--;                                                          \
  62   63          sh->sh_##field = kmem_alloc(len + 1, KM_SLEEP);                 \
  63   64          bcopy(buf, sh->sh_##field, len);                                \
  64   65          sh->sh_##field[len] = '\0';                                     \
  65   66          shl.shl_##field = (int)len;                                     \
  66   67          sh->sh_size += shl.shl_##field; /* Debug counting */
  67   68  
  68   69  #define SHARETAB_DELETE_FIELD(field)                                    \
  69   70          if (sh->sh_##field != NULL) {                                   \
  70   71                  kmem_free(sh->sh_##field,                               \
  71   72                      shl ? shl->shl_##field + 1 :                        \
  72   73                      strlen(sh->sh_##field) + 1);                        \
  73   74          }
  74   75  
  75   76  static zone_key_t sharetab_zone_key;
  76   77  
  77   78  /*
  78   79   * Take care of cleaning up a share.
  79   80   * If passed in a length array, use it to determine how much
  80   81   * space to clean up. Else, figure that out.
  81   82   */
  82   83  static void
  83   84  sharefree(share_t *sh, sharefs_lens_t *shl)
  84   85  {
  85   86          if (sh == NULL)
  86   87                  return;
  87   88  
  88   89          SHARETAB_DELETE_FIELD(path);
  89   90          SHARETAB_DELETE_FIELD(res);
  90   91          SHARETAB_DELETE_FIELD(fstype);
  91   92          SHARETAB_DELETE_FIELD(opts);
  92   93          SHARETAB_DELETE_FIELD(descr);
  93   94  
  94   95          kmem_free(sh, sizeof (*sh));
  95   96  }
  96   97  
  97   98  /*
  98   99   * If there is no error, then this function is responsible for
  99  100   * cleaning up the memory associated with the share argument.
 100  101   */
 101  102  static int
 102  103  sharefs_remove(sharetab_globals_t *sg, share_t *sh, sharefs_lens_t *shl)
 103  104  {
 104  105          int             iHash;
 105  106          sharetab_t      *sht;
 106  107          share_t         *s, *p;
 107  108          int             iPath;
 108  109  
 109  110          if (!sh)
 110  111                  return (ENOENT);
 111  112  
 112  113          rw_enter(&sg->sharetab_lock, RW_WRITER);
 113  114          for (sht = sg->sharefs_sharetab; sht != NULL; sht = sht->s_next) {
 114  115                  if (strcmp(sh->sh_fstype, sht->s_fstype) == 0)
 115  116                          break;
 116  117          }
 117  118  
 118  119          /*
 119  120           * There does not exist a fstype in memory which
 120  121           * matches the share passed in.
 121  122           */
 122  123          if (sht == NULL) {
 123  124                  rw_exit(&sg->sharetab_lock);
 124  125                  return (ENOENT);
 125  126          }
 126  127  
 127  128          iPath = shl != NULL ? shl->shl_path : strlen(sh->sh_path);
 128  129          iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
 129  130  
 130  131          /*
 131  132           * Now walk down the hash table and find the entry to free!
 132  133           */
 133  134          for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
 134  135              s != NULL; s = s->sh_next) {
 135  136                  /*
 136  137                   * We need exact matches.
 137  138                   */
 138  139                  if (strcmp(sh->sh_path, s->sh_path) == 0 &&
 139  140                      strlen(s->sh_path) == iPath) {
 140  141                          if (p != NULL)
 141  142                                  p->sh_next = s->sh_next;
 142  143                          else
 143  144                                  sht->s_buckets[iHash].ssh_sh = s->sh_next;
 144  145  
 145  146                          ASSERT(sht->s_buckets[iHash].ssh_count != 0);
 146  147                          atomic_dec_32(&sht->s_buckets[iHash].ssh_count);
 147  148                          atomic_dec_32(&sht->s_count);
 148  149                          atomic_dec_32(&sg->sharetab_count);
 149  150  
 150  151                          ASSERT(sg->sharetab_size >= s->sh_size);
 151  152                          sg->sharetab_size -= s->sh_size;
 152  153  
 153  154                          gethrestime(&sg->sharetab_mtime);
 154  155                          atomic_inc_32(&sg->sharetab_generation);
 155  156  
 156  157                          break;
 157  158                  }
 158  159  
 159  160                  p = s;
 160  161          }
 161  162  
 162  163          rw_exit(&sg->sharetab_lock);
 163  164  
 164  165          if (s == NULL)
 165  166                  return (ENOENT);
 166  167  
 167  168          s->sh_next = NULL;
 168  169          sharefree(s, NULL);
 169  170  
 170  171          /* We need to free the share for the caller */
 171  172          sharefree(sh, shl);
 172  173  
 173  174          return (0);
 174  175  }
 175  176  
 176  177  /*
 177  178   * The caller must have allocated memory for us to use.
 178  179   */
 179  180  static int
 180  181  sharefs_add(sharetab_globals_t *sg, share_t *sh, sharefs_lens_t *shl)
 181  182  {
 182  183          int             iHash;
 183  184          sharetab_t      *sht;
 184  185          share_t         *s, *p;
 185  186          int             iPath;
 186  187          int             n;
 187  188  
 188  189          if (sh == NULL)
 189  190                  return (ENOENT);
 190  191  
 191  192          /* We need to find the hash buckets for the fstype */
 192  193          rw_enter(&sg->sharetab_lock, RW_WRITER);
 193  194          for (sht = sg->sharefs_sharetab; sht != NULL; sht = sht->s_next) {
 194  195                  if (strcmp(sh->sh_fstype, sht->s_fstype) == 0)
 195  196                          break;
 196  197          }
 197  198  
 198  199          /* Did not exist, so allocate one and add it to the sharetab */
 199  200          if (sht == NULL) {
 200  201                  sht = kmem_zalloc(sizeof (*sht), KM_SLEEP);
 201  202                  n = strlen(sh->sh_fstype);
 202  203                  sht->s_fstype = kmem_zalloc(n + 1, KM_SLEEP);
 203  204                  (void) strncpy(sht->s_fstype, sh->sh_fstype, n);
 204  205  
 205  206                  sht->s_next = sg->sharefs_sharetab;
 206  207                  sg->sharefs_sharetab = sht;
 207  208          }
 208  209  
 209  210          /* Now we need to find where we have to add the entry */
 210  211          iPath = shl != NULL ? shl->shl_path : strlen(sh->sh_path);
 211  212          iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path));
 212  213  
 213  214          if (shl) {
 214  215                  sh->sh_size = shl->shl_path + shl->shl_res +
 215  216                      shl->shl_fstype + shl->shl_opts + shl->shl_descr;
 216  217          } else {
 217  218                  sh->sh_size = strlen(sh->sh_path) +
 218  219                      strlen(sh->sh_res) + strlen(sh->sh_fstype) +
 219  220                      strlen(sh->sh_opts) + strlen(sh->sh_descr);
 220  221          }
 221  222  
 222  223          /* We need to account for field seperators and the EOL */
 223  224          sh->sh_size += 5;
 224  225  
 225  226          /* Now walk down the hash table and add the new entry */
 226  227          for (p = NULL, s = sht->s_buckets[iHash].ssh_sh;
 227  228              s != NULL; s = s->sh_next) {
 228  229                  /*
 229  230                   * We need exact matches.
 230  231                   *
 231  232                   * We found a matching path. Either we have a
 232  233                   * duplicate path in a share command or we are
 233  234                   * being asked to replace an existing entry.
 234  235                   */
 235  236                  if (strcmp(sh->sh_path, s->sh_path) == 0 &&
 236  237                      strlen(s->sh_path) == iPath) {
 237  238                          if (p != NULL)
 238  239                                  p->sh_next = sh;
 239  240                          else
 240  241                                  sht->s_buckets[iHash].ssh_sh = sh;
 241  242  
 242  243                          sh->sh_next = s->sh_next;
 243  244  
 244  245                          ASSERT(sg->sharetab_size >= s->sh_size);
 245  246                          sg->sharetab_size -= s->sh_size;
 246  247                          sg->sharetab_size += sh->sh_size;
 247  248  
 248  249                          /* Get rid of the old node */
 249  250                          sharefree(s, NULL);
 250  251  
 251  252                          gethrestime(&sg->sharetab_mtime);
 252  253                          atomic_inc_32(&sg->sharetab_generation);
 253  254  
 254  255                          ASSERT(sht->s_buckets[iHash].ssh_count != 0);
 255  256                          rw_exit(&sg->sharetab_lock);
 256  257  
 257  258                          return (0);
 258  259                  }
 259  260  
 260  261                  p = s;
 261  262          }
 262  263  
 263  264          /*
 264  265           * Okay, we have gone through the entire hash chain and not
 265  266           * found a match. We just need to add this node.
 266  267           */
 267  268          sh->sh_next = sht->s_buckets[iHash].ssh_sh;
 268  269          sht->s_buckets[iHash].ssh_sh = sh;
 269  270          atomic_inc_32(&sht->s_buckets[iHash].ssh_count);
 270  271          atomic_inc_32(&sht->s_count);
 271  272          atomic_inc_32(&sg->sharetab_count);
 272  273          sg->sharetab_size += sh->sh_size;
 273  274  
 274  275          gethrestime(&sg->sharetab_mtime);
 275  276          atomic_inc_32(&sg->sharetab_generation);
 276  277  
 277  278          rw_exit(&sg->sharetab_lock);
 278  279  
 279  280          return (0);
 280  281  }
 281  282  
 282  283  /* ARGSUSED */
 283  284  static void *
 284  285  sharetab_zone_init(zoneid_t zoneid)
 285  286  {
 286  287          sharetab_globals_t *sg;
 287  288  
 288  289          sg = kmem_zalloc(sizeof (*sg), KM_SLEEP);
 289  290  
 290  291          rw_init(&sg->sharetab_lock, NULL, RW_DEFAULT, NULL);
 291  292          rw_init(&sg->sharefs_lock, NULL, RW_DEFAULT, NULL);
 292  293  
 293  294          sg->sharetab_size = 0;
 294  295          sg->sharetab_count = 0;
 295  296          sg->sharetab_generation = 1;
 296  297  
 297  298          gethrestime(&sg->sharetab_mtime);
 298  299          gethrestime(&sg->sharetab_snap_time);
 299  300  
 300  301          return (sg);
 301  302  }
  
    | 
      ↓ open down ↓ | 
    264 lines elided | 
    
      ↑ open up ↑ | 
  
 302  303  
 303  304  /* ARGSUSED */
 304  305  static void
 305  306  sharetab_zone_fini(zoneid_t zoneid, void *data)
 306  307  {
 307  308          sharetab_globals_t *sg = data;
 308  309  
 309  310          rw_destroy(&sg->sharefs_lock);
 310  311          rw_destroy(&sg->sharetab_lock);
 311  312  
      313 +        /* ALL of the allocated things must be cleaned before we free sg. */
      314 +        while (sg->sharefs_sharetab != NULL) {
      315 +                int i;
      316 +                sharetab_t *freeing = sg->sharefs_sharetab;
      317 +
      318 +                sg->sharefs_sharetab = freeing->s_next;
      319 +                kmem_free(freeing->s_fstype, strlen(freeing->s_fstype) + 1);
      320 +                for (i = 0; i < PKP_HASH_SIZE; i++) {
      321 +                        sharefs_hash_head_t *bucket;
      322 +
      323 +                        bucket = &(freeing->s_buckets[i]);
      324 +                        while (bucket->ssh_sh != NULL) {
      325 +                                share_t *share = bucket->ssh_sh;
      326 +
      327 +                                bucket->ssh_sh = share->sh_next;
      328 +                                sharefree(share, NULL);
      329 +                        }
      330 +                }
      331 +                kmem_free(freeing, sizeof (*freeing));
      332 +        }
      333 +
 312  334          kmem_free(sg, sizeof (*sg));
 313  335  }
 314  336  
 315  337  void
 316  338  sharefs_sharetab_init(void)
 317  339  {
 318  340          zone_key_create(&sharetab_zone_key, sharetab_zone_init,
 319  341              NULL, sharetab_zone_fini);
 320  342  }
 321  343  
 322  344  sharetab_globals_t *
 323  345  sharetab_get_globals(zone_t *zone)
 324  346  {
 325  347          return (zone_getspecific(sharetab_zone_key, zone));
 326  348  }
 327  349  
 328  350  int
 329  351  sharefs_impl(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
 330  352  {
 331  353          int             error = 0;
 332  354          size_t          len;
 333  355          size_t          bufsz;
 334  356          share_t         *sh;
 335  357          sharefs_lens_t  shl;
 336  358          model_t         model;
 337  359          char            *buf = NULL;
 338  360          sharetab_globals_t *sg = sharetab_get_globals(curzone);
 339  361  
 340  362          STRUCT_DECL(share, u_sh);
 341  363  
 342  364          bufsz = iMaxLen;
 343  365  
 344  366          /*
 345  367           * Before we do anything, lets make sure we have
 346  368           * a sharetab in memory if we need one.
 347  369           */
 348  370          rw_enter(&sg->sharetab_lock, RW_READER);
 349  371          switch (opcode) {
 350  372          case SHAREFS_REMOVE:
 351  373          case SHAREFS_REPLACE:
 352  374                  if (!sg->sharefs_sharetab) {
 353  375                          rw_exit(&sg->sharetab_lock);
 354  376                          return (set_errno(ENOENT));
 355  377                  }
 356  378                  break;
 357  379          case SHAREFS_ADD:
 358  380          default:
 359  381                  break;
 360  382          }
 361  383          rw_exit(&sg->sharetab_lock);
 362  384  
 363  385          model = get_udatamodel();
 364  386  
 365  387          /*
 366  388           * Initialize the data pointers.
 367  389           */
 368  390          STRUCT_INIT(u_sh, model);
 369  391          if (copyin(sh_in, STRUCT_BUF(u_sh), STRUCT_SIZE(u_sh)))
 370  392                  return (set_errno(EFAULT));
 371  393  
 372  394          /* Get the share */
 373  395          sh = kmem_zalloc(sizeof (share_t), KM_SLEEP);
 374  396  
 375  397          /* Get some storage for copying in the strings */
 376  398          buf = kmem_zalloc(bufsz + 1, KM_SLEEP);
 377  399          bzero(&shl, sizeof (sharefs_lens_t));
 378  400  
 379  401          /* Only grab these two until we know what we want */
 380  402          SHARETAB_COPYIN(path);
 381  403          SHARETAB_COPYIN(fstype);
 382  404  
 383  405          switch (opcode) {
 384  406          case SHAREFS_ADD:
 385  407          case SHAREFS_REPLACE:
 386  408                  SHARETAB_COPYIN(res);
 387  409                  SHARETAB_COPYIN(opts);
 388  410                  SHARETAB_COPYIN(descr);
 389  411                  error = sharefs_add(sg, sh, &shl);
 390  412                  break;
 391  413          case SHAREFS_REMOVE:
 392  414                  error = sharefs_remove(sg, sh, &shl);
 393  415                  break;
 394  416          default:
 395  417                  error = EINVAL;
 396  418                  break;
 397  419          }
 398  420  
 399  421  cleanup:
 400  422          /*
 401  423           * If there is no error, then we have stashed the structure
 402  424           * away in the sharetab hash table or have deleted it.
 403  425           *
 404  426           * Either way, the only reason to blow away the data is if
 405  427           * there was an error.
 406  428           */
 407  429          if (error != 0)
 408  430                  sharefree(sh, &shl);
 409  431  
 410  432          if (buf != NULL)
 411  433                  kmem_free(buf, bufsz + 1);
 412  434  
 413  435          return (error != 0 ? set_errno(error) : 0);
 414  436  }
 415  437  
 416  438  int
 417  439  sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen)
 418  440  {
 419  441          /*
 420  442           * If we're in the global zone PRIV_SYS_CONFIG gives us the
 421  443           * priviledges needed to act on sharetab. However if we're in
 422  444           * a non-global zone PRIV_SYS_CONFIG is not allowed. To work
 423  445           * around this issue PRIV_SYS_NFS is used in this case.
 424  446           *
 425  447           * TODO: This basically overloads the definition/use of
 426  448           * PRIV_SYS_NFS to work around the limitation of PRIV_SYS_CONFIG
 427  449           * in a zone. Solaris 11 solved this by implementing a PRIV_SYS_SHARE
 428  450           * we should do the same and replace the use of PRIV_SYS_NFS here and
 429  451           * in zfs_secpolicy_share.
 430  452           */
 431  453          if (INGLOBALZONE(curproc)) {
 432  454                  if (secpolicy_sys_config(CRED(), B_FALSE) != 0)
 433  455                          return (set_errno(EPERM));
 434  456          } else {
 435  457                  /* behave like zfs_secpolicy_share() */
 436  458                  if (secpolicy_nfs(CRED()) != 0)
 437  459                          return (set_errno(EPERM));
 438  460  
 439  461          }
 440  462          return (sharefs_impl(opcode, sh_in, iMaxLen));
 441  463  }
  
    | 
      ↓ open down ↓ | 
    120 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX