Print this page
Thread safety fixes.

Split Close
Expand all
Collapse all
          --- old/usr/src/lib/libc/port/locale/localeimpl.c
          +++ new/usr/src/lib/libc/port/locale/localeimpl.c
↓ open down ↓ 61 lines elided ↑ open up ↑
  62   62   * thread-specific locale object if one is present, or the global locale
  63   63   * object otherwise.  Note that once the TSD data is set, the only way
  64   64   * to revert to the global locale is to pass the global locale LC_GLOBAL_LOCALE
  65   65   * to uselocale().
  66   66   *
  67   67   * We are careful to minimize performance impact of multiple calls to
  68   68   * uselocale() or setlocale() by using a cache of locale data whenever possible.
  69   69   * As a consequence of this, applications that iterate over all possible
  70   70   * locales will burn through a lot of virtual memory, but we find such
  71   71   * applications rare.  (locale -a might be an exception, but it is short lived.)
       72 + *
       73 + * Category data is never released (although enclosing locale objects might be),
       74 + * in order to guarantee thread-safety.  Calling freelocale() on an object
       75 + * while it is in use by another thread is a programmer error (use-after-free)
       76 + * and we don't bother to note it further.
       77 + *
       78 + * Locale objects (global locales) established by setlocale() are also
       79 + * never freed (for MT safety), but we will save previous locale objects
       80 + * and reuse them when we can.
  72   81   */
  73   82  
  74      -/*
  75      - * NB: Each of the structures listed herein should have the refcnt
  76      - * set to -1, to ensure that posix locale information is never ever freed,
  77      - * even when changing the global locale.
  78      - */
  79      -
  80   83  typedef struct locdata *(*loadfn_t)(const char *);
  81   84  
  82   85  static const loadfn_t loaders[LC_ALL] = {
  83   86          __lc_ctype_load,
  84   87          __lc_numeric_load,
  85   88          __lc_time_load,
  86   89          __lc_collate_load,
  87   90          __lc_monetary_load,
  88   91          __lc_messages_load,
  89   92  };
