1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2016 Joyent, Inc. 25 */ 26 27 /* Copyright (c) 1988 AT&T */ 28 /* All Rights Reserved */ 29 30 #pragma weak _putenv = putenv 31 32 #include "lint.h" 33 #include <mtlib.h> 34 #include <sys/types.h> 35 #include <thread.h> 36 #include <synch.h> 37 #include <stdlib.h> 38 #include <errno.h> 39 #include <string.h> 40 #include <atomic.h> 41 42 #define MIN_ENV_SIZE 128 43 44 extern const char **_environ; 45 extern void clean_env(); 46 47 /* 48 * For performance and consistency reasons we expand the _environ list using 49 * the trusted "power of two, drop it on the floor" method. This allows for 50 * a lockless, single pass implementation of getenv(), yet the memory leak 51 * is bounded - in normal circumstances total wastage is never greater than 52 * 3x the space needed to hold any _environ list. 53 * 54 * The only abnormal circumstance is if an application modifies the _environ 55 * list pointer directly. Such an application does not conform to POSIX.1 56 * 2001. However, we also care about standards which did not foresee this 57 * issue. For this reason we keep a working copy of our notion of _environ in 58 * my_environ. If, when we are called upon to modify _environ, we ever detect 59 * a mismatch between _environ and my_environ we discard all our assumptions 60 * concerning the location and size of the _environ list. As an additional 61 * precaution we only ever update _environ once we have finished manipulating 62 * our working copy. 63 * 64 * The setenv() API is inherently leaky but we are completely at the mercy 65 * of the application. 66 * 67 * To pacify leak detectors we chain all allocations which are at risk of 68 * being leaked in either of the above two scenarios. chunk_list must only 69 * be updated under the protection of update_lock. 70 * 71 * Although we don't allocate the original _environ list it is likely that 72 * we will leak this too. Accordingly, we create a reference in initenv(). 73 * However, we can't be held responsible for such leaks in abnormal (see 74 * above) circumstances. 75 */ 76 77 typedef struct chunk { 78 struct chunk *next; 79 } chunk_t; 80 81 static mutex_t update_lock = DEFAULTMUTEX; 82 static const char **orig_environ = NULL; 83 static const char **my_environ = NULL; 84 static const char **environ_base = NULL; 85 static int environ_size = 0; 86 static int environ_gen = 0; 87 static int initenv_done = 0; 88 static chunk_t *chunk_list = NULL; 89 90 /* 91 * Compute the size an _environ list including the terminating NULL entry. 92 * This is the only way we have to determine the size of an _environ list 93 * we didn't allocate. 94 */ 95 static int 96 envsize(const char **e) 97 { 98 int size; 99 100 if (e == NULL) 101 return (0); 102 103 for (size = 1; *e != NULL; e++) 104 size++; 105 106 return (size); 107 } 108 109 /* 110 * Initialization for the following scenarios: 111 * 1. The very first time we reference the _environ list we must call in the 112 * NLSPATH janitor, make a reference to the original _environ list to keep 113 * leak detectors happy, initialize my_environ and environ_base, and then 114 * compute environ_size. 115 * 2. Whenever we detect that someone else has hijacked _environ (something 116 * very abnormal) we need to reinitialize my_environ and environ_base, 117 * and then recompute environ_size. 118 * 119 * The local globals my_environ, environ_base and environ_size may be used 120 * by others only if initenv_done is true and only under the protection of 121 * update_lock. However, our callers, who must NOT be holding update_lock, 122 * may safely test initenv_done or my_environ against _environ just prior to 123 * calling us because we test these again whilst holding update_lock. 124 */ 125 static void 126 initenv() 127 { 128 if ((my_environ != _environ) || !initenv_done) { 129 lmutex_lock(&update_lock); 130 if ((my_environ != _environ) || !initenv_done) { 131 if (!initenv_done) { 132 /* Call the NLSPATH janitor in. */ 133 clean_env(); 134 135 /* Pacify leak detectors in normal operation. */ 136 orig_environ = _environ; 137 #ifdef __lint 138 my_environ = orig_environ; 139 #endif 140 } 141 142 my_environ = _environ; 143 environ_base = my_environ; 144 environ_size = envsize(environ_base); 145 membar_producer(); 146 initenv_done = 1; 147 } 148 lmutex_unlock(&update_lock); 149 } 150 membar_consumer(); 151 } 152 153 /* 154 * Search an _environ list for a particular entry. If name_only is set, then 155 * string must be the entry name only, and we return the value of the first 156 * match. Otherwise, string must be of the form "name=value", and we return 157 * the address of the first matching entry. 158 */ 159 static const char ** 160 findenv(const char **e, const char *string, int name_only, char **value) 161 { 162 char target; 163 const char *s1; 164 const char *s2; 165 166 *value = NULL; 167 168 if (e == NULL) 169 return (NULL); 170 171 target = name_only ? '\0' : '='; 172 173 for (; (s2 = *e) != NULL; e++) { 174 s1 = string; 175 176 /* Fast comparison for first char. */ 177 if (*s1 != *s2) 178 continue; 179 180 /* Slow comparison for rest of string. */ 181 while (*s1 == *s2 && *s2 != '=') { 182 s1++; 183 s2++; 184 } 185 186 if (*s1 == target && *s2 == '=') { 187 *value = (char *)s2 + 1; 188 return (e); 189 } 190 } 191 return (NULL); 192 } 193 194 /* 195 * Common code for putenv() and setenv(). We support the lockless getenv() 196 * by inserting new entries at the bottom of the list, and by growing the 197 * list using the trusted "power of two, drop it on the floor" method. We 198 * use a lock (update_lock) to protect all updates to the _environ list, but 199 * we are obliged to release this lock whenever we call malloc() or free(). 200 * A generation number (environ_gen) is bumped whenever names are added to, 201 * or removed from, the _environ list so that we can detect collisions with 202 * other updaters. 203 * 204 * Return values 205 * 0 : success 206 * -1 : with errno set 207 * -2 : an entry already existed and overwrite was zero 208 */ 209 static int 210 addtoenv(char *string, int overwrite) 211 { 212 char *value; 213 const char **p; 214 chunk_t *new_chunk; 215 const char **new_environ; 216 const char **new_base; 217 int new_size; 218 int old_gen; 219 220 initenv(); 221 222 lmutex_lock(&update_lock); 223 224 for (;;) { 225 /* 226 * If the name already exists just overwrite the existing 227 * entry -- except when we were called by setenv() without 228 * the overwrite flag. 229 */ 230 if ((p = findenv(my_environ, string, 0, &value)) != NULL) { 231 if (overwrite) { 232 /* 233 * Replace the value in situ. No name was 234 * added, so there is no need to bump the 235 * generation number. 236 */ 237 *p = string; 238 lmutex_unlock(&update_lock); 239 return (0); 240 } else { 241 /* No change. */ 242 lmutex_unlock(&update_lock); 243 return (-2); 244 } 245 } 246 247 /* Try to insert the new entry at the bottom of the list. */ 248 if (environ_base < my_environ) { 249 /* 250 * The new value must be visible before we decrement 251 * the _environ list pointer. 252 */ 253 my_environ[-1] = string; 254 membar_producer(); 255 my_environ--; 256 _environ = my_environ; 257 258 /* 259 * We've added a name, so bump the generation number. 260 */ 261 environ_gen++; 262 263 lmutex_unlock(&update_lock); 264 return (0); 265 } 266 267 /* 268 * There is no room. Attempt to allocate a new _environ list 269 * which is at least double the size of the current one. See 270 * comment above concerning locking and malloc() etc. 271 */ 272 new_size = environ_size * 2; 273 if (new_size < MIN_ENV_SIZE) 274 new_size = MIN_ENV_SIZE; 275 276 old_gen = environ_gen; 277 lmutex_unlock(&update_lock); 278 279 new_chunk = malloc(sizeof (chunk_t) + 280 new_size * sizeof (char *)); 281 if (new_chunk == NULL) { 282 errno = ENOMEM; 283 return (-1); 284 } 285 286 lmutex_lock(&update_lock); 287 288 /* 289 * If no other thread added or removed names while the lock 290 * was dropped, it is time to break out of this loop. 291 */ 292 if (environ_gen == old_gen) 293 break; 294 295 /* 296 * At least one name has been added or removed, so we need to 297 * try again. It is very likely that we will find sufficient 298 * space the next time around. 299 */ 300 lmutex_unlock(&update_lock); 301 free(new_chunk); 302 lmutex_lock(&update_lock); 303 } 304 305 /* Add the new chunk to chunk_list to hide potential future leak. */ 306 new_chunk->next = chunk_list; 307 chunk_list = new_chunk; 308 309 /* Copy the old _environ list into the top of the new _environ list. */ 310 new_base = (const char **)(new_chunk + 1); 311 new_environ = &new_base[(new_size - 1) - environ_size]; 312 (void) memcpy(new_environ, my_environ, environ_size * sizeof (char *)); 313 314 /* Insert the new entry at the bottom of the new _environ list. */ 315 new_environ[-1] = string; 316 new_environ--; 317 318 /* Ensure that the new _environ list is visible to all. */ 319 membar_producer(); 320 321 /* Make the switch (dropping the old _environ list on the floor). */ 322 environ_base = new_base; 323 my_environ = new_environ; 324 _environ = my_environ; 325 environ_size = new_size; 326 327 /* We've added a name, so bump the generation number. */ 328 environ_gen++; 329 330 lmutex_unlock(&update_lock); 331 return (0); 332 } 333 334 /* 335 * All the work for putenv() is done in addtoenv(). 336 */ 337 int 338 putenv(char *string) 339 { 340 /* 341 * Historically a call to putenv() with no '=' in the string would work 342 * great until someone called getenv() on that particular environment 343 * variable again. As we've always treated this as valid, rather than 344 * teaching the rest of the environment code how to handle something 345 * without an '=' sign, it instead just calls unsetenv(). 346 */ 347 if (strchr(string, '=') == NULL) 348 return (unsetenv(string)); 349 350 return (addtoenv(string, 1)); 351 } 352 353 /* 354 * setenv() is a little more complex than putenv() because we have to allocate 355 * and construct an _environ entry on behalf of the caller. The bulk of the 356 * work is still done in addtoenv(). 357 */ 358 359 int 360 setenv(const char *envname, const char *envval, int overwrite) 361 { 362 chunk_t *new_chunk; 363 char *new_string; 364 size_t name_len; 365 size_t val_len; 366 int res; 367 368 if (envname == NULL || *envname == 0 || strchr(envname, '=') != NULL) { 369 errno = EINVAL; 370 return (-1); 371 } 372 373 name_len = strlen(envname); 374 val_len = strlen(envval); 375 376 new_chunk = malloc(sizeof (chunk_t) + name_len + val_len + 2); 377 if (new_chunk == NULL) { 378 errno = ENOMEM; 379 return (-1); 380 } 381 new_string = (char *)(new_chunk + 1); 382 383 (void) memcpy(new_string, envname, name_len); 384 new_string[name_len] = '='; 385 (void) memcpy(new_string + name_len + 1, envval, val_len); 386 new_string[name_len + 1 + val_len] = 0; 387 388 if ((res = addtoenv(new_string, overwrite)) < 0) { 389 free(new_chunk); 390 if (res == -2) { 391 /* The name already existed, but not an error. */ 392 return (0); 393 } else { 394 /* i.e. res == -1 which means only one thing. */ 395 errno = ENOMEM; 396 return (-1); 397 } 398 } 399 400 /* Hide potential leak of new_string. */ 401 lmutex_lock(&update_lock); 402 new_chunk->next = chunk_list; 403 chunk_list = new_chunk; 404 lmutex_unlock(&update_lock); 405 406 return (0); 407 } 408 409 /* 410 * unsetenv() is tricky because we need to compress the _environ list in a way 411 * which supports a lockless getenv(). The approach here is to move the first 412 * entry from the enrivon list into the space occupied by the entry to be 413 * deleted, and then to increment _environ. This has the added advantage of 414 * making _any_ incremental linear search of the _environ list consistent (i.e. 415 * we will not break any naughty apps which read the list without our help). 416 */ 417 int 418 unsetenv(const char *name) 419 { 420 const char **p; 421 char *value; 422 423 if (name == NULL || *name == 0 || strchr(name, '=') != NULL) { 424 errno = EINVAL; 425 return (-1); 426 } 427 428 initenv(); 429 430 lmutex_lock(&update_lock); 431 432 /* 433 * Find the target, overwrite it with the first entry, increment the 434 * _environ pointer. 435 */ 436 if ((p = findenv(my_environ, name, 1, &value)) != NULL) { 437 /* Overwrite target with the first entry. */ 438 *p = my_environ[0]; 439 440 /* Ensure that the moved entry is visible to all. */ 441 membar_producer(); 442 443 /* Shrink the _environ list. */ 444 my_environ++; 445 _environ = my_environ; 446 447 /* Make sure addtoenv() knows that we've removed a name. */ 448 environ_gen++; 449 } 450 451 lmutex_unlock(&update_lock); 452 return (0); 453 } 454 455 /* 456 * Dump entire environment. 457 */ 458 int 459 clearenv(void) 460 { 461 /* 462 * Just drop the entire environment list on the floor, as it 463 * would be non-trivial to try and free the used memory. 464 */ 465 static const char *nullp = NULL; 466 467 lmutex_lock(&update_lock); 468 _environ = &nullp; 469 my_environ = NULL; 470 environ_base = NULL; 471 environ_size = 0; 472 environ_gen++; 473 membar_producer(); 474 lmutex_unlock(&update_lock); 475 476 return (0); 477 } 478 479 /* 480 * At last, a lockless implementation of getenv()! 481 */ 482 char * 483 getenv(const char *name) 484 { 485 char *value; 486 487 initenv(); 488 489 if (findenv(_environ, name, 1, &value) != NULL) 490 return (value); 491 492 return (NULL); 493 } 494