1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/base/arena.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1214 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * arena.c 1.10 + * 1.11 + * This contains the implementation of NSS's thread-safe arenas. 1.12 + */ 1.13 + 1.14 +#ifndef BASE_H 1.15 +#include "base.h" 1.16 +#endif /* BASE_H */ 1.17 + 1.18 +#ifdef ARENA_THREADMARK 1.19 +#include "prthread.h" 1.20 +#endif /* ARENA_THREADMARK */ 1.21 + 1.22 +#include "prlock.h" 1.23 +#include "plarena.h" 1.24 + 1.25 +#include <string.h> 1.26 + 1.27 +/* 1.28 + * NSSArena 1.29 + * 1.30 + * This is based on NSPR's arena code, but it is threadsafe. 1.31 + * 1.32 + * The public methods relating to this type are: 1.33 + * 1.34 + * NSSArena_Create -- constructor 1.35 + * NSSArena_Destroy 1.36 + * NSS_ZAlloc 1.37 + * NSS_ZRealloc 1.38 + * NSS_ZFreeIf 1.39 + * 1.40 + * The nonpublic methods relating to this type are: 1.41 + * 1.42 + * nssArena_Create -- constructor 1.43 + * nssArena_Destroy 1.44 + * nssArena_Mark 1.45 + * nssArena_Release 1.46 + * nssArena_Unmark 1.47 + * 1.48 + * nss_ZAlloc 1.49 + * nss_ZFreeIf 1.50 + * nss_ZRealloc 1.51 + * 1.52 + * In debug builds, the following calls are available: 1.53 + * 1.54 + * nssArena_verifyPointer 1.55 + * nssArena_registerDestructor 1.56 + * nssArena_deregisterDestructor 1.57 + */ 1.58 + 1.59 +struct NSSArenaStr { 1.60 + PLArenaPool pool; 1.61 + PRLock *lock; 1.62 +#ifdef ARENA_THREADMARK 1.63 + PRThread *marking_thread; 1.64 + nssArenaMark *first_mark; 1.65 + nssArenaMark *last_mark; 1.66 +#endif /* ARENA_THREADMARK */ 1.67 +#ifdef ARENA_DESTRUCTOR_LIST 1.68 + struct arena_destructor_node *first_destructor; 1.69 + struct arena_destructor_node *last_destructor; 1.70 +#endif /* ARENA_DESTRUCTOR_LIST */ 1.71 +}; 1.72 + 1.73 +/* 1.74 + * nssArenaMark 1.75 + * 1.76 + * This type is used to mark the current state of an NSSArena. 1.77 + */ 1.78 + 1.79 +struct nssArenaMarkStr { 1.80 + PRUint32 magic; 1.81 + void *mark; 1.82 +#ifdef ARENA_THREADMARK 1.83 + nssArenaMark *next; 1.84 +#endif /* ARENA_THREADMARK */ 1.85 +#ifdef ARENA_DESTRUCTOR_LIST 1.86 + struct arena_destructor_node *next_destructor; 1.87 + struct arena_destructor_node *prev_destructor; 1.88 +#endif /* ARENA_DESTRUCTOR_LIST */ 1.89 +}; 1.90 + 1.91 +#define MARK_MAGIC 0x4d41524b /* "MARK" how original */ 1.92 + 1.93 +/* 1.94 + * But first, the pointer-tracking code 1.95 + */ 1.96 +#ifdef DEBUG 1.97 +extern const NSSError NSS_ERROR_INTERNAL_ERROR; 1.98 + 1.99 +static nssPointerTracker arena_pointer_tracker; 1.100 + 1.101 +static PRStatus 1.102 +arena_add_pointer 1.103 +( 1.104 + const NSSArena *arena 1.105 +) 1.106 +{ 1.107 + PRStatus rv; 1.108 + 1.109 + rv = nssPointerTracker_initialize(&arena_pointer_tracker); 1.110 + if( PR_SUCCESS != rv ) { 1.111 + return rv; 1.112 + } 1.113 + 1.114 + rv = nssPointerTracker_add(&arena_pointer_tracker, arena); 1.115 + if( PR_SUCCESS != rv ) { 1.116 + NSSError e = NSS_GetError(); 1.117 + if( NSS_ERROR_NO_MEMORY != e ) { 1.118 + nss_SetError(NSS_ERROR_INTERNAL_ERROR); 1.119 + } 1.120 + 1.121 + return rv; 1.122 + } 1.123 + 1.124 + return PR_SUCCESS; 1.125 +} 1.126 + 1.127 +static PRStatus 1.128 +arena_remove_pointer 1.129 +( 1.130 + const NSSArena *arena 1.131 +) 1.132 +{ 1.133 + PRStatus rv; 1.134 + 1.135 + rv = nssPointerTracker_remove(&arena_pointer_tracker, arena); 1.136 + if( PR_SUCCESS != rv ) { 1.137 + nss_SetError(NSS_ERROR_INTERNAL_ERROR); 1.138 + } 1.139 + 1.140 + return rv; 1.141 +} 1.142 + 1.143 +/* 1.144 + * nssArena_verifyPointer 1.145 + * 1.146 + * This method is only present in debug builds. 1.147 + * 1.148 + * If the specified pointer is a valid pointer to an NSSArena object, 1.149 + * this routine will return PR_SUCCESS. Otherwise, it will put an 1.150 + * error on the error stack and return PR_FAILURE. 1.151 + * 1.152 + * The error may be one of the following values: 1.153 + * NSS_ERROR_INVALID_ARENA 1.154 + * 1.155 + * Return value: 1.156 + * PR_SUCCESS if the pointer is valid 1.157 + * PR_FAILURE if it isn't 1.158 + */ 1.159 + 1.160 +NSS_IMPLEMENT PRStatus 1.161 +nssArena_verifyPointer 1.162 +( 1.163 + const NSSArena *arena 1.164 +) 1.165 +{ 1.166 + PRStatus rv; 1.167 + 1.168 + rv = nssPointerTracker_initialize(&arena_pointer_tracker); 1.169 + if( PR_SUCCESS != rv ) { 1.170 + /* 1.171 + * This is a little disingenious. We have to initialize the 1.172 + * tracker, because someone could "legitimately" try to verify 1.173 + * an arena pointer before one is ever created. And this step 1.174 + * might fail, due to lack of memory. But the only way that 1.175 + * this step can fail is if it's doing the call_once stuff, 1.176 + * (later calls just no-op). And if it didn't no-op, there 1.177 + * aren't any valid arenas.. so the argument certainly isn't one. 1.178 + */ 1.179 + nss_SetError(NSS_ERROR_INVALID_ARENA); 1.180 + return PR_FAILURE; 1.181 + } 1.182 + 1.183 + rv = nssPointerTracker_verify(&arena_pointer_tracker, arena); 1.184 + if( PR_SUCCESS != rv ) { 1.185 + nss_SetError(NSS_ERROR_INVALID_ARENA); 1.186 + return PR_FAILURE; 1.187 + } 1.188 + 1.189 + return PR_SUCCESS; 1.190 +} 1.191 +#endif /* DEBUG */ 1.192 + 1.193 +#ifdef ARENA_DESTRUCTOR_LIST 1.194 + 1.195 +struct arena_destructor_node { 1.196 + struct arena_destructor_node *next; 1.197 + struct arena_destructor_node *prev; 1.198 + void (*destructor)(void *argument); 1.199 + void *arg; 1.200 +}; 1.201 + 1.202 +/* 1.203 + * nssArena_registerDestructor 1.204 + * 1.205 + * This routine stores a pointer to a callback and an arbitrary 1.206 + * pointer-sized argument in the arena, at the current point in 1.207 + * the mark stack. If the arena is destroyed, or an "earlier" 1.208 + * mark is released, then this destructor will be called at that 1.209 + * time. Note that the destructor will be called with the arena 1.210 + * locked, which means the destructor may free memory in that 1.211 + * arena, but it may not allocate or cause to be allocated any 1.212 + * memory. This callback facility was included to support our 1.213 + * debug-version pointer-tracker feature; overuse runs counter to 1.214 + * the the original intent of arenas. This routine returns a 1.215 + * PRStatus value; if successful, it will return PR_SUCCESS. If 1.216 + * unsuccessful, it will set an error on the error stack and 1.217 + * return PR_FAILURE. 1.218 + * 1.219 + * The error may be one of the following values: 1.220 + * NSS_ERROR_INVALID_ARENA 1.221 + * NSS_ERROR_NO_MEMORY 1.222 + * 1.223 + * Return value: 1.224 + * PR_SUCCESS 1.225 + * PR_FAILURE 1.226 + */ 1.227 + 1.228 +NSS_IMPLEMENT PRStatus 1.229 +nssArena_registerDestructor 1.230 +( 1.231 + NSSArena *arena, 1.232 + void (*destructor)(void *argument), 1.233 + void *arg 1.234 +) 1.235 +{ 1.236 + struct arena_destructor_node *it; 1.237 + 1.238 +#ifdef NSSDEBUG 1.239 + if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { 1.240 + return PR_FAILURE; 1.241 + } 1.242 +#endif /* NSSDEBUG */ 1.243 + 1.244 + it = nss_ZNEW(arena, struct arena_destructor_node); 1.245 + if( (struct arena_destructor_node *)NULL == it ) { 1.246 + return PR_FAILURE; 1.247 + } 1.248 + 1.249 + it->prev = arena->last_destructor; 1.250 + arena->last_destructor->next = it; 1.251 + arena->last_destructor = it; 1.252 + it->destructor = destructor; 1.253 + it->arg = arg; 1.254 + 1.255 + if( (nssArenaMark *)NULL != arena->last_mark ) { 1.256 + arena->last_mark->prev_destructor = it->prev; 1.257 + arena->last_mark->next_destructor = it->next; 1.258 + } 1.259 + 1.260 + return PR_SUCCESS; 1.261 +} 1.262 + 1.263 +NSS_IMPLEMENT PRStatus 1.264 +nssArena_deregisterDestructor 1.265 +( 1.266 + NSSArena *arena, 1.267 + void (*destructor)(void *argument), 1.268 + void *arg 1.269 +) 1.270 +{ 1.271 + struct arena_destructor_node *it; 1.272 + 1.273 +#ifdef NSSDEBUG 1.274 + if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { 1.275 + return PR_FAILURE; 1.276 + } 1.277 +#endif /* NSSDEBUG */ 1.278 + 1.279 + for( it = arena->first_destructor; it; it = it->next ) { 1.280 + if( (it->destructor == destructor) && (it->arg == arg) ) { 1.281 + break; 1.282 + } 1.283 + } 1.284 + 1.285 + if( (struct arena_destructor_node *)NULL == it ) { 1.286 + nss_SetError(NSS_ERROR_NOT_FOUND); 1.287 + return PR_FAILURE; 1.288 + } 1.289 + 1.290 + if( it == arena->first_destructor ) { 1.291 + arena->first_destructor = it->next; 1.292 + } 1.293 + 1.294 + if( it == arena->last_destructor ) { 1.295 + arena->last_destructor = it->prev; 1.296 + } 1.297 + 1.298 + if( (struct arena_destructor_node *)NULL != it->prev ) { 1.299 + it->prev->next = it->next; 1.300 + } 1.301 + 1.302 + if( (struct arena_destructor_node *)NULL != it->next ) { 1.303 + it->next->prev = it->prev; 1.304 + } 1.305 + 1.306 + { 1.307 + nssArenaMark *m; 1.308 + for( m = arena->first_mark; m; m = m->next ) { 1.309 + if( m->next_destructor == it ) { 1.310 + m->next_destructor = it->next; 1.311 + } 1.312 + if( m->prev_destructor == it ) { 1.313 + m->prev_destructor = it->prev; 1.314 + } 1.315 + } 1.316 + } 1.317 + 1.318 + nss_ZFreeIf(it); 1.319 + return PR_SUCCESS; 1.320 +} 1.321 + 1.322 +static void 1.323 +nss_arena_call_destructor_chain 1.324 +( 1.325 + struct arena_destructor_node *it 1.326 +) 1.327 +{ 1.328 + for( ; it ; it = it->next ) { 1.329 + (*(it->destructor))(it->arg); 1.330 + } 1.331 +} 1.332 + 1.333 +#endif /* ARENA_DESTRUCTOR_LIST */ 1.334 + 1.335 +/* 1.336 + * NSSArena_Create 1.337 + * 1.338 + * This routine creates a new memory arena. This routine may return 1.339 + * NULL upon error, in which case it will have created an error stack. 1.340 + * 1.341 + * The top-level error may be one of the following values: 1.342 + * NSS_ERROR_NO_MEMORY 1.343 + * 1.344 + * Return value: 1.345 + * NULL upon error 1.346 + * A pointer to an NSSArena upon success 1.347 + */ 1.348 + 1.349 +NSS_IMPLEMENT NSSArena * 1.350 +NSSArena_Create 1.351 +( 1.352 + void 1.353 +) 1.354 +{ 1.355 + nss_ClearErrorStack(); 1.356 + return nssArena_Create(); 1.357 +} 1.358 + 1.359 +/* 1.360 + * nssArena_Create 1.361 + * 1.362 + * This routine creates a new memory arena. This routine may return 1.363 + * NULL upon error, in which case it will have set an error on the 1.364 + * error stack. 1.365 + * 1.366 + * The error may be one of the following values: 1.367 + * NSS_ERROR_NO_MEMORY 1.368 + * 1.369 + * Return value: 1.370 + * NULL upon error 1.371 + * A pointer to an NSSArena upon success 1.372 + */ 1.373 + 1.374 +NSS_IMPLEMENT NSSArena * 1.375 +nssArena_Create 1.376 +( 1.377 + void 1.378 +) 1.379 +{ 1.380 + NSSArena *rv = (NSSArena *)NULL; 1.381 + 1.382 + rv = nss_ZNEW((NSSArena *)NULL, NSSArena); 1.383 + if( (NSSArena *)NULL == rv ) { 1.384 + nss_SetError(NSS_ERROR_NO_MEMORY); 1.385 + return (NSSArena *)NULL; 1.386 + } 1.387 + 1.388 + rv->lock = PR_NewLock(); 1.389 + if( (PRLock *)NULL == rv->lock ) { 1.390 + (void)nss_ZFreeIf(rv); 1.391 + nss_SetError(NSS_ERROR_NO_MEMORY); 1.392 + return (NSSArena *)NULL; 1.393 + } 1.394 + 1.395 + /* 1.396 + * Arena sizes. The current security code has 229 occurrences of 1.397 + * PORT_NewArena. The default chunksizes specified break down as 1.398 + * 1.399 + * Size Mult. Specified as 1.400 + * 512 1 512 1.401 + * 1024 7 1024 1.402 + * 2048 5 2048 1.403 + * 2048 5 CRMF_DEFAULT_ARENA_SIZE 1.404 + * 2048 190 DER_DEFAULT_CHUNKSIZE 1.405 + * 2048 20 SEC_ASN1_DEFAULT_ARENA_SIZE 1.406 + * 4096 1 4096 1.407 + * 1.408 + * Obviously this "default chunksize" flexibility isn't very 1.409 + * useful to us, so I'll just pick 2048. 1.410 + */ 1.411 + 1.412 + PL_InitArenaPool(&rv->pool, "NSS", 2048, sizeof(double)); 1.413 + 1.414 +#ifdef DEBUG 1.415 + { 1.416 + PRStatus st; 1.417 + st = arena_add_pointer(rv); 1.418 + if( PR_SUCCESS != st ) { 1.419 + PL_FinishArenaPool(&rv->pool); 1.420 + PR_DestroyLock(rv->lock); 1.421 + (void)nss_ZFreeIf(rv); 1.422 + return (NSSArena *)NULL; 1.423 + } 1.424 + } 1.425 +#endif /* DEBUG */ 1.426 + 1.427 + return rv; 1.428 +} 1.429 + 1.430 +/* 1.431 + * NSSArena_Destroy 1.432 + * 1.433 + * This routine will destroy the specified arena, freeing all memory 1.434 + * allocated from it. This routine returns a PRStatus value; if 1.435 + * successful, it will return PR_SUCCESS. If unsuccessful, it will 1.436 + * create an error stack and return PR_FAILURE. 1.437 + * 1.438 + * The top-level error may be one of the following values: 1.439 + * NSS_ERROR_INVALID_ARENA 1.440 + * 1.441 + * Return value: 1.442 + * PR_SUCCESS upon success 1.443 + * PR_FAILURE upon failure 1.444 + */ 1.445 + 1.446 +NSS_IMPLEMENT PRStatus 1.447 +NSSArena_Destroy 1.448 +( 1.449 + NSSArena *arena 1.450 +) 1.451 +{ 1.452 + nss_ClearErrorStack(); 1.453 + 1.454 +#ifdef DEBUG 1.455 + if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { 1.456 + return PR_FAILURE; 1.457 + } 1.458 +#endif /* DEBUG */ 1.459 + 1.460 + return nssArena_Destroy(arena); 1.461 +} 1.462 + 1.463 +/* 1.464 + * nssArena_Destroy 1.465 + * 1.466 + * This routine will destroy the specified arena, freeing all memory 1.467 + * allocated from it. This routine returns a PRStatus value; if 1.468 + * successful, it will return PR_SUCCESS. If unsuccessful, it will 1.469 + * set an error on the error stack and return PR_FAILURE. 1.470 + * 1.471 + * The error may be one of the following values: 1.472 + * NSS_ERROR_INVALID_ARENA 1.473 + * 1.474 + * Return value: 1.475 + * PR_SUCCESS 1.476 + * PR_FAILURE 1.477 + */ 1.478 + 1.479 +NSS_IMPLEMENT PRStatus 1.480 +nssArena_Destroy 1.481 +( 1.482 + NSSArena *arena 1.483 +) 1.484 +{ 1.485 + PRLock *lock; 1.486 + 1.487 +#ifdef NSSDEBUG 1.488 + if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { 1.489 + return PR_FAILURE; 1.490 + } 1.491 +#endif /* NSSDEBUG */ 1.492 + 1.493 + if( (PRLock *)NULL == arena->lock ) { 1.494 + /* Just got destroyed */ 1.495 + nss_SetError(NSS_ERROR_INVALID_ARENA); 1.496 + return PR_FAILURE; 1.497 + } 1.498 + PR_Lock(arena->lock); 1.499 + 1.500 +#ifdef DEBUG 1.501 + if( PR_SUCCESS != arena_remove_pointer(arena) ) { 1.502 + PR_Unlock(arena->lock); 1.503 + return PR_FAILURE; 1.504 + } 1.505 +#endif /* DEBUG */ 1.506 + 1.507 +#ifdef ARENA_DESTRUCTOR_LIST 1.508 + /* Note that the arena is locked at this time */ 1.509 + nss_arena_call_destructor_chain(arena->first_destructor); 1.510 +#endif /* ARENA_DESTRUCTOR_LIST */ 1.511 + 1.512 + PL_FinishArenaPool(&arena->pool); 1.513 + lock = arena->lock; 1.514 + arena->lock = (PRLock *)NULL; 1.515 + PR_Unlock(lock); 1.516 + PR_DestroyLock(lock); 1.517 + (void)nss_ZFreeIf(arena); 1.518 + return PR_SUCCESS; 1.519 +} 1.520 + 1.521 +static void *nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size); 1.522 + 1.523 +/* 1.524 + * nssArena_Mark 1.525 + * 1.526 + * This routine "marks" the current state of an arena. Space 1.527 + * allocated after the arena has been marked can be freed by 1.528 + * releasing the arena back to the mark with nssArena_Release, 1.529 + * or committed by calling nssArena_Unmark. When successful, 1.530 + * this routine returns a valid nssArenaMark pointer. This 1.531 + * routine may return NULL upon error, in which case it will 1.532 + * have set an error on the error stack. 1.533 + * 1.534 + * The error may be one of the following values: 1.535 + * NSS_ERROR_INVALID_ARENA 1.536 + * NSS_ERROR_NO_MEMORY 1.537 + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD 1.538 + * 1.539 + * Return value: 1.540 + * NULL upon failure 1.541 + * An nssArenaMark pointer upon success 1.542 + */ 1.543 + 1.544 +NSS_IMPLEMENT nssArenaMark * 1.545 +nssArena_Mark 1.546 +( 1.547 + NSSArena *arena 1.548 +) 1.549 +{ 1.550 + nssArenaMark *rv; 1.551 + void *p; 1.552 + 1.553 +#ifdef NSSDEBUG 1.554 + if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { 1.555 + return (nssArenaMark *)NULL; 1.556 + } 1.557 +#endif /* NSSDEBUG */ 1.558 + 1.559 + if( (PRLock *)NULL == arena->lock ) { 1.560 + /* Just got destroyed */ 1.561 + nss_SetError(NSS_ERROR_INVALID_ARENA); 1.562 + return (nssArenaMark *)NULL; 1.563 + } 1.564 + PR_Lock(arena->lock); 1.565 + 1.566 +#ifdef ARENA_THREADMARK 1.567 + if( (PRThread *)NULL == arena->marking_thread ) { 1.568 + /* Unmarked. Store our thread ID */ 1.569 + arena->marking_thread = PR_GetCurrentThread(); 1.570 + /* This call never fails. */ 1.571 + } else { 1.572 + /* Marked. Verify it's the current thread */ 1.573 + if( PR_GetCurrentThread() != arena->marking_thread ) { 1.574 + PR_Unlock(arena->lock); 1.575 + nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); 1.576 + return (nssArenaMark *)NULL; 1.577 + } 1.578 + } 1.579 +#endif /* ARENA_THREADMARK */ 1.580 + 1.581 + p = PL_ARENA_MARK(&arena->pool); 1.582 + /* No error possible */ 1.583 + 1.584 + /* Do this after the mark */ 1.585 + rv = (nssArenaMark *)nss_zalloc_arena_locked(arena, sizeof(nssArenaMark)); 1.586 + if( (nssArenaMark *)NULL == rv ) { 1.587 + PR_Unlock(arena->lock); 1.588 + nss_SetError(NSS_ERROR_NO_MEMORY); 1.589 + return (nssArenaMark *)NULL; 1.590 + } 1.591 + 1.592 +#ifdef ARENA_THREADMARK 1.593 + if ( (nssArenaMark *)NULL == arena->first_mark) { 1.594 + arena->first_mark = rv; 1.595 + arena->last_mark = rv; 1.596 + } else { 1.597 + arena->last_mark->next = rv; 1.598 + arena->last_mark = rv; 1.599 + } 1.600 +#endif /* ARENA_THREADMARK */ 1.601 + 1.602 + rv->mark = p; 1.603 + rv->magic = MARK_MAGIC; 1.604 + 1.605 +#ifdef ARENA_DESTRUCTOR_LIST 1.606 + rv->prev_destructor = arena->last_destructor; 1.607 +#endif /* ARENA_DESTRUCTOR_LIST */ 1.608 + 1.609 + PR_Unlock(arena->lock); 1.610 + 1.611 + return rv; 1.612 +} 1.613 + 1.614 +/* 1.615 + * nss_arena_unmark_release 1.616 + * 1.617 + * This static routine implements the routines nssArena_Release 1.618 + * ans nssArena_Unmark, which are almost identical. 1.619 + */ 1.620 + 1.621 +static PRStatus 1.622 +nss_arena_unmark_release 1.623 +( 1.624 + NSSArena *arena, 1.625 + nssArenaMark *arenaMark, 1.626 + PRBool release 1.627 +) 1.628 +{ 1.629 + void *inner_mark; 1.630 + 1.631 +#ifdef NSSDEBUG 1.632 + if( PR_SUCCESS != nssArena_verifyPointer(arena) ) { 1.633 + return PR_FAILURE; 1.634 + } 1.635 +#endif /* NSSDEBUG */ 1.636 + 1.637 + if( MARK_MAGIC != arenaMark->magic ) { 1.638 + nss_SetError(NSS_ERROR_INVALID_ARENA_MARK); 1.639 + return PR_FAILURE; 1.640 + } 1.641 + 1.642 + if( (PRLock *)NULL == arena->lock ) { 1.643 + /* Just got destroyed */ 1.644 + nss_SetError(NSS_ERROR_INVALID_ARENA); 1.645 + return PR_FAILURE; 1.646 + } 1.647 + PR_Lock(arena->lock); 1.648 + 1.649 +#ifdef ARENA_THREADMARK 1.650 + if( (PRThread *)NULL != arena->marking_thread ) { 1.651 + if( PR_GetCurrentThread() != arena->marking_thread ) { 1.652 + PR_Unlock(arena->lock); 1.653 + nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); 1.654 + return PR_FAILURE; 1.655 + } 1.656 + } 1.657 +#endif /* ARENA_THREADMARK */ 1.658 + 1.659 + if( MARK_MAGIC != arenaMark->magic ) { 1.660 + /* Just got released */ 1.661 + PR_Unlock(arena->lock); 1.662 + nss_SetError(NSS_ERROR_INVALID_ARENA_MARK); 1.663 + return PR_FAILURE; 1.664 + } 1.665 + 1.666 + arenaMark->magic = 0; 1.667 + inner_mark = arenaMark->mark; 1.668 + 1.669 +#ifdef ARENA_THREADMARK 1.670 + { 1.671 + nssArenaMark **pMark = &arena->first_mark; 1.672 + nssArenaMark *rest; 1.673 + nssArenaMark *last = (nssArenaMark *)NULL; 1.674 + 1.675 + /* Find this mark */ 1.676 + while( *pMark != arenaMark ) { 1.677 + last = *pMark; 1.678 + pMark = &(*pMark)->next; 1.679 + } 1.680 + 1.681 + /* Remember the pointer, then zero it */ 1.682 + rest = (*pMark)->next; 1.683 + *pMark = (nssArenaMark *)NULL; 1.684 + 1.685 + arena->last_mark = last; 1.686 + 1.687 + /* Invalidate any later marks being implicitly released */ 1.688 + for( ; (nssArenaMark *)NULL != rest; rest = rest->next ) { 1.689 + rest->magic = 0; 1.690 + } 1.691 + 1.692 + /* If we just got rid of the first mark, clear the thread ID */ 1.693 + if( (nssArenaMark *)NULL == arena->first_mark ) { 1.694 + arena->marking_thread = (PRThread *)NULL; 1.695 + } 1.696 + } 1.697 +#endif /* ARENA_THREADMARK */ 1.698 + 1.699 + if( release ) { 1.700 +#ifdef ARENA_DESTRUCTOR_LIST 1.701 + if( (struct arena_destructor_node *)NULL != arenaMark->prev_destructor ) { 1.702 + arenaMark->prev_destructor->next = (struct arena_destructor_node *)NULL; 1.703 + } 1.704 + arena->last_destructor = arenaMark->prev_destructor; 1.705 + 1.706 + /* Note that the arena is locked at this time */ 1.707 + nss_arena_call_destructor_chain(arenaMark->next_destructor); 1.708 +#endif /* ARENA_DESTRUCTOR_LIST */ 1.709 + 1.710 + PL_ARENA_RELEASE(&arena->pool, inner_mark); 1.711 + /* No error return */ 1.712 + } 1.713 + 1.714 + PR_Unlock(arena->lock); 1.715 + return PR_SUCCESS; 1.716 +} 1.717 + 1.718 +/* 1.719 + * nssArena_Release 1.720 + * 1.721 + * This routine invalidates and releases all memory allocated from 1.722 + * the specified arena after the point at which the specified mark 1.723 + * was obtained. This routine returns a PRStatus value; if successful, 1.724 + * it will return PR_SUCCESS. If unsuccessful, it will set an error 1.725 + * on the error stack and return PR_FAILURE. 1.726 + * 1.727 + * The error may be one of the following values: 1.728 + * NSS_ERROR_INVALID_ARENA 1.729 + * NSS_ERROR_INVALID_ARENA_MARK 1.730 + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD 1.731 + * 1.732 + * Return value: 1.733 + * PR_SUCCESS 1.734 + * PR_FAILURE 1.735 + */ 1.736 + 1.737 +NSS_IMPLEMENT PRStatus 1.738 +nssArena_Release 1.739 +( 1.740 + NSSArena *arena, 1.741 + nssArenaMark *arenaMark 1.742 +) 1.743 +{ 1.744 + return nss_arena_unmark_release(arena, arenaMark, PR_TRUE); 1.745 +} 1.746 + 1.747 +/* 1.748 + * nssArena_Unmark 1.749 + * 1.750 + * This routine "commits" the indicated mark and any marks after 1.751 + * it, making them unreleasable. Note that any earlier marks can 1.752 + * still be released, and such a release will invalidate these 1.753 + * later unmarked regions. If an arena is to be safely shared by 1.754 + * more than one thread, all marks must be either released or 1.755 + * unmarked. This routine returns a PRStatus value; if successful, 1.756 + * it will return PR_SUCCESS. If unsuccessful, it will set an error 1.757 + * on the error stack and return PR_FAILURE. 1.758 + * 1.759 + * The error may be one of the following values: 1.760 + * NSS_ERROR_INVALID_ARENA 1.761 + * NSS_ERROR_INVALID_ARENA_MARK 1.762 + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD 1.763 + * 1.764 + * Return value: 1.765 + * PR_SUCCESS 1.766 + * PR_FAILURE 1.767 + */ 1.768 + 1.769 +NSS_IMPLEMENT PRStatus 1.770 +nssArena_Unmark 1.771 +( 1.772 + NSSArena *arena, 1.773 + nssArenaMark *arenaMark 1.774 +) 1.775 +{ 1.776 + return nss_arena_unmark_release(arena, arenaMark, PR_FALSE); 1.777 +} 1.778 + 1.779 +/* 1.780 + * We prefix this header to all allocated blocks. It is a multiple 1.781 + * of the alignment size. Note that this usage of a header may make 1.782 + * purify spew bogus warnings about "potentially leaked blocks" of 1.783 + * memory; if that gets too annoying we can add in a pointer to the 1.784 + * header in the header itself. There's not a lot of safety here; 1.785 + * maybe we should add a magic value? 1.786 + */ 1.787 +struct pointer_header { 1.788 + NSSArena *arena; 1.789 + PRUint32 size; 1.790 +}; 1.791 + 1.792 +static void * 1.793 +nss_zalloc_arena_locked 1.794 +( 1.795 + NSSArena *arena, 1.796 + PRUint32 size 1.797 +) 1.798 +{ 1.799 + void *p; 1.800 + void *rv; 1.801 + struct pointer_header *h; 1.802 + PRUint32 my_size = size + sizeof(struct pointer_header); 1.803 + PL_ARENA_ALLOCATE(p, &arena->pool, my_size); 1.804 + if( (void *)NULL == p ) { 1.805 + nss_SetError(NSS_ERROR_NO_MEMORY); 1.806 + return (void *)NULL; 1.807 + } 1.808 + /* 1.809 + * Do this before we unlock. This way if the user is using 1.810 + * an arena in one thread while destroying it in another, he'll 1.811 + * fault/FMR in his code, not ours. 1.812 + */ 1.813 + h = (struct pointer_header *)p; 1.814 + h->arena = arena; 1.815 + h->size = size; 1.816 + rv = (void *)((char *)h + sizeof(struct pointer_header)); 1.817 + (void)nsslibc_memset(rv, 0, size); 1.818 + return rv; 1.819 +} 1.820 + 1.821 +/* 1.822 + * NSS_ZAlloc 1.823 + * 1.824 + * This routine allocates and zeroes a section of memory of the 1.825 + * size, and returns to the caller a pointer to that memory. If 1.826 + * the optional arena argument is non-null, the memory will be 1.827 + * obtained from that arena; otherwise, the memory will be obtained 1.828 + * from the heap. This routine may return NULL upon error, in 1.829 + * which case it will have set an error upon the error stack. The 1.830 + * value specified for size may be zero; in which case a valid 1.831 + * zero-length block of memory will be allocated. This block may 1.832 + * be expanded by calling NSS_ZRealloc. 1.833 + * 1.834 + * The error may be one of the following values: 1.835 + * NSS_ERROR_INVALID_ARENA 1.836 + * NSS_ERROR_NO_MEMORY 1.837 + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD 1.838 + * 1.839 + * Return value: 1.840 + * NULL upon error 1.841 + * A pointer to the new segment of zeroed memory 1.842 + */ 1.843 + 1.844 +NSS_IMPLEMENT void * 1.845 +NSS_ZAlloc 1.846 +( 1.847 + NSSArena *arenaOpt, 1.848 + PRUint32 size 1.849 +) 1.850 +{ 1.851 + return nss_ZAlloc(arenaOpt, size); 1.852 +} 1.853 + 1.854 +/* 1.855 + * nss_ZAlloc 1.856 + * 1.857 + * This routine allocates and zeroes a section of memory of the 1.858 + * size, and returns to the caller a pointer to that memory. If 1.859 + * the optional arena argument is non-null, the memory will be 1.860 + * obtained from that arena; otherwise, the memory will be obtained 1.861 + * from the heap. This routine may return NULL upon error, in 1.862 + * which case it will have set an error upon the error stack. The 1.863 + * value specified for size may be zero; in which case a valid 1.864 + * zero-length block of memory will be allocated. This block may 1.865 + * be expanded by calling nss_ZRealloc. 1.866 + * 1.867 + * The error may be one of the following values: 1.868 + * NSS_ERROR_INVALID_ARENA 1.869 + * NSS_ERROR_NO_MEMORY 1.870 + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD 1.871 + * 1.872 + * Return value: 1.873 + * NULL upon error 1.874 + * A pointer to the new segment of zeroed memory 1.875 + */ 1.876 + 1.877 +NSS_IMPLEMENT void * 1.878 +nss_ZAlloc 1.879 +( 1.880 + NSSArena *arenaOpt, 1.881 + PRUint32 size 1.882 +) 1.883 +{ 1.884 + struct pointer_header *h; 1.885 + PRUint32 my_size = size + sizeof(struct pointer_header); 1.886 + 1.887 + if( my_size < sizeof(struct pointer_header) ) { 1.888 + /* Wrapped */ 1.889 + nss_SetError(NSS_ERROR_NO_MEMORY); 1.890 + return (void *)NULL; 1.891 + } 1.892 + 1.893 + if( (NSSArena *)NULL == arenaOpt ) { 1.894 + /* Heap allocation, no locking required. */ 1.895 + h = (struct pointer_header *)PR_Calloc(1, my_size); 1.896 + if( (struct pointer_header *)NULL == h ) { 1.897 + nss_SetError(NSS_ERROR_NO_MEMORY); 1.898 + return (void *)NULL; 1.899 + } 1.900 + 1.901 + h->arena = (NSSArena *)NULL; 1.902 + h->size = size; 1.903 + /* We used calloc: it's already zeroed */ 1.904 + 1.905 + return (void *)((char *)h + sizeof(struct pointer_header)); 1.906 + } else { 1.907 + void *rv; 1.908 + /* Arena allocation */ 1.909 +#ifdef NSSDEBUG 1.910 + if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) { 1.911 + return (void *)NULL; 1.912 + } 1.913 +#endif /* NSSDEBUG */ 1.914 + 1.915 + if( (PRLock *)NULL == arenaOpt->lock ) { 1.916 + /* Just got destroyed */ 1.917 + nss_SetError(NSS_ERROR_INVALID_ARENA); 1.918 + return (void *)NULL; 1.919 + } 1.920 + PR_Lock(arenaOpt->lock); 1.921 + 1.922 +#ifdef ARENA_THREADMARK 1.923 + if( (PRThread *)NULL != arenaOpt->marking_thread ) { 1.924 + if( PR_GetCurrentThread() != arenaOpt->marking_thread ) { 1.925 + nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); 1.926 + PR_Unlock(arenaOpt->lock); 1.927 + return (void *)NULL; 1.928 + } 1.929 + } 1.930 +#endif /* ARENA_THREADMARK */ 1.931 + 1.932 + rv = nss_zalloc_arena_locked(arenaOpt, size); 1.933 + 1.934 + PR_Unlock(arenaOpt->lock); 1.935 + return rv; 1.936 + } 1.937 + /*NOTREACHED*/ 1.938 +} 1.939 + 1.940 +/* 1.941 + * NSS_ZFreeIf 1.942 + * 1.943 + * If the specified pointer is non-null, then the region of memory 1.944 + * to which it points -- which must have been allocated with 1.945 + * NSS_ZAlloc -- will be zeroed and released. This routine 1.946 + * returns a PRStatus value; if successful, it will return PR_SUCCESS. 1.947 + * If unsuccessful, it will set an error on the error stack and return 1.948 + * PR_FAILURE. 1.949 + * 1.950 + * The error may be one of the following values: 1.951 + * NSS_ERROR_INVALID_POINTER 1.952 + * 1.953 + * Return value: 1.954 + * PR_SUCCESS 1.955 + * PR_FAILURE 1.956 + */ 1.957 +NSS_IMPLEMENT PRStatus 1.958 +NSS_ZFreeIf 1.959 +( 1.960 + void *pointer 1.961 +) 1.962 +{ 1.963 + return nss_ZFreeIf(pointer); 1.964 +} 1.965 + 1.966 +/* 1.967 + * nss_ZFreeIf 1.968 + * 1.969 + * If the specified pointer is non-null, then the region of memory 1.970 + * to which it points -- which must have been allocated with 1.971 + * nss_ZAlloc -- will be zeroed and released. This routine 1.972 + * returns a PRStatus value; if successful, it will return PR_SUCCESS. 1.973 + * If unsuccessful, it will set an error on the error stack and return 1.974 + * PR_FAILURE. 1.975 + * 1.976 + * The error may be one of the following values: 1.977 + * NSS_ERROR_INVALID_POINTER 1.978 + * 1.979 + * Return value: 1.980 + * PR_SUCCESS 1.981 + * PR_FAILURE 1.982 + */ 1.983 + 1.984 +NSS_IMPLEMENT PRStatus 1.985 +nss_ZFreeIf 1.986 +( 1.987 + void *pointer 1.988 +) 1.989 +{ 1.990 + struct pointer_header *h; 1.991 + 1.992 + if( (void *)NULL == pointer ) { 1.993 + return PR_SUCCESS; 1.994 + } 1.995 + 1.996 + h = (struct pointer_header *)((char *)pointer 1.997 + - sizeof(struct pointer_header)); 1.998 + 1.999 + /* Check any magic here */ 1.1000 + 1.1001 + if( (NSSArena *)NULL == h->arena ) { 1.1002 + /* Heap */ 1.1003 + (void)nsslibc_memset(pointer, 0, h->size); 1.1004 + PR_Free(h); 1.1005 + return PR_SUCCESS; 1.1006 + } else { 1.1007 + /* Arena */ 1.1008 +#ifdef NSSDEBUG 1.1009 + if( PR_SUCCESS != nssArena_verifyPointer(h->arena) ) { 1.1010 + return PR_FAILURE; 1.1011 + } 1.1012 +#endif /* NSSDEBUG */ 1.1013 + 1.1014 + if( (PRLock *)NULL == h->arena->lock ) { 1.1015 + /* Just got destroyed.. so this pointer is invalid */ 1.1016 + nss_SetError(NSS_ERROR_INVALID_POINTER); 1.1017 + return PR_FAILURE; 1.1018 + } 1.1019 + PR_Lock(h->arena->lock); 1.1020 + 1.1021 + (void)nsslibc_memset(pointer, 0, h->size); 1.1022 + 1.1023 + /* No way to "free" it within an NSPR arena. */ 1.1024 + 1.1025 + PR_Unlock(h->arena->lock); 1.1026 + return PR_SUCCESS; 1.1027 + } 1.1028 + /*NOTREACHED*/ 1.1029 +} 1.1030 + 1.1031 +/* 1.1032 + * NSS_ZRealloc 1.1033 + * 1.1034 + * This routine reallocates a block of memory obtained by calling 1.1035 + * nss_ZAlloc or nss_ZRealloc. The portion of memory 1.1036 + * between the new and old sizes -- which is either being newly 1.1037 + * obtained or released -- is in either case zeroed. This routine 1.1038 + * may return NULL upon failure, in which case it will have placed 1.1039 + * an error on the error stack. 1.1040 + * 1.1041 + * The error may be one of the following values: 1.1042 + * NSS_ERROR_INVALID_POINTER 1.1043 + * NSS_ERROR_NO_MEMORY 1.1044 + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD 1.1045 + * 1.1046 + * Return value: 1.1047 + * NULL upon error 1.1048 + * A pointer to the replacement segment of memory 1.1049 + */ 1.1050 + 1.1051 +NSS_EXTERN void * 1.1052 +NSS_ZRealloc 1.1053 +( 1.1054 + void *pointer, 1.1055 + PRUint32 newSize 1.1056 +) 1.1057 +{ 1.1058 + return nss_ZRealloc(pointer, newSize); 1.1059 +} 1.1060 + 1.1061 +/* 1.1062 + * nss_ZRealloc 1.1063 + * 1.1064 + * This routine reallocates a block of memory obtained by calling 1.1065 + * nss_ZAlloc or nss_ZRealloc. The portion of memory 1.1066 + * between the new and old sizes -- which is either being newly 1.1067 + * obtained or released -- is in either case zeroed. This routine 1.1068 + * may return NULL upon failure, in which case it will have placed 1.1069 + * an error on the error stack. 1.1070 + * 1.1071 + * The error may be one of the following values: 1.1072 + * NSS_ERROR_INVALID_POINTER 1.1073 + * NSS_ERROR_NO_MEMORY 1.1074 + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD 1.1075 + * 1.1076 + * Return value: 1.1077 + * NULL upon error 1.1078 + * A pointer to the replacement segment of memory 1.1079 + */ 1.1080 + 1.1081 +NSS_EXTERN void * 1.1082 +nss_ZRealloc 1.1083 +( 1.1084 + void *pointer, 1.1085 + PRUint32 newSize 1.1086 +) 1.1087 +{ 1.1088 + NSSArena *arena; 1.1089 + struct pointer_header *h, *new_h; 1.1090 + PRUint32 my_newSize = newSize + sizeof(struct pointer_header); 1.1091 + void *rv; 1.1092 + 1.1093 + if( my_newSize < sizeof(struct pointer_header) ) { 1.1094 + /* Wrapped */ 1.1095 + nss_SetError(NSS_ERROR_NO_MEMORY); 1.1096 + return (void *)NULL; 1.1097 + } 1.1098 + 1.1099 + if( (void *)NULL == pointer ) { 1.1100 + nss_SetError(NSS_ERROR_INVALID_POINTER); 1.1101 + return (void *)NULL; 1.1102 + } 1.1103 + 1.1104 + h = (struct pointer_header *)((char *)pointer 1.1105 + - sizeof(struct pointer_header)); 1.1106 + 1.1107 + /* Check any magic here */ 1.1108 + 1.1109 + if( newSize == h->size ) { 1.1110 + /* saves thrashing */ 1.1111 + return pointer; 1.1112 + } 1.1113 + 1.1114 + arena = h->arena; 1.1115 + if (!arena) { 1.1116 + /* Heap */ 1.1117 + new_h = (struct pointer_header *)PR_Calloc(1, my_newSize); 1.1118 + if( (struct pointer_header *)NULL == new_h ) { 1.1119 + nss_SetError(NSS_ERROR_NO_MEMORY); 1.1120 + return (void *)NULL; 1.1121 + } 1.1122 + 1.1123 + new_h->arena = (NSSArena *)NULL; 1.1124 + new_h->size = newSize; 1.1125 + rv = (void *)((char *)new_h + sizeof(struct pointer_header)); 1.1126 + 1.1127 + if( newSize > h->size ) { 1.1128 + (void)nsslibc_memcpy(rv, pointer, h->size); 1.1129 + (void)nsslibc_memset(&((char *)rv)[ h->size ], 1.1130 + 0, (newSize - h->size)); 1.1131 + } else { 1.1132 + (void)nsslibc_memcpy(rv, pointer, newSize); 1.1133 + } 1.1134 + 1.1135 + (void)nsslibc_memset(pointer, 0, h->size); 1.1136 + h->size = 0; 1.1137 + PR_Free(h); 1.1138 + 1.1139 + return rv; 1.1140 + } else { 1.1141 + void *p; 1.1142 + /* Arena */ 1.1143 +#ifdef NSSDEBUG 1.1144 + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { 1.1145 + return (void *)NULL; 1.1146 + } 1.1147 +#endif /* NSSDEBUG */ 1.1148 + 1.1149 + if (!arena->lock) { 1.1150 + /* Just got destroyed.. so this pointer is invalid */ 1.1151 + nss_SetError(NSS_ERROR_INVALID_POINTER); 1.1152 + return (void *)NULL; 1.1153 + } 1.1154 + PR_Lock(arena->lock); 1.1155 + 1.1156 +#ifdef ARENA_THREADMARK 1.1157 + if (arena->marking_thread) { 1.1158 + if (PR_GetCurrentThread() != arena->marking_thread) { 1.1159 + PR_Unlock(arena->lock); 1.1160 + nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); 1.1161 + return (void *)NULL; 1.1162 + } 1.1163 + } 1.1164 +#endif /* ARENA_THREADMARK */ 1.1165 + 1.1166 + if( newSize < h->size ) { 1.1167 + /* 1.1168 + * We have no general way of returning memory to the arena 1.1169 + * (mark/release doesn't work because things may have been 1.1170 + * allocated after this object), so the memory is gone 1.1171 + * anyway. We might as well just return the same pointer to 1.1172 + * the user, saying "yeah, uh-hunh, you can only use less of 1.1173 + * it now." We'll zero the leftover part, of course. And 1.1174 + * in fact we might as well *not* adjust h->size-- this way, 1.1175 + * if the user reallocs back up to something not greater than 1.1176 + * the original size, then voila, there's the memory! This 1.1177 + * way a thrash big/small/big/small doesn't burn up the arena. 1.1178 + */ 1.1179 + char *extra = &((char *)pointer)[ newSize ]; 1.1180 + (void)nsslibc_memset(extra, 0, (h->size - newSize)); 1.1181 + PR_Unlock(arena->lock); 1.1182 + return pointer; 1.1183 + } 1.1184 + 1.1185 + PL_ARENA_ALLOCATE(p, &arena->pool, my_newSize); 1.1186 + if( (void *)NULL == p ) { 1.1187 + PR_Unlock(arena->lock); 1.1188 + nss_SetError(NSS_ERROR_NO_MEMORY); 1.1189 + return (void *)NULL; 1.1190 + } 1.1191 + 1.1192 + new_h = (struct pointer_header *)p; 1.1193 + new_h->arena = arena; 1.1194 + new_h->size = newSize; 1.1195 + rv = (void *)((char *)new_h + sizeof(struct pointer_header)); 1.1196 + if (rv != pointer) { 1.1197 + (void)nsslibc_memcpy(rv, pointer, h->size); 1.1198 + (void)nsslibc_memset(pointer, 0, h->size); 1.1199 + } 1.1200 + (void)nsslibc_memset(&((char *)rv)[ h->size ], 0, (newSize - h->size)); 1.1201 + h->arena = (NSSArena *)NULL; 1.1202 + h->size = 0; 1.1203 + PR_Unlock(arena->lock); 1.1204 + return rv; 1.1205 + } 1.1206 + /*NOTREACHED*/ 1.1207 +} 1.1208 + 1.1209 +PRStatus 1.1210 +nssArena_Shutdown(void) 1.1211 +{ 1.1212 + PRStatus rv = PR_SUCCESS; 1.1213 +#ifdef DEBUG 1.1214 + rv = nssPointerTracker_finalize(&arena_pointer_tracker); 1.1215 +#endif 1.1216 + return rv; 1.1217 +}