↓ open down ↓ 46 lines elided ↑ open up ↑
 136  139          "LC_MESSAGES",
 137  140          "LC_ALL",
 138  141  };
 139  142  
 140  143  /*
 141  144   * Prototypes.
 142  145   */
 143  146  static const char *get_locale_env(int);
 144  147  static struct locdata *locdata_get(int, const const char *);
 145  148  static struct locdata *locdata_get_cache(int, const char *);
 146      -static void locdata_set_cache(int, struct locdata *);
 147  149  
 148  150  /*
 149  151   * Some utility routines.
 150  152   */
 151      -struct locdata *
 152      -__locdata_hold(struct locdata *ld)
 153      -{
 154      -        if (ld != NULL && ld->l_refcnt != (uint32_t)-1)
 155      -                atomic_inc_32(&ld->l_refcnt);
 156      -        return (ld);
 157      -}
 158  153  
 159      -void
 160      -__locdata_release(struct locdata *ld)
 161      -{
 162      -        if (ld->l_refcnt == (uint32_t)-1)
 163      -                return;
 164      -
 165      -        if (atomic_dec_32_nv(&ld->l_refcnt) == 0) {
 166      -                for (int i = 0; i < NLOCDATA; i++)
 167      -                        free(ld->l_data[i]);
 168      -                if (ld->l_map && ld->l_map_len) {
 169      -                        (void) munmap(ld->l_map, ld->l_map_len);
 170      -                }
 171      -                free(ld);
 172      -        }
 173      -}
 174      -
 175  154  struct locdata *
 176  155  __locdata_alloc(const char *name, size_t memsz)
 177  156  {
 178  157          struct locdata *ldata;
 179  158  
 180  159          if ((ldata = calloc(1, sizeof (*ldata))) == NULL) {
 181  160                  return (NULL);
 182  161          }
 183  162          if ((ldata->l_data[0] = calloc(1, memsz)) == NULL) {
 184  163                  free(ldata);
 185  164                  errno = ENOMEM;
 186  165                  return (NULL);
 187  166          }
 188  167          (void) strlcpy(ldata->l_lname, name, sizeof (ldata->l_lname));
 189      -        ldata->l_refcnt = 1;
 190  168  
 191  169          return (ldata);
 192  170  }
 193  171  
 194  172  /*
      173 + * Normally we never free locale data truly, but if we failed to load it
      174 + * for some reason, this routine is used to cleanup the partial mess.
      175 + */
      176 +void
      177 +__locdata_free(struct locdata *ldata)
      178 +{
      179 +        for (int i = 0; i < NLOCDATA; i++)
      180 +                free(ldata->l_data[i]);
      181 +        if (ldata->l_map != NULL && ldata->l_map_len)
      182 +                (void) munmap(ldata->l_map, ldata->l_map_len);
      183 +        free(ldata);
      184 +}
      185 +
      186 +/*
 195  187   * It turns out that for performance reasons we would really like to
 196  188   * cache the most recently referenced locale data to avoid wasteful
 197  189   * loading from files.
 198  190   */
 199  191  
 200  192  static struct locdata *cache_data[LC_ALL];
      193 +static struct locdata *cat_data[LC_ALL];
 201  194  static mutex_t cache_lock = DEFAULTMUTEX;
 202  195  
 203  196  /*
 204  197   * Returns the cached data if the locale name is the same.  If not,
 205  198   * returns NULL (cache miss).  The locdata is returned with a hold on
 206  199   * it, taken on behalf of the caller.  The caller should drop the hold
 207  200   * when it is finished.
 208  201   */
 209  202  static struct locdata *
 210  203  locdata_get_cache(int category, const char *locname)
 211  204  {
 212  205          struct locdata *loc;
 213  206  
 214  207          if (category < 0 || category >= LC_ALL)
 215  208                  return (NULL);
 216  209  
      210 +        /* Try cache first. */
 217  211          lmutex_lock(&cache_lock);
 218      -        if ((loc = cache_data[category]) != NULL) {
 219      -                if (strcmp(locname, loc->l_lname) == 0) {
 220      -                        loc = __locdata_hold(loc);
 221      -                } else {
 222      -                        loc = NULL;
 223      -                }
      212 +        loc = cache_data[category];
      213 +
      214 +        if ((loc != NULL) && (strcmp(loc->l_lname, locname) == 0)) {
      215 +                lmutex_unlock(&cache_lock);
      216 +                return (loc);
 224  217          }
 225      -        lmutex_unlock(&cache_lock);
 226      -        return (loc);
 227      -}
 228  218  
 229      -/*
 230      - * Set the cache for the category to specific content.  An additional hold
 231      - * is taken for the data while it is in the cache, so the caller may drop
 232      - * its own hold once this is complete.  Also, releases the hold on any
 233      - * previously cached data.
 234      - */
 235      -static void
 236      -locdata_set_cache(int category, struct locdata *loc)
 237      -{
 238      -        struct locdata *old;
      219 +        /*
      220 +         * Failing that try previously loaded locales (linear search) --
      221 +         * this could be optimized to a hash, but its unlikely that a single
      222 +         * application will ever need to work with more than a few locales.
      223 +         */
      224 +                for (loc = cat_data[category]; loc != NULL; loc = loc->l_next) {
      225 +                        if (strcmp(locname, loc->l_lname) == 0) {
      226 +                                break;
      227 +                        }
      228 +                }
 239  229  
 240      -        if (category < 0 || category >= LC_ALL)
 241      -                return;
      230 +                /*
      231 +                 * Finally, if we still don't have one, try loading the locale
      232 +                 * data from the actual on-disk data.
      233 +                 *
      234 +                 * We drop the lock (libc wants to ensure no internal locks
      235 +                 * are held when we call other routines required to read from
      236 +                 * files, allocate memory, etc.)  There is a small race here,
      237 +                 * but the consequences of the race are benign -- if multiple
      238 +                 * threads hit this at precisely the same point, we could
      239 +                 * wind up with duplicates of the locale data in the cache.
      240 +                 *
      241 +                 * This wastes the memory for an extra copy of the locale
      242 +                 * data, but there is no further harm beyond that.  Its not
      243 +                 * worth the effort to recode this to something "safe"
      244 +                 * (which would require rescanning the list, etc.), given
      245 +                 * that this race will probably never actually occur.
      246 +                 */
      247 +                if (loc == NULL) {
      248 +                        lmutex_unlock(&cache_lock);
      249 +                        loc = (*loaders[category])(locname);
      250 +                        lmutex_lock(&cache_lock);
      251 +                        (void) strlcpy(loc->l_lname, locname,
      252 +                            sizeof (loc->l_lname));
      253 +                }
 242  254  
 243      -        lmutex_lock(&cache_lock);
 244      -        old = cache_data[category];
 245      -        cache_data[category] = __locdata_hold(loc);
 246      -        lmutex_unlock(&cache_lock);
      255 +        /*
      256 +         * Assuming we got one, update the cache, and stick us on the list
      257 +         * of loaded locale data.  We insert into the head (more recent
      258 +         * use is likely to win.)
      259 +         */
      260 +        if (loc != NULL) {
      261 +                cache_data[category] = loc;
      262 +                if (loc->l_next == NULL) {
      263 +                        loc->l_next = cat_data[category];
      264 +                        cat_data[category] = loc;
      265 +                }
      266 +        }
 247  267  
 248      -        /* drop our reference on the old data */
 249      -        if (old)
 250      -                __locdata_release(old);
      268 +        lmutex_unlock(&cache_lock);
      269 +        return (loc);
 251  270  }
 252  271  
 253  272  /*
 254  273   * Routine to get the locdata for a given category and locale.
 255  274   * This includes retrieving it from cache, retrieving it from
 256  275   * a file, etc.
 257  276   */
 258  277  static struct locdata *
 259  278  locdata_get(int category, const char *locname)
 260  279  {
 261      -        struct locdata *ldata;
 262  280          char scratch[ENCODING_LEN + 1];
 263  281          char *slash;
 264  282          int cnt;
 265  283          int len;
 266  284  
 267  285          if (locname == NULL || *locname == 0) {
 268  286                  locname = get_locale_env(category);
 269  287          }
 270  288  
 271  289          /*
↓ open down ↓ 11 lines elided ↑ open up ↑
 283  301                                  len = sizeof (scratch);
 284  302                          }
 285  303                  } else {
 286  304                          len = sizeof (scratch);
 287  305                  }
 288  306                  (void) strlcpy(scratch, locname, len);
 289  307                  locname = scratch;
 290  308          }
 291  309  
 292  310          if ((strcmp(locname, "C") == 0) || (strcmp(locname, "POSIX") == 0))
 293      -                return (__locdata_hold(posix_locale.locdata[category]));
      311 +                return (posix_locale.locdata[category]);
 294  312  
 295      -        ldata = locdata_get_cache(category, locname);
 296      -        if (ldata != NULL)
 297      -                return (ldata);
 298      -
 299      -        /* Otherwise load it */
 300      -        ldata = (*loaders[category])(locname);
 301      -        if (ldata != NULL) {
 302      -                locdata_set_cache(category, ldata);
 303      -        }
 304      -        return (ldata);
      313 +        return (locdata_get_cache(category, locname));
 305  314  }
 306  315  
 307  316  /* tsd destructor */
 308  317  static void
 309  318  freelocptr(void *arg)
 310  319  {
 311  320          locale_t *locptr = arg;
 312  321          if (*locptr != NULL)
 313  322                  freelocale(*locptr);
 314  323  }
↓ open down ↓ 46 lines elided ↑ open up ↑
 361  370  locale_t
 362  371  duplocale(locale_t src)
 363  372  {
 364  373          locale_t        loc;
 365  374          int             i;
 366  375  
 367  376          loc = calloc(1, sizeof (*loc));
 368  377          if (loc == NULL) {
 369  378                  return (NULL);
 370  379          }
      380 +        if (src == NULL) {
      381 +                /* illumos extension: POSIX says LC_GLOBAL_LOCALE here */
      382 +                src = ___global_locale;
      383 +        }
 371  384          for (i = 0; i < LC_ALL; i++) {
 372      -                loc->locdata[i] = __locdata_hold(src->locdata[i]);
      385 +                loc->locdata[i] = src->locdata[i];
 373  386                  loc->loaded[i] = 0;
 374  387          }
 375  388          loc->collate = loc->locdata[LC_COLLATE]->l_data[0];
 376  389          loc->ctype = loc->locdata[LC_CTYPE]->l_data[0];
 377  390          loc->runelocale = loc->locdata[LC_CTYPE]->l_data[1];
 378  391          loc->messages = loc->locdata[LC_MESSAGES]->l_data[0];
 379  392          loc->monetary = loc->locdata[LC_MONETARY]->l_data[0];
 380  393          loc->numeric = loc->locdata[LC_NUMERIC]->l_data[0];
 381  394          loc->time = loc->locdata[LC_TIME]->l_data[0];
 382  395          return (loc);
 383  396  }
 384  397  
 385  398  void
 386  399  freelocale(locale_t loc)
 387  400  {
 388      -        int i;
 389      -        for (i = 0; i < LC_ALL; i++)
 390      -                __locdata_release(loc->locdata[i]);
 391      -        if (loc != &posix_locale)
      401 +        /*
      402 +         * We take extra care never to free a saved locale created by
      403 +         * setlocale().  This shouldn't be strictly necessary, but a little
      404 +         * extra safety doesn't hurt here.
      405 +         */
      406 +        if ((loc != &posix_locale) && (loc->next == NULL))
 392  407                  free(loc);
 393  408  }
 394  409  
 395  410  locale_t
 396  411  newlocale(int catmask, const char *locname, locale_t base)
 397  412  {
 398  413          locale_t loc;
 399  414          int i, e;
 400  415  
 401  416          if (catmask & ~(LC_ALL_MASK)) {
 402  417                  errno = EINVAL;
 403  418                  return (NULL);
 404  419          }
 405      -        loc = duplocale(base != NULL ? base : ___global_locale);
      420 +        /*
      421 +         * Technically passing LC_GLOBAL_LOCALE here is illegal,
      422 +         * but we allow it.
      423 +         */
      424 +        if (base == NULL || base == ___global_locale) {
      425 +                loc = duplocale(___global_locale);
      426 +        } else {
      427 +                loc = base;
      428 +        }
 406  429          if (loc == NULL) {
 407  430                  return (NULL);
 408  431          }
 409  432  
 410  433          for (i = 0; i < LC_ALL; i++) {
 411  434                  struct locdata *ldata;
 412  435                  loc->loaded[i] = 0;
 413  436                  if (((1 << i) & catmask) == 0) {
 414  437                          /* Default to base locale if not overriding */
 415  438                          continue;
 416  439                  }
 417  440                  ldata = locdata_get(i, locname);
 418  441                  if (ldata == NULL) {
 419  442                          e = errno;
 420  443                          freelocale(loc);
 421  444                          errno = e;
 422  445                          return (NULL);
 423  446                  }
 424      -                __locdata_release(loc->locdata[i]);
 425  447                  loc->locdata[i] = ldata;
 426  448          }
 427      -        if (base && base != ___global_locale) {
 428      -                freelocale(base);
 429      -        }
 430  449          loc->collate = loc->locdata[LC_COLLATE]->l_data[0];
 431  450          loc->ctype = loc->locdata[LC_CTYPE]->l_data[0];
 432  451          loc->runelocale = loc->locdata[LC_CTYPE]->l_data[1];
 433  452          loc->messages = loc->locdata[LC_MESSAGES]->l_data[0];
 434  453          loc->monetary = loc->locdata[LC_MONETARY]->l_data[0];
 435  454          loc->numeric = loc->locdata[LC_NUMERIC]->l_data[0];
 436  455          loc->time = loc->locdata[LC_TIME]->l_data[0];
 437  456          return (loc);
 438  457  }
 439  458  
↓ open down ↓ 37 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX