diff options
Diffstat (limited to 'security/nss/lib/base/arena.c')
-rw-r--r-- | security/nss/lib/base/arena.c | 1143 |
1 files changed, 1143 insertions, 0 deletions
diff --git a/security/nss/lib/base/arena.c b/security/nss/lib/base/arena.c new file mode 100644 index 0000000000..b8e64643dd --- /dev/null +++ b/security/nss/lib/base/arena.c @@ -0,0 +1,1143 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * arena.c + * + * This contains the implementation of NSS's thread-safe arenas. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +#ifdef ARENA_THREADMARK +#include "prthread.h" +#endif /* ARENA_THREADMARK */ + +#include "prlock.h" +#include "plarena.h" + +#include <string.h> + +/* + * NSSArena + * + * This is based on NSPR's arena code, but it is threadsafe. + * + * The public methods relating to this type are: + * + * NSSArena_Create -- constructor + * NSSArena_Destroy + * NSS_ZAlloc + * NSS_ZRealloc + * NSS_ZFreeIf + * + * The nonpublic methods relating to this type are: + * + * nssArena_Create -- constructor + * nssArena_Destroy + * nssArena_Mark + * nssArena_Release + * nssArena_Unmark + * + * nss_ZAlloc + * nss_ZFreeIf + * nss_ZRealloc + * + * In debug builds, the following calls are available: + * + * nssArena_verifyPointer + * nssArena_registerDestructor + * nssArena_deregisterDestructor + */ + +struct NSSArenaStr { + PLArenaPool pool; + PRLock *lock; +#ifdef ARENA_THREADMARK + PRThread *marking_thread; + nssArenaMark *first_mark; + nssArenaMark *last_mark; +#endif /* ARENA_THREADMARK */ +#ifdef ARENA_DESTRUCTOR_LIST + struct arena_destructor_node *first_destructor; + struct arena_destructor_node *last_destructor; +#endif /* ARENA_DESTRUCTOR_LIST */ +}; + +/* + * nssArenaMark + * + * This type is used to mark the current state of an NSSArena. + */ + +struct nssArenaMarkStr { + PRUint32 magic; + void *mark; +#ifdef ARENA_THREADMARK + nssArenaMark *next; +#endif /* ARENA_THREADMARK */ +#ifdef ARENA_DESTRUCTOR_LIST + struct arena_destructor_node *next_destructor; + struct arena_destructor_node *prev_destructor; +#endif /* ARENA_DESTRUCTOR_LIST */ +}; + +#define MARK_MAGIC 0x4d41524b /* "MARK" how original */ + +/* + * But first, the pointer-tracking code + */ +#ifdef DEBUG +extern const NSSError NSS_ERROR_INTERNAL_ERROR; + +static nssPointerTracker arena_pointer_tracker; + +static PRStatus +arena_add_pointer(const NSSArena *arena) +{ + PRStatus rv; + + rv = nssPointerTracker_initialize(&arena_pointer_tracker); + if (PR_SUCCESS != rv) { + return rv; + } + + rv = nssPointerTracker_add(&arena_pointer_tracker, arena); + if (PR_SUCCESS != rv) { + NSSError e = NSS_GetError(); + if (NSS_ERROR_NO_MEMORY != e) { + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + } + + return rv; + } + + return PR_SUCCESS; +} + +static PRStatus +arena_remove_pointer(const NSSArena *arena) +{ + PRStatus rv; + + rv = nssPointerTracker_remove(&arena_pointer_tracker, arena); + if (PR_SUCCESS != rv) { + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + } + + return rv; +} + +/* + * nssArena_verifyPointer + * + * This method is only present in debug builds. + * + * If the specified pointer is a valid pointer to an NSSArena object, + * this routine will return PR_SUCCESS. Otherwise, it will put an + * error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * + * Return value: + * PR_SUCCESS if the pointer is valid + * PR_FAILURE if it isn't + */ + +NSS_IMPLEMENT PRStatus +nssArena_verifyPointer(const NSSArena *arena) +{ + PRStatus rv; + + rv = nssPointerTracker_initialize(&arena_pointer_tracker); + if (PR_SUCCESS != rv) { + /* + * This is a little disingenious. We have to initialize the + * tracker, because someone could "legitimately" try to verify + * an arena pointer before one is ever created. And this step + * might fail, due to lack of memory. But the only way that + * this step can fail is if it's doing the call_once stuff, + * (later calls just no-op). And if it didn't no-op, there + * aren't any valid arenas.. so the argument certainly isn't one. + */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return PR_FAILURE; + } + + rv = nssPointerTracker_verify(&arena_pointer_tracker, arena); + if (PR_SUCCESS != rv) { + nss_SetError(NSS_ERROR_INVALID_ARENA); + return PR_FAILURE; + } + + return PR_SUCCESS; +} +#endif /* DEBUG */ + +#ifdef ARENA_DESTRUCTOR_LIST + +struct arena_destructor_node { + struct arena_destructor_node *next; + struct arena_destructor_node *prev; + void (*destructor)(void *argument); + void *arg; +}; + +/* + * nssArena_registerDestructor + * + * This routine stores a pointer to a callback and an arbitrary + * pointer-sized argument in the arena, at the current point in + * the mark stack. If the arena is destroyed, or an "earlier" + * mark is released, then this destructor will be called at that + * time. Note that the destructor will be called with the arena + * locked, which means the destructor may free memory in that + * arena, but it may not allocate or cause to be allocated any + * memory. This callback facility was included to support our + * debug-version pointer-tracker feature; overuse runs counter to + * the the original intent of arenas. This routine returns a + * PRStatus value; if successful, it will return PR_SUCCESS. If + * unsuccessful, it will set an error on the error stack and + * return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssArena_registerDestructor(NSSArena *arena, void (*destructor)(void *argument), + void *arg) +{ + struct arena_destructor_node *it; + +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return PR_FAILURE; + } +#endif /* NSSDEBUG */ + + it = nss_ZNEW(arena, struct arena_destructor_node); + if ((struct arena_destructor_node *)NULL == it) { + return PR_FAILURE; + } + + it->prev = arena->last_destructor; + arena->last_destructor->next = it; + arena->last_destructor = it; + it->destructor = destructor; + it->arg = arg; + + if ((nssArenaMark *)NULL != arena->last_mark) { + arena->last_mark->prev_destructor = it->prev; + arena->last_mark->next_destructor = it->next; + } + + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +nssArena_deregisterDestructor(NSSArena *arena, + void (*destructor)(void *argument), void *arg) +{ + struct arena_destructor_node *it; + +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return PR_FAILURE; + } +#endif /* NSSDEBUG */ + + for (it = arena->first_destructor; it; it = it->next) { + if ((it->destructor == destructor) && (it->arg == arg)) { + break; + } + } + + if ((struct arena_destructor_node *)NULL == it) { + nss_SetError(NSS_ERROR_NOT_FOUND); + return PR_FAILURE; + } + + if (it == arena->first_destructor) { + arena->first_destructor = it->next; + } + + if (it == arena->last_destructor) { + arena->last_destructor = it->prev; + } + + if ((struct arena_destructor_node *)NULL != it->prev) { + it->prev->next = it->next; + } + + if ((struct arena_destructor_node *)NULL != it->next) { + it->next->prev = it->prev; + } + + { + nssArenaMark *m; + for (m = arena->first_mark; m; m = m->next) { + if (m->next_destructor == it) { + m->next_destructor = it->next; + } + if (m->prev_destructor == it) { + m->prev_destructor = it->prev; + } + } + } + + nss_ZFreeIf(it); + return PR_SUCCESS; +} + +static void +nss_arena_call_destructor_chain(struct arena_destructor_node *it) +{ + for (; it; it = it->next) { + (*(it->destructor))(it->arg); + } +} + +#endif /* ARENA_DESTRUCTOR_LIST */ + +/* + * NSSArena_Create + * + * This routine creates a new memory arena. This routine may return + * NULL upon error, in which case it will have created an error stack. + * + * The top-level error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSArena upon success + */ + +NSS_IMPLEMENT NSSArena * +NSSArena_Create(void) +{ + nss_ClearErrorStack(); + return nssArena_Create(); +} + +/* + * nssArena_Create + * + * This routine creates a new memory arena. This routine may return + * NULL upon error, in which case it will have set an error on the + * error stack. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to an NSSArena upon success + */ + +NSS_IMPLEMENT NSSArena * +nssArena_Create(void) +{ + NSSArena *rv = (NSSArena *)NULL; + + rv = nss_ZNEW((NSSArena *)NULL, NSSArena); + if ((NSSArena *)NULL == rv) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSArena *)NULL; + } + + rv->lock = PR_NewLock(); + if ((PRLock *)NULL == rv->lock) { + (void)nss_ZFreeIf(rv); + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSArena *)NULL; + } + + /* + * Arena sizes. The current security code has 229 occurrences of + * PORT_NewArena. The default chunksizes specified break down as + * + * Size Mult. Specified as + * 512 1 512 + * 1024 7 1024 + * 2048 5 2048 + * 2048 5 CRMF_DEFAULT_ARENA_SIZE + * 2048 190 DER_DEFAULT_CHUNKSIZE + * 2048 20 SEC_ASN1_DEFAULT_ARENA_SIZE + * 4096 1 4096 + * + * Obviously this "default chunksize" flexibility isn't very + * useful to us, so I'll just pick 2048. + */ + + PL_InitArenaPool(&rv->pool, "NSS", 2048, sizeof(double)); + +#ifdef DEBUG + { + PRStatus st; + st = arena_add_pointer(rv); + if (PR_SUCCESS != st) { + PL_FinishArenaPool(&rv->pool); + PR_DestroyLock(rv->lock); + (void)nss_ZFreeIf(rv); + return (NSSArena *)NULL; + } + } +#endif /* DEBUG */ + + return rv; +} + +/* + * NSSArena_Destroy + * + * This routine will destroy the specified arena, freeing all memory + * allocated from it. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * create an error stack and return PR_FAILURE. + * + * The top-level error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * + * Return value: + * PR_SUCCESS upon success + * PR_FAILURE upon failure + */ + +NSS_IMPLEMENT PRStatus +NSSArena_Destroy(NSSArena *arena) +{ + nss_ClearErrorStack(); + +#ifdef DEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return PR_FAILURE; + } +#endif /* DEBUG */ + + return nssArena_Destroy(arena); +} + +/* + * nssArena_Destroy + * + * This routine will destroy the specified arena, freeing all memory + * allocated from it. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. If unsuccessful, it will + * set an error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssArena_Destroy(NSSArena *arena) +{ + PRLock *lock; + +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return PR_FAILURE; + } +#endif /* NSSDEBUG */ + + if ((PRLock *)NULL == arena->lock) { + /* Just got destroyed */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return PR_FAILURE; + } + PR_Lock(arena->lock); + +#ifdef DEBUG + if (PR_SUCCESS != arena_remove_pointer(arena)) { + PR_Unlock(arena->lock); + return PR_FAILURE; + } +#endif /* DEBUG */ + +#ifdef ARENA_DESTRUCTOR_LIST + /* Note that the arena is locked at this time */ + nss_arena_call_destructor_chain(arena->first_destructor); +#endif /* ARENA_DESTRUCTOR_LIST */ + + PL_FinishArenaPool(&arena->pool); + lock = arena->lock; + arena->lock = (PRLock *)NULL; + PR_Unlock(lock); + PR_DestroyLock(lock); + (void)nss_ZFreeIf(arena); + return PR_SUCCESS; +} + +static void *nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size); + +/* + * nssArena_Mark + * + * This routine "marks" the current state of an arena. Space + * allocated after the arena has been marked can be freed by + * releasing the arena back to the mark with nssArena_Release, + * or committed by calling nssArena_Unmark. When successful, + * this routine returns a valid nssArenaMark pointer. This + * routine may return NULL upon error, in which case it will + * have set an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon failure + * An nssArenaMark pointer upon success + */ + +NSS_IMPLEMENT nssArenaMark * +nssArena_Mark(NSSArena *arena) +{ + nssArenaMark *rv; + void *p; + +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return (nssArenaMark *)NULL; + } +#endif /* NSSDEBUG */ + + if ((PRLock *)NULL == arena->lock) { + /* Just got destroyed */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return (nssArenaMark *)NULL; + } + PR_Lock(arena->lock); + +#ifdef ARENA_THREADMARK + if ((PRThread *)NULL == arena->marking_thread) { + /* Unmarked. Store our thread ID */ + arena->marking_thread = PR_GetCurrentThread(); + /* This call never fails. */ + } else { + /* Marked. Verify it's the current thread */ + if (PR_GetCurrentThread() != arena->marking_thread) { + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); + return (nssArenaMark *)NULL; + } + } +#endif /* ARENA_THREADMARK */ + + p = PL_ARENA_MARK(&arena->pool); + /* No error possible */ + + /* Do this after the mark */ + rv = (nssArenaMark *)nss_zalloc_arena_locked(arena, sizeof(nssArenaMark)); + if ((nssArenaMark *)NULL == rv) { + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_NO_MEMORY); + return (nssArenaMark *)NULL; + } + +#ifdef ARENA_THREADMARK + if ((nssArenaMark *)NULL == arena->first_mark) { + arena->first_mark = rv; + arena->last_mark = rv; + } else { + arena->last_mark->next = rv; + arena->last_mark = rv; + } +#endif /* ARENA_THREADMARK */ + + rv->mark = p; + rv->magic = MARK_MAGIC; + +#ifdef ARENA_DESTRUCTOR_LIST + rv->prev_destructor = arena->last_destructor; +#endif /* ARENA_DESTRUCTOR_LIST */ + + PR_Unlock(arena->lock); + + return rv; +} + +/* + * nss_arena_unmark_release + * + * This static routine implements the routines nssArena_Release + * ans nssArena_Unmark, which are almost identical. + */ + +static PRStatus +nss_arena_unmark_release(NSSArena *arena, nssArenaMark *arenaMark, + PRBool release) +{ + void *inner_mark; + +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return PR_FAILURE; + } +#endif /* NSSDEBUG */ + + if (MARK_MAGIC != arenaMark->magic) { + nss_SetError(NSS_ERROR_INVALID_ARENA_MARK); + return PR_FAILURE; + } + + if ((PRLock *)NULL == arena->lock) { + /* Just got destroyed */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return PR_FAILURE; + } + PR_Lock(arena->lock); + +#ifdef ARENA_THREADMARK + if ((PRThread *)NULL != arena->marking_thread) { + if (PR_GetCurrentThread() != arena->marking_thread) { + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); + return PR_FAILURE; + } + } +#endif /* ARENA_THREADMARK */ + + if (MARK_MAGIC != arenaMark->magic) { + /* Just got released */ + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_INVALID_ARENA_MARK); + return PR_FAILURE; + } + + arenaMark->magic = 0; + inner_mark = arenaMark->mark; + +#ifdef ARENA_THREADMARK + { + nssArenaMark **pMark = &arena->first_mark; + nssArenaMark *rest; + nssArenaMark *last = (nssArenaMark *)NULL; + + /* Find this mark */ + while (*pMark != arenaMark) { + last = *pMark; + pMark = &(*pMark)->next; + } + + /* Remember the pointer, then zero it */ + rest = (*pMark)->next; + *pMark = (nssArenaMark *)NULL; + + arena->last_mark = last; + + /* Invalidate any later marks being implicitly released */ + for (; (nssArenaMark *)NULL != rest; rest = rest->next) { + rest->magic = 0; + } + + /* If we just got rid of the first mark, clear the thread ID */ + if ((nssArenaMark *)NULL == arena->first_mark) { + arena->marking_thread = (PRThread *)NULL; + } + } +#endif /* ARENA_THREADMARK */ + + if (release) { +#ifdef ARENA_DESTRUCTOR_LIST + if ((struct arena_destructor_node *)NULL != + arenaMark->prev_destructor) { + arenaMark->prev_destructor->next = + (struct arena_destructor_node *)NULL; + } + arena->last_destructor = arenaMark->prev_destructor; + + /* Note that the arena is locked at this time */ + nss_arena_call_destructor_chain(arenaMark->next_destructor); +#endif /* ARENA_DESTRUCTOR_LIST */ + + PL_ARENA_RELEASE(&arena->pool, inner_mark); + /* No error return */ + } + + PR_Unlock(arena->lock); + return PR_SUCCESS; +} + +/* + * nssArena_Release + * + * This routine invalidates and releases all memory allocated from + * the specified arena after the point at which the specified mark + * was obtained. This routine returns a PRStatus value; if successful, + * it will return PR_SUCCESS. If unsuccessful, it will set an error + * on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_INVALID_ARENA_MARK + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssArena_Release(NSSArena *arena, nssArenaMark *arenaMark) +{ + return nss_arena_unmark_release(arena, arenaMark, PR_TRUE); +} + +/* + * nssArena_Unmark + * + * This routine "commits" the indicated mark and any marks after + * it, making them unreleasable. Note that any earlier marks can + * still be released, and such a release will invalidate these + * later unmarked regions. If an arena is to be safely shared by + * more than one thread, all marks must be either released or + * unmarked. This routine returns a PRStatus value; if successful, + * it will return PR_SUCCESS. If unsuccessful, it will set an error + * on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_INVALID_ARENA_MARK + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssArena_Unmark(NSSArena *arena, nssArenaMark *arenaMark) +{ + return nss_arena_unmark_release(arena, arenaMark, PR_FALSE); +} + +/* + * We prefix this header to all allocated blocks. It is a multiple + * of the alignment size. Note that this usage of a header may make + * purify spew bogus warnings about "potentially leaked blocks" of + * memory; if that gets too annoying we can add in a pointer to the + * header in the header itself. There's not a lot of safety here; + * maybe we should add a magic value? + */ +struct pointer_header { + NSSArena *arena; + PRUint32 size; +}; + +static void * +nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size) +{ + void *p; + void *rv; + struct pointer_header *h; + PRUint32 my_size = size + sizeof(struct pointer_header); + PL_ARENA_ALLOCATE(p, &arena->pool, my_size); + if ((void *)NULL == p) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + /* + * Do this before we unlock. This way if the user is using + * an arena in one thread while destroying it in another, he'll + * fault/FMR in his code, not ours. + */ + h = (struct pointer_header *)p; + h->arena = arena; + h->size = size; + rv = (void *)((char *)h + sizeof(struct pointer_header)); + (void)nsslibc_memset(rv, 0, size); + return rv; +} + +/* + * NSS_ZAlloc + * + * This routine allocates and zeroes a section of memory of the + * size, and returns to the caller a pointer to that memory. If + * the optional arena argument is non-null, the memory will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in + * which case it will have set an error upon the error stack. The + * value specified for size may be zero; in which case a valid + * zero-length block of memory will be allocated. This block may + * be expanded by calling NSS_ZRealloc. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +NSS_IMPLEMENT void * +NSS_ZAlloc(NSSArena *arenaOpt, PRUint32 size) +{ + return nss_ZAlloc(arenaOpt, size); +} + +/* + * nss_ZAlloc + * + * This routine allocates and zeroes a section of memory of the + * size, and returns to the caller a pointer to that memory. If + * the optional arena argument is non-null, the memory will be + * obtained from that arena; otherwise, the memory will be obtained + * from the heap. This routine may return NULL upon error, in + * which case it will have set an error upon the error stack. The + * value specified for size may be zero; in which case a valid + * zero-length block of memory will be allocated. This block may + * be expanded by calling nss_ZRealloc. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +NSS_IMPLEMENT void * +nss_ZAlloc(NSSArena *arenaOpt, PRUint32 size) +{ + struct pointer_header *h; + PRUint32 my_size = size + sizeof(struct pointer_header); + + if (my_size < sizeof(struct pointer_header)) { + /* Wrapped */ + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + + if ((NSSArena *)NULL == arenaOpt) { + /* Heap allocation, no locking required. */ + h = (struct pointer_header *)PR_Calloc(1, my_size); + if ((struct pointer_header *)NULL == h) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + + h->arena = (NSSArena *)NULL; + h->size = size; + /* We used calloc: it's already zeroed */ + + return (void *)((char *)h + sizeof(struct pointer_header)); + } else { + void *rv; +/* Arena allocation */ +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + return (void *)NULL; + } +#endif /* NSSDEBUG */ + + if ((PRLock *)NULL == arenaOpt->lock) { + /* Just got destroyed */ + nss_SetError(NSS_ERROR_INVALID_ARENA); + return (void *)NULL; + } + PR_Lock(arenaOpt->lock); + +#ifdef ARENA_THREADMARK + if ((PRThread *)NULL != arenaOpt->marking_thread) { + if (PR_GetCurrentThread() != arenaOpt->marking_thread) { + nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); + PR_Unlock(arenaOpt->lock); + return (void *)NULL; + } + } +#endif /* ARENA_THREADMARK */ + + rv = nss_zalloc_arena_locked(arenaOpt, size); + + PR_Unlock(arenaOpt->lock); + return rv; + } + /*NOTREACHED*/ +} + +/* + * NSS_ZFreeIf + * + * If the specified pointer is non-null, then the region of memory + * to which it points -- which must have been allocated with + * NSS_ZAlloc -- will be zeroed and released. This routine + * returns a PRStatus value; if successful, it will return PR_SUCCESS. + * If unsuccessful, it will set an error on the error stack and return + * PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ +NSS_IMPLEMENT PRStatus +NSS_ZFreeIf(void *pointer) +{ + return nss_ZFreeIf(pointer); +} + +/* + * nss_ZFreeIf + * + * If the specified pointer is non-null, then the region of memory + * to which it points -- which must have been allocated with + * nss_ZAlloc -- will be zeroed and released. This routine + * returns a PRStatus value; if successful, it will return PR_SUCCESS. + * If unsuccessful, it will set an error on the error stack and return + * PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nss_ZFreeIf(void *pointer) +{ + struct pointer_header *h; + + if ((void *)NULL == pointer) { + return PR_SUCCESS; + } + + h = (struct pointer_header *)((char *)pointer - + sizeof(struct pointer_header)); + + /* Check any magic here */ + + if ((NSSArena *)NULL == h->arena) { + /* Heap */ + (void)nsslibc_memset(pointer, 0, h->size); + PR_Free(h); + return PR_SUCCESS; + } else { +/* Arena */ +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(h->arena)) { + return PR_FAILURE; + } +#endif /* NSSDEBUG */ + + if ((PRLock *)NULL == h->arena->lock) { + /* Just got destroyed.. so this pointer is invalid */ + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FAILURE; + } + PR_Lock(h->arena->lock); + + (void)nsslibc_memset(pointer, 0, h->size); + + /* No way to "free" it within an NSPR arena. */ + + PR_Unlock(h->arena->lock); + return PR_SUCCESS; + } + /*NOTREACHED*/ +} + +/* + * NSS_ZRealloc + * + * This routine reallocates a block of memory obtained by calling + * nss_ZAlloc or nss_ZRealloc. The portion of memory + * between the new and old sizes -- which is either being newly + * obtained or released -- is in either case zeroed. This routine + * may return NULL upon failure, in which case it will have placed + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the replacement segment of memory + */ + +NSS_EXTERN void * +NSS_ZRealloc(void *pointer, PRUint32 newSize) +{ + return nss_ZRealloc(pointer, newSize); +} + +/* + * nss_ZRealloc + * + * This routine reallocates a block of memory obtained by calling + * nss_ZAlloc or nss_ZRealloc. The portion of memory + * between the new and old sizes -- which is either being newly + * obtained or released -- is in either case zeroed. This routine + * may return NULL upon failure, in which case it will have placed + * an error on the error stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * + * Return value: + * NULL upon error + * A pointer to the replacement segment of memory + */ + +NSS_EXTERN void * +nss_ZRealloc(void *pointer, PRUint32 newSize) +{ + NSSArena *arena; + struct pointer_header *h, *new_h; + PRUint32 my_newSize = newSize + sizeof(struct pointer_header); + void *rv; + + if (my_newSize < sizeof(struct pointer_header)) { + /* Wrapped */ + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + + if ((void *)NULL == pointer) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (void *)NULL; + } + + h = (struct pointer_header *)((char *)pointer - + sizeof(struct pointer_header)); + + /* Check any magic here */ + + if (newSize == h->size) { + /* saves thrashing */ + return pointer; + } + + arena = h->arena; + if (!arena) { + /* Heap */ + new_h = (struct pointer_header *)PR_Calloc(1, my_newSize); + if ((struct pointer_header *)NULL == new_h) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + + new_h->arena = (NSSArena *)NULL; + new_h->size = newSize; + rv = (void *)((char *)new_h + sizeof(struct pointer_header)); + + if (newSize > h->size) { + (void)nsslibc_memcpy(rv, pointer, h->size); + (void)nsslibc_memset(&((char *)rv)[h->size], 0, + (newSize - h->size)); + } else { + (void)nsslibc_memcpy(rv, pointer, newSize); + } + + (void)nsslibc_memset(pointer, 0, h->size); + h->size = 0; + PR_Free(h); + + return rv; + } else { + void *p; +/* Arena */ +#ifdef NSSDEBUG + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return (void *)NULL; + } +#endif /* NSSDEBUG */ + + if (!arena->lock) { + /* Just got destroyed.. so this pointer is invalid */ + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (void *)NULL; + } + PR_Lock(arena->lock); + +#ifdef ARENA_THREADMARK + if (arena->marking_thread) { + if (PR_GetCurrentThread() != arena->marking_thread) { + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD); + return (void *)NULL; + } + } +#endif /* ARENA_THREADMARK */ + + if (newSize < h->size) { + /* + * We have no general way of returning memory to the arena + * (mark/release doesn't work because things may have been + * allocated after this object), so the memory is gone + * anyway. We might as well just return the same pointer to + * the user, saying "yeah, uh-hunh, you can only use less of + * it now." We'll zero the leftover part, of course. And + * in fact we might as well *not* adjust h->size-- this way, + * if the user reallocs back up to something not greater than + * the original size, then voila, there's the memory! This + * way a thrash big/small/big/small doesn't burn up the arena. + */ + char *extra = &((char *)pointer)[newSize]; + (void)nsslibc_memset(extra, 0, (h->size - newSize)); + PR_Unlock(arena->lock); + return pointer; + } + + PL_ARENA_ALLOCATE(p, &arena->pool, my_newSize); + if ((void *)NULL == p) { + PR_Unlock(arena->lock); + nss_SetError(NSS_ERROR_NO_MEMORY); + return (void *)NULL; + } + + new_h = (struct pointer_header *)p; + new_h->arena = arena; + new_h->size = newSize; + rv = (void *)((char *)new_h + sizeof(struct pointer_header)); + if (rv != pointer) { + (void)nsslibc_memcpy(rv, pointer, h->size); + (void)nsslibc_memset(pointer, 0, h->size); + } + (void)nsslibc_memset(&((char *)rv)[h->size], 0, (newSize - h->size)); + h->arena = (NSSArena *)NULL; + h->size = 0; + PR_Unlock(arena->lock); + return rv; + } + /*NOTREACHED*/ +} + +PRStatus +nssArena_Shutdown(void) +{ + PRStatus rv = PR_SUCCESS; +#ifdef DEBUG + rv = nssPointerTracker_finalize(&arena_pointer_tracker); +#endif + return rv; +} |