/* 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 /* * 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; }