diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/lib/base | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/base/Makefile | 14 | ||||
-rw-r--r-- | security/nss/lib/base/arena.c | 1143 | ||||
-rw-r--r-- | security/nss/lib/base/base.gyp | 32 | ||||
-rw-r--r-- | security/nss/lib/base/base.h | 1106 | ||||
-rw-r--r-- | security/nss/lib/base/baset.h | 124 | ||||
-rw-r--r-- | security/nss/lib/base/error.c | 301 | ||||
-rw-r--r-- | security/nss/lib/base/errorval.c | 65 | ||||
-rw-r--r-- | security/nss/lib/base/exports.gyp | 33 | ||||
-rw-r--r-- | security/nss/lib/base/hash.c | 314 | ||||
-rw-r--r-- | security/nss/lib/base/hashops.c | 64 | ||||
-rw-r--r-- | security/nss/lib/base/item.c | 186 | ||||
-rw-r--r-- | security/nss/lib/base/libc.c | 143 | ||||
-rw-r--r-- | security/nss/lib/base/list.c | 405 | ||||
-rw-r--r-- | security/nss/lib/base/manifest.mn | 39 | ||||
-rw-r--r-- | security/nss/lib/base/nssbase.h | 233 | ||||
-rw-r--r-- | security/nss/lib/base/nssbaset.h | 118 | ||||
-rw-r--r-- | security/nss/lib/base/tracker.c | 378 | ||||
-rw-r--r-- | security/nss/lib/base/utf8.c | 702 |
18 files changed, 5400 insertions, 0 deletions
diff --git a/security/nss/lib/base/Makefile b/security/nss/lib/base/Makefile new file mode 100644 index 0000000000..3f49eaa45d --- /dev/null +++ b/security/nss/lib/base/Makefile @@ -0,0 +1,14 @@ +# +# 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/. + +include manifest.mn + +include $(CORE_DEPTH)/coreconf/config.mk + +ifdef BUILD_IDG +DEFINES += -DNSSDEBUG +endif + +include $(CORE_DEPTH)/coreconf/rules.mk 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; +} diff --git a/security/nss/lib/base/base.gyp b/security/nss/lib/base/base.gyp new file mode 100644 index 0000000000..42318c897b --- /dev/null +++ b/security/nss/lib/base/base.gyp @@ -0,0 +1,32 @@ +# 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/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'nssb', + 'type': 'static_library', + 'sources': [ + 'arena.c', + 'error.c', + 'errorval.c', + 'hash.c', + 'hashops.c', + 'item.c', + 'libc.c', + 'list.c', + 'tracker.c', + 'utf8.c' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports' + ] + } + ], + 'variables': { + 'module': 'nss' + } +}
\ No newline at end of file diff --git a/security/nss/lib/base/base.h b/security/nss/lib/base/base.h new file mode 100644 index 0000000000..41ab86f6b0 --- /dev/null +++ b/security/nss/lib/base/base.h @@ -0,0 +1,1106 @@ +/* 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/. */ + +#ifndef BASE_H +#define BASE_H + +/* + * base.h + * + * This header file contains basic prototypes and preprocessor + * definitions used throughout nss but not available publicly. + */ + +#ifndef BASET_H +#include "baset.h" +#endif /* BASET_H */ + +#ifndef NSSBASE_H +#include "nssbase.h" +#endif /* NSSBASE_H */ + +#include "plhash.h" + +PR_BEGIN_EXTERN_C + +/* + * NSSArena + * + * 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 + * + * Additionally, there are some preprocessor macros: + * + * nss_ZNEW + * nss_ZNEWARRAY + * + * In debug builds, the following calls are available: + * + * nssArena_verifyPointer + * nssArena_registerDestructor + * nssArena_deregisterDestructor + * + * The following preprocessor macro is also always available: + * + * nssArena_VERIFYPOINTER + * + * A constant PLHashAllocOps structure is available for users + * of the NSPL PLHashTable routines. + * + * nssArenaHashAllocOps + */ + +/* + * 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 + */ + +/* + * XXX fgmr + * Arenas can be named upon creation; this is mostly of use when + * debugging. Should we expose that here, allowing an optional + * "const char *name" argument? Should the public version of this + * call (NSSArena_Create) have it too? + */ + +NSS_EXTERN NSSArena *nssArena_Create(void); + +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * 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_EXTERN PRStatus nssArena_Destroy(NSSArena *arena); + +extern const NSSError NSS_ERROR_INVALID_ARENA; + +/* + * 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_EXTERN nssArenaMark *nssArena_Mark(NSSArena *arena); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_NO_MEMORY; +extern const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD; + +/* + * 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_EXTERN PRStatus nssArena_Release(NSSArena *arena, nssArenaMark *arenaMark); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_INVALID_ARENA_MARK; + +/* + * 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_EXTERN PRStatus nssArena_Unmark(NSSArena *arena, nssArenaMark *arenaMark); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_INVALID_ARENA_MARK; +extern const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD; + +#ifdef ARENA_DESTRUCTOR_LIST + +/* + * 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_EXTERN PRStatus nssArena_registerDestructor( + NSSArena *arena, void (*destructor)(void *argument), void *arg); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * nssArena_deregisterDestructor + * + * This routine will remove the first destructor in the specified + * arena which has the specified destructor and argument values. + * The destructor will not be called. 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_NOT_FOUND + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_EXTERN PRStatus nssArena_deregisterDestructor( + NSSArena *arena, void (*destructor)(void *argument), void *arg); + +extern const NSSError NSS_ERROR_INVALID_ITEM; +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_NOT_FOUND; + +#endif /* ARENA_DESTRUCTOR_LIST */ + +/* + * 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_EXTERN void *nss_ZAlloc(NSSArena *arenaOpt, PRUint32 size); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +extern const NSSError NSS_ERROR_NO_MEMORY; +extern const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD; + +/* + * 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_EXTERN PRStatus nss_ZFreeIf(void *pointer); + +extern const NSSError NSS_ERROR_INVALID_POINTER; + +/* + * 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); + +extern const NSSError NSS_ERROR_INVALID_POINTER; +extern const NSSError NSS_ERROR_NO_MEMORY; +extern const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD; + +/* + * nss_ZNEW + * + * This preprocessor macro will allocate memory for a new object + * of the specified type with nss_ZAlloc, and will cast the + * return value appropriately. 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 error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +#define nss_ZNEW(arenaOpt, type) ((type *)nss_ZAlloc((arenaOpt), sizeof(type))) + +/* + * nss_ZNEWARRAY + * + * This preprocessor macro will allocate memory for an array of + * new objects, and will cast the return value appropriately. + * 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 array size may be specified as zero. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +#define nss_ZNEWARRAY(arenaOpt, type, quantity) \ + ((type *)nss_ZAlloc((arenaOpt), sizeof(type) * (quantity))) + +/* + * nss_ZREALLOCARRAY + * + * This preprocessor macro will reallocate memory for an array of + * new objects, and will cast the return value appropriately. + * This routine may return NULL upon error, in which case it will + * have set an error upon 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 + */ +#define nss_ZREALLOCARRAY(p, type, quantity) \ + ((type *)nss_ZRealloc((p), sizeof(type) * (quantity))) + +/* + * 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 + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus nssArena_verifyPointer(const NSSArena *arena); + +extern const NSSError NSS_ERROR_INVALID_ARENA; +#endif /* DEBUG */ + +/* + * nssArena_VERIFYPOINTER + * + * This macro is always available. In debug builds it will call + * nssArena_verifyPointer; in non-debug builds, it will merely + * check that the pointer is not null. Note that in non-debug + * builds it cannot place an error on the error stack. + * + * Return value: + * PR_SUCCESS if the pointer is valid + * PR_FAILURE if it isn't + */ + +#ifdef DEBUG +#define nssArena_VERIFYPOINTER(p) nssArena_verifyPointer(p) +#else /* DEBUG */ + +#define nssArena_VERIFYPOINTER(p) \ + (((NSSArena *)NULL == (p)) ? PR_FAILURE : PR_SUCCESS) +#endif /* DEBUG */ + +/* + * Private function to be called by NSS_Shutdown to cleanup nssArena + * bookkeeping. + */ +extern PRStatus nssArena_Shutdown(void); + +/* + * nssArenaHashAllocOps + * + * This constant structure contains allocation callbacks designed for + * use with the NSPL routine PL_NewHashTable. For example: + * + * NSSArena *hashTableArena = nssArena_Create(); + * PLHashTable *t = PL_NewHashTable(n, hasher, key_compare, + * value_compare, nssArenaHashAllocOps, hashTableArena); + */ + +NSS_EXTERN_DATA PLHashAllocOps nssArenaHashAllocOps; + +/* + * The error stack + * + * The nonpublic methods relating to the error stack are: + * + * nss_SetError + * nss_ClearErrorStack + */ + +/* + * nss_SetError + * + * This routine places a new error code on the top of the calling + * thread's error stack. Calling this routine wiht an error code + * of zero will clear the error stack. + */ + +NSS_EXTERN void nss_SetError(PRUint32 error); + +/* + * nss_ClearErrorStack + * + * This routine clears the calling thread's error stack. + */ + +NSS_EXTERN void nss_ClearErrorStack(void); + +/* + * nss_DestroyErrorStack + * + * This routine frees the calling thread's error stack. + */ + +NSS_EXTERN void nss_DestroyErrorStack(void); + +/* + * NSSItem + * + * nssItem_Create + * nssItem_Duplicate + * nssItem_Equal + */ + +NSS_EXTERN NSSItem *nssItem_Create(NSSArena *arenaOpt, NSSItem *rvOpt, + PRUint32 length, const void *data); + +NSS_EXTERN void nssItem_Destroy(NSSItem *item); + +NSS_EXTERN NSSItem *nssItem_Duplicate(NSSItem *obj, NSSArena *arenaOpt, + NSSItem *rvOpt); + +NSS_EXTERN PRBool nssItem_Equal(const NSSItem *one, const NSSItem *two, + PRStatus *statusOpt); + +/* + * NSSUTF8 + * + * nssUTF8_CaseIgnoreMatch + * nssUTF8_Duplicate + * nssUTF8_Size + * nssUTF8_Length + * nssUTF8_CopyIntoFixedBuffer + */ + +/* + * nssUTF8_CaseIgnoreMatch + * + * Returns true if the two UTF8-encoded strings pointed to by the + * two specified NSSUTF8 pointers differ only in typcase. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_TRUE if the strings match, ignoring case + * PR_FALSE if they don't + * PR_FALSE upon error + */ + +NSS_EXTERN PRBool nssUTF8_CaseIgnoreMatch(const NSSUTF8 *a, const NSSUTF8 *b, + PRStatus *statusOpt); + +/* + * nssUTF8_Duplicate + * + * This routine duplicates the UTF8-encoded string pointed to by the + * specified NSSUTF8 pointer. If the optional arenaOpt argument is + * not null, the memory required will be obtained from that arena; + * otherwise, the memory required will be obtained from the heap. + * A pointer to the new string will be returned. In case of error, + * an error will be placed on the error stack and NULL will be + * returned. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + */ + +NSS_EXTERN NSSUTF8 *nssUTF8_Duplicate(const NSSUTF8 *s, NSSArena *arenaOpt); + +/* + * nssUTF8_PrintableMatch + * + * Returns true if the two Printable strings pointed to by the + * two specified NSSUTF8 pointers match when compared with the + * rules for Printable String (leading and trailing spaces are + * disregarded, extents of whitespace match irregardless of length, + * and case is not significant), then PR_TRUE will be returned. + * Otherwise, PR_FALSE will be returned. Upon failure, PR_FALSE + * will be returned. If the optional statusOpt argument is not + * NULL, then PR_SUCCESS or PR_FAILURE will be stored in that + * location. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_TRUE if the strings match, ignoring case + * PR_FALSE if they don't + * PR_FALSE upon error + */ + +NSS_EXTERN PRBool nssUTF8_PrintableMatch(const NSSUTF8 *a, const NSSUTF8 *b, + PRStatus *statusOpt); + +/* + * nssUTF8_Size + * + * This routine returns the length in bytes (including the terminating + * null) of the UTF8-encoded string pointed to by the specified + * NSSUTF8 pointer. Zero is returned on error. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_VALUE_TOO_LARGE + * + * Return value: + * nonzero size of the string + * 0 on error + */ + +NSS_EXTERN PRUint32 nssUTF8_Size(const NSSUTF8 *s, PRStatus *statusOpt); + +extern const NSSError NSS_ERROR_INVALID_POINTER; +extern const NSSError NSS_ERROR_VALUE_TOO_LARGE; + +/* + * nssUTF8_Length + * + * This routine returns the length in characters (not including the + * terminating null) of the UTF8-encoded string pointed to by the + * specified NSSUTF8 pointer. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_VALUE_TOO_LARGE + * NSS_ERROR_INVALID_STRING + * + * Return value: + * length of the string (which may be zero) + * 0 on error + */ + +NSS_EXTERN PRUint32 nssUTF8_Length(const NSSUTF8 *s, PRStatus *statusOpt); + +extern const NSSError NSS_ERROR_INVALID_POINTER; +extern const NSSError NSS_ERROR_VALUE_TOO_LARGE; +extern const NSSError NSS_ERROR_INVALID_STRING; + +/* + * nssUTF8_Create + * + * This routine creates a UTF8 string from a string in some other + * format. Some types of string may include embedded null characters, + * so for them the length parameter must be used. For string types + * that are null-terminated, the length parameter is optional; if it + * is zero, it will be ignored. If the optional arena argument is + * non-null, the memory used for the new string will be obtained from + * that arena, otherwise it will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have + * placed an error on the error stack. + * + * The error may be one of the following: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_UNSUPPORTED_TYPE + * + * Return value: + * NULL upon error + * A non-null pointer to a new UTF8 string otherwise + */ + +NSS_EXTERN NSSUTF8 *nssUTF8_Create(NSSArena *arenaOpt, nssStringType type, + const void *inputString, + PRUint32 size /* in bytes, not characters */ +); + +extern const NSSError NSS_ERROR_INVALID_POINTER; +extern const NSSError NSS_ERROR_NO_MEMORY; +extern const NSSError NSS_ERROR_UNSUPPORTED_TYPE; + +NSS_EXTERN NSSItem *nssUTF8_GetEncoding(NSSArena *arenaOpt, NSSItem *rvOpt, + nssStringType type, NSSUTF8 *string); + +/* + * nssUTF8_CopyIntoFixedBuffer + * + * This will copy a UTF8 string into a fixed-length buffer, making + * sure that the all characters are valid. Any remaining space will + * be padded with the specified ASCII character, typically either + * null or space. + * + * Blah, blah, blah. + */ + +extern const NSSError NSS_ERROR_INVALID_POINTER; +extern const NSSError NSS_ERROR_INVALID_ARGUMENT; + +NSS_EXTERN PRStatus nssUTF8_CopyIntoFixedBuffer(NSSUTF8 *string, char *buffer, + PRUint32 bufferSize, char pad); + +/* + * nssUTF8_Equal + * + */ + +NSS_EXTERN PRBool nssUTF8_Equal(const NSSUTF8 *a, const NSSUTF8 *b, + PRStatus *statusOpt); + +/* + * nssList + * + * The goal is to provide a simple, optionally threadsafe, linked list + * class. Since NSS did not seem to use the circularity of PRCList + * much before, this provides a list that appears to be a linear, + * NULL-terminated list. + */ + +/* + * nssList_Create + * + * If threadsafe is true, the list will be locked during modifications + * and traversals. + */ +NSS_EXTERN nssList *nssList_Create(NSSArena *arenaOpt, PRBool threadSafe); + +/* + * nssList_Destroy + */ +NSS_EXTERN PRStatus nssList_Destroy(nssList *list); + +NSS_EXTERN void nssList_Clear(nssList *list, + nssListElementDestructorFunc destructor); + +/* + * nssList_SetCompareFunction + * + * By default, two list elements will be compared by comparing their + * data pointers. By setting this function, the user can control + * how elements are compared. + */ +NSS_EXTERN void nssList_SetCompareFunction(nssList *list, + nssListCompareFunc compareFunc); + +/* + * nssList_SetSortFunction + * + * Sort function to use for an ordered list. + */ +NSS_EXTERN void nssList_SetSortFunction(nssList *list, + nssListSortFunc sortFunc); + +/* + * nssList_Add + */ +NSS_EXTERN PRStatus nssList_Add(nssList *list, void *data); + +/* + * nssList_AddUnique + * + * This will use the compare function to see if the element is already + * in the list. + */ +NSS_EXTERN PRStatus nssList_AddUnique(nssList *list, void *data); + +/* + * nssList_Remove + * + * Uses the compare function to locate the element and remove it. + */ +NSS_EXTERN PRStatus nssList_Remove(nssList *list, void *data); + +/* + * nssList_Get + * + * Uses the compare function to locate an element. Also serves as + * nssList_Exists. + */ +NSS_EXTERN void *nssList_Get(nssList *list, void *data); + +/* + * nssList_Count + */ +NSS_EXTERN PRUint32 nssList_Count(nssList *list); + +/* + * nssList_GetArray + * + * Fill rvArray, up to maxElements, with elements in the list. The + * array is NULL-terminated, so its allocated size must be maxElements + 1. + */ +NSS_EXTERN PRStatus nssList_GetArray(nssList *list, void **rvArray, + PRUint32 maxElements); + +/* + * nssList_CreateIterator + * + * Create an iterator for list traversal. + */ +NSS_EXTERN nssListIterator *nssList_CreateIterator(nssList *list); + +NSS_EXTERN nssList *nssList_Clone(nssList *list); + +/* + * nssListIterator_Destroy + */ +NSS_EXTERN void nssListIterator_Destroy(nssListIterator *iter); + +/* + * nssListIterator_Start + * + * Begin a list iteration. After this call, if the list is threadSafe, + * the list is *locked*. + */ +NSS_EXTERN void *nssListIterator_Start(nssListIterator *iter); + +/* + * nssListIterator_Next + * + * Continue a list iteration. + */ +NSS_EXTERN void *nssListIterator_Next(nssListIterator *iter); + +/* + * nssListIterator_Finish + * + * Complete a list iteration. This *must* be called in order for the + * lock to be released. + */ +NSS_EXTERN PRStatus nssListIterator_Finish(nssListIterator *iter); + +/* + * nssHash + * + * nssHash_Create + * nssHash_Destroy + * nssHash_Add + * nssHash_Remove + * nssHash_Count + * nssHash_Exists + * nssHash_Lookup + * nssHash_Iterate + */ + +/* + * nssHash_Create + * + */ + +NSS_EXTERN nssHash *nssHash_Create(NSSArena *arenaOpt, PRUint32 numBuckets, + PLHashFunction keyHash, + PLHashComparator keyCompare, + PLHashComparator valueCompare); + +NSS_EXTERN nssHash *nssHash_CreatePointer(NSSArena *arenaOpt, + PRUint32 numBuckets); + +NSS_EXTERN nssHash *nssHash_CreateString(NSSArena *arenaOpt, + PRUint32 numBuckets); + +NSS_EXTERN nssHash *nssHash_CreateItem(NSSArena *arenaOpt, PRUint32 numBuckets); + +/* + * nssHash_Destroy + * + */ +NSS_EXTERN void nssHash_Destroy(nssHash *hash); + +/* + * nssHash_Add + * + */ + +extern const NSSError NSS_ERROR_HASH_COLLISION; + +NSS_EXTERN PRStatus nssHash_Add(nssHash *hash, const void *key, + const void *value); + +/* + * nssHash_Remove + * + */ +NSS_EXTERN void nssHash_Remove(nssHash *hash, const void *it); + +/* + * nssHash_Count + * + */ +NSS_EXTERN PRUint32 nssHash_Count(nssHash *hash); + +/* + * nssHash_Exists + * + */ +NSS_EXTERN PRBool nssHash_Exists(nssHash *hash, const void *it); + +/* + * nssHash_Lookup + * + */ +NSS_EXTERN void *nssHash_Lookup(nssHash *hash, const void *it); + +/* + * nssHash_Iterate + * + */ +NSS_EXTERN void nssHash_Iterate(nssHash *hash, nssHashIterator fcn, + void *closure); + +/* + * nssPointerTracker + * + * This type and these methods are only present in debug builds. + * + * The nonpublic methods relating to this type are: + * + * nssPointerTracker_initialize + * nssPointerTracker_finalize + * nssPointerTracker_add + * nssPointerTracker_remove + * nssPointerTracker_verify + */ + +/* + * nssPointerTracker_initialize + * + * This method is only present in debug builds. + * + * This routine initializes an nssPointerTracker object. Note that + * the object must have been declared *static* to guarantee that it + * is in a zeroed state initially. This routine is idempotent, and + * may even be safely called by multiple threads simultaneously with + * the same argument. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. On failure it will set an + * error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus nssPointerTracker_initialize(nssPointerTracker *tracker); + +extern const NSSError NSS_ERROR_NO_MEMORY; +#endif /* DEBUG */ + +/* + * nssPointerTracker_finalize + * + * This method is only present in debug builds. + * + * This routine returns the nssPointerTracker object to the pre- + * initialized state, releasing all resources used by the object. + * It will *NOT* destroy the objects being tracked by the pointer + * (should any remain), and therefore cannot be used to "sweep up" + * remaining objects. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCES. On failure it will set an + * error on the error stack and return PR_FAILURE. If any objects + * remain in the tracker when it is finalized, that will be treated + * as an error. + * + * The error may be one of the following values: + * NSS_ERROR_TRACKER_NOT_EMPTY + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus nssPointerTracker_finalize(nssPointerTracker *tracker); + +extern const NSSError NSS_ERROR_TRACKER_NOT_EMPTY; +#endif /* DEBUG */ + +/* + * nssPointerTracker_add + * + * This method is only present in debug builds. + * + * This routine adds the specified pointer to the nssPointerTracker + * object. It should be called in constructor objects to register + * new valid objects. The nssPointerTracker is threadsafe, but this + * call is not idempotent. This routine returns a PRStatus value; + * if successful it will return PR_SUCCESS. On failure it will set + * an error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_TRACKER_NOT_INITIALIZED + * NSS_ERROR_DUPLICATE_POINTER + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus nssPointerTracker_add(nssPointerTracker *tracker, + const void *pointer); + +extern const NSSError NSS_ERROR_NO_MEMORY; +extern const NSSError NSS_ERROR_TRACKER_NOT_INITIALIZED; +extern const NSSError NSS_ERROR_DUPLICATE_POINTER; +#endif /* DEBUG */ + +/* + * nssPointerTracker_remove + * + * This method is only present in debug builds. + * + * This routine removes the specified pointer from the + * nssPointerTracker object. It does not call any destructor for the + * object; rather, this should be called from the object's destructor. + * The nssPointerTracker is threadsafe, but this call is not + * idempotent. This routine returns a PRStatus value; if successful + * it will return PR_SUCCESS. On failure it will set an error on the + * error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_TRACKER_NOT_INITIALIZED + * NSS_ERROR_POINTER_NOT_REGISTERED + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus nssPointerTracker_remove(nssPointerTracker *tracker, + const void *pointer); + +extern const NSSError NSS_ERROR_TRACKER_NOT_INITIALIZED; +extern const NSSError NSS_ERROR_POINTER_NOT_REGISTERED; +#endif /* DEBUG */ + +/* + * nssPointerTracker_verify + * + * This method is only present in debug builds. + * + * This routine verifies that the specified pointer has been registered + * with the nssPointerTracker object. The nssPointerTracker object is + * threadsafe, and this call may be safely called from multiple threads + * simultaneously with the same arguments. This routine returns a + * PRStatus value; if the pointer is registered this will return + * PR_SUCCESS. Otherwise it will set an error on the error stack and + * return PR_FAILURE. Although the error is suitable for leaving on + * the stack, callers may wish to augment the information available by + * placing a more type-specific error on the stack. + * + * The error may be one of the following values: + * NSS_ERROR_POINTER_NOT_REGISTERED + * + * Return value: + * PR_SUCCESS + * PR_FAILRUE + */ + +#ifdef DEBUG +NSS_EXTERN PRStatus nssPointerTracker_verify(nssPointerTracker *tracker, + const void *pointer); + +extern const NSSError NSS_ERROR_POINTER_NOT_REGISTERED; +#endif /* DEBUG */ + +/* + * libc + * + * nsslibc_memcpy + * nsslibc_memset + * nsslibc_offsetof + */ + +/* + * nsslibc_memcpy + * + * Errors: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * NULL on error + * The destination pointer on success + */ + +NSS_EXTERN void *nsslibc_memcpy(void *dest, const void *source, PRUint32 n); + +extern const NSSError NSS_ERROR_INVALID_POINTER; + +/* + * nsslibc_memset + * + * Errors: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * NULL on error + * The destination pointer on success + */ + +NSS_EXTERN void *nsslibc_memset(void *dest, PRUint8 byte, PRUint32 n); + +extern const NSSError NSS_ERROR_INVALID_POINTER; + +/* + * nsslibc_memequal + * + * Errors: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_TRUE if they match + * PR_FALSE if they don't + * PR_FALSE upon error + */ + +NSS_EXTERN PRBool nsslibc_memequal(const void *a, const void *b, PRUint32 len, + PRStatus *statusOpt); + +extern const NSSError NSS_ERROR_INVALID_POINTER; + +#define nsslibc_offsetof(str, memb) ((PRPtrdiff)(&(((str *)0)->memb))) + +PR_END_EXTERN_C + +#endif /* BASE_H */ diff --git a/security/nss/lib/base/baset.h b/security/nss/lib/base/baset.h new file mode 100644 index 0000000000..3953a755e5 --- /dev/null +++ b/security/nss/lib/base/baset.h @@ -0,0 +1,124 @@ +/* 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/. */ + +#ifndef BASET_H +#define BASET_H + +/* + * baset.h + * + * This file contains definitions for the basic types used throughout + * nss but not available publicly. + */ + +#ifndef NSSBASET_H +#include "nssbaset.h" +#endif /* NSSBASET_H */ + +#include "plhash.h" + +PR_BEGIN_EXTERN_C + +/* + * nssArenaMark + * + * This type is used to mark the current state of an NSSArena. + */ + +struct nssArenaMarkStr; +typedef struct nssArenaMarkStr nssArenaMark; + +#ifdef DEBUG +/* + * ARENA_THREADMARK + * + * Optionally, this arena implementation can be compiled with some + * runtime checking enabled, which will catch the situation where + * one thread "marks" the arena, another thread allocates memory, + * and then the mark is released. Usually this is a surprise to + * the second thread, and this leads to weird runtime errors. + * Define ARENA_THREADMARK to catch these cases; we define it for all + * (internal and external) debug builds. + */ +#define ARENA_THREADMARK + +/* + * ARENA_DESTRUCTOR_LIST + * + * Unfortunately, our pointer-tracker facility, used in debug + * builds to agressively fight invalid pointers, requries that + * pointers be deregistered when objects are destroyed. This + * conflicts with the standard arena usage where "memory-only" + * objects (that don't hold onto resources outside the arena) + * can be allocated in an arena, and never destroyed other than + * when the arena is destroyed. Therefore we have added a + * destructor-registratio facility to our arenas. This was not + * a simple decision, since we're getting ever-further away from + * the original arena philosophy. However, it was felt that + * adding this in debug builds wouldn't be so bad; as it would + * discourage them from being used for "serious" purposes. + * This facility requires ARENA_THREADMARK to be defined. + */ +#ifdef ARENA_THREADMARK +#define ARENA_DESTRUCTOR_LIST +#endif /* ARENA_THREADMARK */ + +#endif /* DEBUG */ + +typedef struct nssListStr nssList; +typedef struct nssListIteratorStr nssListIterator; +typedef PRBool (*nssListCompareFunc)(void *a, void *b); +typedef PRIntn (*nssListSortFunc)(void *a, void *b); +typedef void (*nssListElementDestructorFunc)(void *el); + +typedef struct nssHashStr nssHash; +typedef void(PR_CALLBACK *nssHashIterator)(const void *key, void *value, + void *arg); + +/* + * nssPointerTracker + * + * This type is used in debug builds (both external and internal) to + * track our object pointers. Objects of this type must be statically + * allocated, which means the structure size must be available to the + * compiler. Therefore we must expose the contents of this structure. + * But please don't access elements directly; use the accessors. + */ + +#ifdef DEBUG +struct nssPointerTrackerStr { + PRCallOnceType once; + PZLock *lock; + PLHashTable *table; +}; +typedef struct nssPointerTrackerStr nssPointerTracker; +#endif /* DEBUG */ + +/* + * nssStringType + * + * There are several types of strings in the real world. We try to + * use only UTF8 and avoid the rest, but that's not always possible. + * So we have a couple converter routines to go to and from the other + * string types. We have to be able to specify those string types, + * so we have this enumeration. + */ + +enum nssStringTypeEnum { + nssStringType_DirectoryString, + nssStringType_TeletexString, /* Not "teletext" with trailing 't' */ + nssStringType_PrintableString, + nssStringType_UniversalString, + nssStringType_BMPString, + nssStringType_UTF8String, + nssStringType_PHGString, + nssStringType_GeneralString, + + nssStringType_Unknown = -1 +}; +typedef enum nssStringTypeEnum nssStringType; + +PR_END_EXTERN_C + +#endif /* BASET_H */ diff --git a/security/nss/lib/base/error.c b/security/nss/lib/base/error.c new file mode 100644 index 0000000000..6cbe785c34 --- /dev/null +++ b/security/nss/lib/base/error.c @@ -0,0 +1,301 @@ +/* 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/. */ + +/* + * error.c + * + * This file contains the code implementing the per-thread error + * stacks upon which most NSS routines report their errors. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ +#include <limits.h> /* for UINT_MAX */ +#include <string.h> /* for memmove */ + +#if defined(__MINGW32__) +#include <windows.h> +#endif + +#define NSS_MAX_ERROR_STACK_COUNT 16 /* error codes */ + +/* + * The stack itself has a header, and a sequence of integers. + * The header records the amount of space (as measured in stack + * slots) already allocated for the stack, and the count of the + * number of records currently being used. + */ + +struct stack_header_str { + PRUint16 space; + PRUint16 count; +}; + +struct error_stack_str { + struct stack_header_str header; + PRInt32 stack[1]; +}; +typedef struct error_stack_str error_stack; + +/* + * error_stack_index + * + * Thread-private data must be indexed. This is that index. + * See PR_NewThreadPrivateIndex for more information. + * + * Thread-private data indexes are in the range [0, 127]. + */ + +#define INVALID_TPD_INDEX UINT_MAX +static PRUintn error_stack_index = INVALID_TPD_INDEX; + +/* + * call_once + * + * The thread-private index must be obtained (once!) at runtime. + * This block is used for that one-time call. + */ + +static PRCallOnceType error_call_once; +static const PRCallOnceType error_call_again; + +/* + * error_once_function + * + * This is the once-called callback. + */ +static PRStatus +error_once_function(void) +{ + +/* + * This #ifdef function is redundant. It performs the same thing as the + * else case. + * + * However, the MinGW version looks up the function from nss3's export + * table, and on MinGW _that_ behaves differently than passing a + * function pointer in a different module because MinGW has + * -mnop-fun-dllimport specified, which generates function thunks for + * cross-module calls. And when a module (like nssckbi) gets unloaded, + * and you try to call into that thunk (which is now missing) you'll + * crash. So we do this bit of ugly to avoid that crash. Fortunately + * this is the only place we've had to do this. + */ +#if defined(__MINGW32__) + HMODULE nss3 = GetModuleHandleW(L"nss3"); + if (nss3) { + PRThreadPrivateDTOR freePtr = (PRThreadPrivateDTOR)GetProcAddress(nss3, "PR_Free"); + if (freePtr) { + return PR_NewThreadPrivateIndex(&error_stack_index, freePtr); + } + } + return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free); +#else + return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free); +#endif +} + +/* + * error_get_my_stack + * + * This routine returns the calling thread's error stack, creating + * it if necessary. It may return NULL upon error, which implicitly + * means that it ran out of memory. + */ + +static error_stack * +error_get_my_stack(void) +{ + PRStatus st; + error_stack *rv; + PRUintn new_size; + PRUint32 new_bytes; + error_stack *new_stack; + + if (INVALID_TPD_INDEX == error_stack_index) { + st = PR_CallOnce(&error_call_once, error_once_function); + if (PR_SUCCESS != st) { + return (error_stack *)NULL; + } + } + + rv = (error_stack *)PR_GetThreadPrivate(error_stack_index); + if ((error_stack *)NULL == rv) { + /* Doesn't exist; create one */ + new_size = 16; + } else if (rv->header.count == rv->header.space && + rv->header.count < NSS_MAX_ERROR_STACK_COUNT) { + /* Too small, expand it */ + new_size = PR_MIN(rv->header.space * 2, NSS_MAX_ERROR_STACK_COUNT); + } else { + /* Okay, return it */ + return rv; + } + + new_bytes = (new_size * sizeof(PRInt32)) + sizeof(error_stack); + /* Use NSPR's calloc/realloc, not NSS's, to avoid loops! */ + new_stack = PR_Calloc(1, new_bytes); + + if ((error_stack *)NULL != new_stack) { + if ((error_stack *)NULL != rv) { + (void)nsslibc_memcpy(new_stack, rv, rv->header.space); + } + new_stack->header.space = new_size; + } + + /* Set the value, whether or not the allocation worked */ + PR_SetThreadPrivate(error_stack_index, new_stack); + return new_stack; +} + +/* + * The error stack + * + * The public methods relating to the error stack are: + * + * NSS_GetError + * NSS_GetErrorStack + * + * The nonpublic methods relating to the error stack are: + * + * nss_SetError + * nss_ClearErrorStack + * + */ + +/* + * NSS_GetError + * + * This routine returns the highest-level (most general) error set + * by the most recent NSS library routine called by the same thread + * calling this routine. + * + * This routine cannot fail. However, it may return zero, which + * indicates that the previous NSS library call did not set an error. + * + * Return value: + * 0 if no error has been set + * A nonzero error number + */ + +NSS_IMPLEMENT PRInt32 +NSS_GetError(void) +{ + error_stack *es = error_get_my_stack(); + + if ((error_stack *)NULL == es) { + return NSS_ERROR_NO_MEMORY; /* Good guess! */ + } + + if (0 == es->header.count) { + return 0; + } + + return es->stack[es->header.count - 1]; +} + +/* + * NSS_GetErrorStack + * + * This routine returns a pointer to an array of integers, containing + * the entire sequence or "stack" of errors set by the most recent NSS + * library routine called by the same thread calling this routine. + * NOTE: the caller DOES NOT OWN the memory pointed to by the return + * value. The pointer will remain valid until the calling thread + * calls another NSS routine. The lowest-level (most specific) error + * is first in the array, and the highest-level is last. The array is + * zero-terminated. This routine may return NULL upon error; this + * indicates a low-memory situation. + * + * Return value: + * NULL upon error, which is an implied NSS_ERROR_NO_MEMORY + * A NON-caller-owned pointer to an array of integers + */ + +NSS_IMPLEMENT PRInt32 * +NSS_GetErrorStack(void) +{ + error_stack *es = error_get_my_stack(); + + if ((error_stack *)NULL == es) { + return (PRInt32 *)NULL; + } + + /* Make sure it's terminated */ + es->stack[es->header.count] = 0; + + return es->stack; +} + +/* + * nss_SetError + * + * This routine places a new error code on the top of the calling + * thread's error stack. Calling this routine wiht an error code + * of zero will clear the error stack. + */ + +NSS_IMPLEMENT void +nss_SetError(PRUint32 error) +{ + error_stack *es; + + if (0 == error) { + nss_ClearErrorStack(); + return; + } + + es = error_get_my_stack(); + if ((error_stack *)NULL == es) { + /* Oh, well. */ + return; + } + + if (es->header.count < es->header.space) { + es->stack[es->header.count++] = error; + } else { + memmove(es->stack, es->stack + 1, + (es->header.space - 1) * (sizeof es->stack[0])); + es->stack[es->header.space - 1] = error; + } + return; +} + +/* + * nss_ClearErrorStack + * + * This routine clears the calling thread's error stack. + */ + +NSS_IMPLEMENT void +nss_ClearErrorStack(void) +{ + error_stack *es = error_get_my_stack(); + if ((error_stack *)NULL == es) { + /* Oh, well. */ + return; + } + + es->header.count = 0; + es->stack[0] = 0; + return; +} + +/* + * nss_DestroyErrorStack + * + * This routine frees the calling thread's error stack. + */ + +NSS_IMPLEMENT void +nss_DestroyErrorStack(void) +{ + if (INVALID_TPD_INDEX != error_stack_index) { + PR_SetThreadPrivate(error_stack_index, NULL); + error_stack_index = INVALID_TPD_INDEX; + error_call_once = error_call_again; /* allow to init again */ + } + return; +} diff --git a/security/nss/lib/base/errorval.c b/security/nss/lib/base/errorval.c new file mode 100644 index 0000000000..b7045a3905 --- /dev/null +++ b/security/nss/lib/base/errorval.c @@ -0,0 +1,65 @@ +/* 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/. */ + +/* + * errorval.c + * + * This file contains the actual error constants used in NSS. + */ + +#ifndef NSSBASET_H +#include "nssbaset.h" +#endif /* NSSBASET_H */ + +/* clang-format off */ + +const NSSError NSS_ERROR_NO_ERROR = 0; +const NSSError NSS_ERROR_INTERNAL_ERROR = 1; +const NSSError NSS_ERROR_NO_MEMORY = 2; +const NSSError NSS_ERROR_INVALID_POINTER = 3; +const NSSError NSS_ERROR_INVALID_ARENA = 4; +const NSSError NSS_ERROR_INVALID_ARENA_MARK = 5; +const NSSError NSS_ERROR_DUPLICATE_POINTER = 6; +const NSSError NSS_ERROR_POINTER_NOT_REGISTERED = 7; +const NSSError NSS_ERROR_TRACKER_NOT_EMPTY = 8; +const NSSError NSS_ERROR_TRACKER_NOT_INITIALIZED = 9; +const NSSError NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD = 10; +const NSSError NSS_ERROR_VALUE_TOO_LARGE = 11; +const NSSError NSS_ERROR_UNSUPPORTED_TYPE = 12; +const NSSError NSS_ERROR_BUFFER_TOO_SHORT = 13; +const NSSError NSS_ERROR_INVALID_ATOB_CONTEXT = 14; +const NSSError NSS_ERROR_INVALID_BASE64 = 15; +const NSSError NSS_ERROR_INVALID_BTOA_CONTEXT = 16; +const NSSError NSS_ERROR_INVALID_ITEM = 17; +const NSSError NSS_ERROR_INVALID_STRING = 18; +const NSSError NSS_ERROR_INVALID_ASN1ENCODER = 19; +const NSSError NSS_ERROR_INVALID_ASN1DECODER = 20; + +const NSSError NSS_ERROR_INVALID_BER = 21; +const NSSError NSS_ERROR_INVALID_ATAV = 22; +const NSSError NSS_ERROR_INVALID_ARGUMENT = 23; +const NSSError NSS_ERROR_INVALID_UTF8 = 24; +const NSSError NSS_ERROR_INVALID_NSSOID = 25; +const NSSError NSS_ERROR_UNKNOWN_ATTRIBUTE = 26; + +const NSSError NSS_ERROR_NOT_FOUND = 27; + +const NSSError NSS_ERROR_INVALID_PASSWORD = 28; +const NSSError NSS_ERROR_USER_CANCELED = 29; + +const NSSError NSS_ERROR_MAXIMUM_FOUND = 30; + +const NSSError NSS_ERROR_CERTIFICATE_ISSUER_NOT_FOUND = 31; + +const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE = 32; + +const NSSError NSS_ERROR_HASH_COLLISION = 33; +const NSSError NSS_ERROR_DEVICE_ERROR = 34; +const NSSError NSS_ERROR_INVALID_CERTIFICATE = 35; +const NSSError NSS_ERROR_BUSY = 36; +const NSSError NSS_ERROR_ALREADY_INITIALIZED = 37; + +const NSSError NSS_ERROR_PKCS11 = 38; + +/* clang-format on */
\ No newline at end of file diff --git a/security/nss/lib/base/exports.gyp b/security/nss/lib/base/exports.gyp new file mode 100644 index 0000000000..394bc1dea7 --- /dev/null +++ b/security/nss/lib/base/exports.gyp @@ -0,0 +1,33 @@ +# 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/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'lib_base_exports', + 'type': 'none', + 'copies': [ + { + 'files': [ + 'nssbase.h', + 'nssbaset.h' + ], + 'destination': '<(nss_public_dist_dir)/<(module)' + }, + { + 'files': [ + 'base.h', + 'baset.h' + ], + 'destination': '<(nss_private_dist_dir)/<(module)' + } + ] + } + ], + 'variables': { + 'module': 'nss' + } +} diff --git a/security/nss/lib/base/hash.c b/security/nss/lib/base/hash.c new file mode 100644 index 0000000000..f9ee758038 --- /dev/null +++ b/security/nss/lib/base/hash.c @@ -0,0 +1,314 @@ +/* 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/. */ + +/* + * hash.c + * + * This is merely a couple wrappers around NSPR's PLHashTable, using + * the identity hash and arena-aware allocators. + * This is a copy of ckfw/hash.c, with modifications to use NSS types + * (not Cryptoki types). Would like for this to be a single implementation, + * but doesn't seem like it will work. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +#include "prbit.h" + +/* + * nssHash + * + * nssHash_Create + * nssHash_Destroy + * nssHash_Add + * nssHash_Remove + * nssHash_Count + * nssHash_Exists + * nssHash_Lookup + * nssHash_Iterate + */ + +struct nssHashStr { + NSSArena *arena; + PRBool i_alloced_arena; + PRLock *mutex; + + /* + * The invariant that mutex protects is: + * The count accurately reflects the hashtable state. + */ + + PLHashTable *plHashTable; + PRUint32 count; +}; + +static PLHashNumber +nss_identity_hash(const void *key) +{ + return (PLHashNumber)((char *)key - (char *)NULL); +} + +static PLHashNumber +nss_item_hash(const void *key) +{ + unsigned int i; + PLHashNumber h; + NSSItem *it = (NSSItem *)key; + h = 0; + for (i = 0; i < it->size; i++) + h = PR_ROTATE_LEFT32(h, 4) ^ ((unsigned char *)it->data)[i]; + return h; +} + +static int +nss_compare_items(const void *v1, const void *v2) +{ + PRStatus ignore; + return (int)nssItem_Equal((NSSItem *)v1, (NSSItem *)v2, &ignore); +} + +/* + * nssHash_create + * + */ +NSS_IMPLEMENT nssHash * +nssHash_Create(NSSArena *arenaOpt, PRUint32 numBuckets, PLHashFunction keyHash, + PLHashComparator keyCompare, PLHashComparator valueCompare) +{ + nssHash *rv; + NSSArena *arena; + PRBool i_alloced; + +#ifdef NSSDEBUG + if (arenaOpt && PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (nssHash *)NULL; + } +#endif /* NSSDEBUG */ + + if (arenaOpt) { + arena = arenaOpt; + i_alloced = PR_FALSE; + } else { + arena = nssArena_Create(); + i_alloced = PR_TRUE; + } + + rv = nss_ZNEW(arena, nssHash); + if ((nssHash *)NULL == rv) { + goto loser; + } + + rv->mutex = PZ_NewLock(nssILockOther); + if ((PZLock *)NULL == rv->mutex) { + goto loser; + } + + rv->plHashTable = + PL_NewHashTable(numBuckets, keyHash, keyCompare, valueCompare, + &nssArenaHashAllocOps, arena); + if ((PLHashTable *)NULL == rv->plHashTable) { + (void)PZ_DestroyLock(rv->mutex); + goto loser; + } + + rv->count = 0; + rv->arena = arena; + rv->i_alloced_arena = i_alloced; + + return rv; +loser: + (void)nss_ZFreeIf(rv); + return (nssHash *)NULL; +} + +/* + * nssHash_CreatePointer + * + */ +NSS_IMPLEMENT nssHash * +nssHash_CreatePointer(NSSArena *arenaOpt, PRUint32 numBuckets) +{ + return nssHash_Create(arenaOpt, numBuckets, nss_identity_hash, + PL_CompareValues, PL_CompareValues); +} + +/* + * nssHash_CreateString + * + */ +NSS_IMPLEMENT nssHash * +nssHash_CreateString(NSSArena *arenaOpt, PRUint32 numBuckets) +{ + return nssHash_Create(arenaOpt, numBuckets, PL_HashString, + PL_CompareStrings, PL_CompareStrings); +} + +/* + * nssHash_CreateItem + * + */ +NSS_IMPLEMENT nssHash * +nssHash_CreateItem(NSSArena *arenaOpt, PRUint32 numBuckets) +{ + return nssHash_Create(arenaOpt, numBuckets, nss_item_hash, + nss_compare_items, PL_CompareValues); +} + +/* + * nssHash_Destroy + * + */ +NSS_IMPLEMENT void +nssHash_Destroy(nssHash *hash) +{ + (void)PZ_DestroyLock(hash->mutex); + PL_HashTableDestroy(hash->plHashTable); + if (hash->i_alloced_arena) { + nssArena_Destroy(hash->arena); + } else { + nss_ZFreeIf(hash); + } +} + +/* + * nssHash_Add + * + */ +NSS_IMPLEMENT PRStatus +nssHash_Add(nssHash *hash, const void *key, const void *value) +{ + PRStatus error = PR_FAILURE; + PLHashEntry *he; + + PZ_Lock(hash->mutex); + + he = PL_HashTableAdd(hash->plHashTable, key, (void *)value); + if ((PLHashEntry *)NULL == he) { + nss_SetError(NSS_ERROR_NO_MEMORY); + } else if (he->value != value) { + nss_SetError(NSS_ERROR_HASH_COLLISION); + } else { + hash->count++; + error = PR_SUCCESS; + } + + (void)PZ_Unlock(hash->mutex); + + return error; +} + +/* + * nssHash_Remove + * + */ +NSS_IMPLEMENT void +nssHash_Remove(nssHash *hash, const void *it) +{ + PRBool found; + + PZ_Lock(hash->mutex); + + found = PL_HashTableRemove(hash->plHashTable, it); + if (found) { + hash->count--; + } + + (void)PZ_Unlock(hash->mutex); + return; +} + +/* + * nssHash_Count + * + */ +NSS_IMPLEMENT PRUint32 +nssHash_Count(nssHash *hash) +{ + PRUint32 count; + + PZ_Lock(hash->mutex); + + count = hash->count; + + (void)PZ_Unlock(hash->mutex); + + return count; +} + +/* + * nssHash_Exists + * + */ +NSS_IMPLEMENT PRBool +nssHash_Exists(nssHash *hash, const void *it) +{ + void *value; + + PZ_Lock(hash->mutex); + + value = PL_HashTableLookup(hash->plHashTable, it); + + (void)PZ_Unlock(hash->mutex); + + if ((void *)NULL == value) { + return PR_FALSE; + } else { + return PR_TRUE; + } +} + +/* + * nssHash_Lookup + * + */ +NSS_IMPLEMENT void * +nssHash_Lookup(nssHash *hash, const void *it) +{ + void *rv; + + PZ_Lock(hash->mutex); + + rv = PL_HashTableLookup(hash->plHashTable, it); + + (void)PZ_Unlock(hash->mutex); + + return rv; +} + +struct arg_str { + nssHashIterator fcn; + void *closure; +}; + +static PRIntn +nss_hash_enumerator(PLHashEntry *he, PRIntn index, void *arg) +{ + struct arg_str *as = (struct arg_str *)arg; + as->fcn(he->key, he->value, as->closure); + return HT_ENUMERATE_NEXT; +} + +/* + * nssHash_Iterate + * + * NOTE that the iteration function will be called with the hashtable locked. + */ +NSS_IMPLEMENT void +nssHash_Iterate(nssHash *hash, nssHashIterator fcn, void *closure) +{ + struct arg_str as; + as.fcn = fcn; + as.closure = closure; + + PZ_Lock(hash->mutex); + + PL_HashTableEnumerateEntries(hash->plHashTable, nss_hash_enumerator, &as); + + (void)PZ_Unlock(hash->mutex); + + return; +} diff --git a/security/nss/lib/base/hashops.c b/security/nss/lib/base/hashops.c new file mode 100644 index 0000000000..57b30dd1fd --- /dev/null +++ b/security/nss/lib/base/hashops.c @@ -0,0 +1,64 @@ +/* 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/. */ + +/* + * hashops.c + * + * This file includes a set of PLHashAllocOps that use NSSArenas. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +static void *PR_CALLBACK +nss_arena_hash_alloc_table(void *pool, PRSize size) +{ + NSSArena *arena = (NSSArena *)NULL; + +#ifdef NSSDEBUG + if ((void *)NULL != arena) { + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return (void *)NULL; + } + } +#endif /* NSSDEBUG */ + + return nss_ZAlloc(arena, size); +} + +static void PR_CALLBACK +nss_arena_hash_free_table(void *pool, void *item) +{ + (void)nss_ZFreeIf(item); +} + +static PLHashEntry *PR_CALLBACK +nss_arena_hash_alloc_entry(void *pool, const void *key) +{ + NSSArena *arena = NULL; + +#ifdef NSSDEBUG + if ((void *)NULL != arena) { + if (PR_SUCCESS != nssArena_verifyPointer(arena)) { + return (void *)NULL; + } + } +#endif /* NSSDEBUG */ + + return nss_ZNEW(arena, PLHashEntry); +} + +static void PR_CALLBACK +nss_arena_hash_free_entry(void *pool, PLHashEntry *he, PRUintn flag) +{ + if (HT_FREE_ENTRY == flag) { + (void)nss_ZFreeIf(he); + } +} + +NSS_IMPLEMENT_DATA PLHashAllocOps nssArenaHashAllocOps = { + nss_arena_hash_alloc_table, nss_arena_hash_free_table, + nss_arena_hash_alloc_entry, nss_arena_hash_free_entry +}; diff --git a/security/nss/lib/base/item.c b/security/nss/lib/base/item.c new file mode 100644 index 0000000000..a1bb802ed6 --- /dev/null +++ b/security/nss/lib/base/item.c @@ -0,0 +1,186 @@ +/* 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/. */ + +/* + * item.c + * + * This contains some item-manipulation code. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +/* + * nssItem_Create + * + * -- fgmr comments -- + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * A pointer to an NSSItem upon success + * NULL upon failure + */ + +NSS_IMPLEMENT NSSItem * +nssItem_Create(NSSArena *arenaOpt, NSSItem *rvOpt, PRUint32 length, + const void *data) +{ + NSSItem *rv = (NSSItem *)NULL; + +#ifdef DEBUG + if ((NSSArena *)NULL != arenaOpt) { + if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + return (NSSItem *)NULL; + } + } + + if ((const void *)NULL == data) { + if (length > 0) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (NSSItem *)NULL; + } + } +#endif /* DEBUG */ + + if ((NSSItem *)NULL == rvOpt) { + rv = (NSSItem *)nss_ZNEW(arenaOpt, NSSItem); + if ((NSSItem *)NULL == rv) { + goto loser; + } + } else { + rv = rvOpt; + } + + rv->size = length; + rv->data = nss_ZAlloc(arenaOpt, length); + if ((void *)NULL == rv->data) { + goto loser; + } + + if (length > 0) { + (void)nsslibc_memcpy(rv->data, data, length); + } + + return rv; + +loser: + if (rv != rvOpt) { + nss_ZFreeIf(rv); + } + + return (NSSItem *)NULL; +} + +NSS_IMPLEMENT void +nssItem_Destroy(NSSItem *item) +{ + nss_ClearErrorStack(); + + nss_ZFreeIf(item->data); + nss_ZFreeIf(item); +} + +/* + * nssItem_Duplicate + * + * -- fgmr comments -- + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD + * NSS_ERROR_INVALID_ITEM + * + * Return value: + * A pointer to an NSSItem upon success + * NULL upon failure + */ + +NSS_IMPLEMENT NSSItem * +nssItem_Duplicate(NSSItem *obj, NSSArena *arenaOpt, NSSItem *rvOpt) +{ +#ifdef DEBUG + if ((NSSArena *)NULL != arenaOpt) { + if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + return (NSSItem *)NULL; + } + } + + if ((NSSItem *)NULL == obj) { + nss_SetError(NSS_ERROR_INVALID_ITEM); + return (NSSItem *)NULL; + } +#endif /* DEBUG */ + + return nssItem_Create(arenaOpt, rvOpt, obj->size, obj->data); +} + +#ifdef DEBUG +/* + * nssItem_verifyPointer + * + * -- fgmr comments -- + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ITEM + * + * Return value: + * PR_SUCCESS upon success + * PR_FAILURE upon failure + */ + +NSS_IMPLEMENT PRStatus +nssItem_verifyPointer(const NSSItem *item) +{ + if (((const NSSItem *)NULL == item) || + (((void *)NULL == item->data) && (item->size > 0))) { + nss_SetError(NSS_ERROR_INVALID_ITEM); + return PR_FAILURE; + } + + return PR_SUCCESS; +} +#endif /* DEBUG */ + +/* + * nssItem_Equal + * + * -- fgmr comments -- + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ITEM + * + * Return value: + * PR_TRUE if the items are identical + * PR_FALSE if they aren't + * PR_FALSE upon error + */ + +NSS_IMPLEMENT PRBool +nssItem_Equal(const NSSItem *one, const NSSItem *two, PRStatus *statusOpt) +{ + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + if (((const NSSItem *)NULL == one) && ((const NSSItem *)NULL == two)) { + return PR_TRUE; + } + + if (((const NSSItem *)NULL == one) || ((const NSSItem *)NULL == two)) { + return PR_FALSE; + } + + if (one->size != two->size) { + return PR_FALSE; + } + + return nsslibc_memequal(one->data, two->data, one->size, statusOpt); +} diff --git a/security/nss/lib/base/libc.c b/security/nss/lib/base/libc.c new file mode 100644 index 0000000000..7954a31610 --- /dev/null +++ b/security/nss/lib/base/libc.c @@ -0,0 +1,143 @@ +/* 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/. */ + +/* + * libc.c + * + * This file contains our wrappers/reimplementations for "standard" + * libc functions. Things like "memcpy." We add to this as we need + * it. Oh, and let's keep it in alphabetical order, should it ever + * get large. Most string/character stuff should be in utf8.c, not + * here. This file (and maybe utf8.c) should be the only ones in + * NSS to include files with angle brackets. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +#include <string.h> /* memcpy, memset */ + +/* + * nsslibc_memcpy + * nsslibc_memset + * nsslibc_offsetof + * nsslibc_memequal + */ + +/* + * nsslibc_memcpy + * + * Errors: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * NULL on error + * The destination pointer on success + */ + +NSS_IMPLEMENT void * +nsslibc_memcpy(void *dest, const void *source, PRUint32 n) +{ +#ifdef NSSDEBUG + if (((void *)NULL == dest) || ((const void *)NULL == source)) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (void *)NULL; + } +#endif /* NSSDEBUG */ + + return memcpy(dest, source, (size_t)n); +} + +/* + * nsslibc_memset + * + * Errors: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * NULL on error + * The destination pointer on success + */ + +NSS_IMPLEMENT void * +nsslibc_memset(void *dest, PRUint8 byte, PRUint32 n) +{ +#ifdef NSSDEBUG + if (((void *)NULL == dest)) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (void *)NULL; + } +#endif /* NSSDEBUG */ + + return memset(dest, (int)byte, (size_t)n); +} + +/* + * nsslibc_memequal + * + * Errors: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_TRUE if they match + * PR_FALSE if they don't + * PR_FALSE upon error + */ + +NSS_IMPLEMENT PRBool +nsslibc_memequal(const void *a, const void *b, PRUint32 len, + PRStatus *statusOpt) +{ +#ifdef NSSDEBUG + if ((((void *)NULL == a) || ((void *)NULL == b))) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return PR_FALSE; + } +#endif /* NSSDEBUG */ + + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + if (0 == memcmp(a, b, len)) { + return PR_TRUE; + } else { + return PR_FALSE; + } +} + +/* + * nsslibc_memcmp + */ + +NSS_IMPLEMENT PRInt32 +nsslibc_memcmp(const void *a, const void *b, PRUint32 len, PRStatus *statusOpt) +{ + int v; + +#ifdef NSSDEBUG + if ((((void *)NULL == a) || ((void *)NULL == b))) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return -2; + } +#endif /* NSSDEBUG */ + + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + v = memcmp(a, b, len); + return (PRInt32)v; +} + +/* + * offsetof is a preprocessor definition + */ diff --git a/security/nss/lib/base/list.c b/security/nss/lib/base/list.c new file mode 100644 index 0000000000..e798cf09cd --- /dev/null +++ b/security/nss/lib/base/list.c @@ -0,0 +1,405 @@ +/* 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/. */ + +/* + * list.c + * + * This contains the implementation of NSS's thread-safe linked list. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +struct nssListElementStr { + PRCList link; + void *data; +}; + +typedef struct nssListElementStr nssListElement; + +struct nssListStr { + NSSArena *arena; + PZLock *lock; + nssListElement *head; + PRUint32 count; + nssListCompareFunc compareFunc; + nssListSortFunc sortFunc; + PRBool i_alloced_arena; +}; + +struct nssListIteratorStr { + PZLock *lock; + nssList *list; + nssListElement *current; +}; + +#define NSSLIST_LOCK_IF(list) \ + if ((list)->lock) \ + PZ_Lock((list)->lock) + +#define NSSLIST_UNLOCK_IF(list) \ + if ((list)->lock) \ + PZ_Unlock((list)->lock) + +static PRBool +pointer_compare(void *a, void *b) +{ + return (PRBool)(a == b); +} + +static nssListElement * +nsslist_get_matching_element(nssList *list, void *data) +{ + nssListElement *node; + node = list->head; + if (!node) { + return NULL; + } + while (node) { + /* using a callback slows things down when it's just compare ... */ + if (list->compareFunc(node->data, data)) { + break; + } + if (&node->link == PR_LIST_TAIL(&list->head->link)) { + node = NULL; + break; + } + node = (nssListElement *)PR_NEXT_LINK(&node->link); + } + return node; +} + +NSS_IMPLEMENT nssList * +nssList_Create(NSSArena *arenaOpt, PRBool threadSafe) +{ + NSSArena *arena; + nssList *list; + PRBool i_alloced; + if (arenaOpt) { + arena = arenaOpt; + i_alloced = PR_FALSE; + } else { + arena = nssArena_Create(); + i_alloced = PR_TRUE; + } + if (!arena) { + return (nssList *)NULL; + } + list = nss_ZNEW(arena, nssList); + if (!list) { + if (!arenaOpt) { + NSSArena_Destroy(arena); + } + return (nssList *)NULL; + } + if (threadSafe) { + list->lock = PZ_NewLock(nssILockOther); + if (!list->lock) { + if (arenaOpt) { + nss_ZFreeIf(list); + } else { + NSSArena_Destroy(arena); + } + return (nssList *)NULL; + } + } + list->arena = arena; + list->i_alloced_arena = i_alloced; + list->compareFunc = pointer_compare; + return list; +} + +NSS_IMPLEMENT PRStatus +nssList_Destroy(nssList *list) +{ + if (!list) { + return PR_SUCCESS; + } + if (!list->i_alloced_arena) { + nssList_Clear(list, NULL); + } + if (list->lock) { + (void)PZ_DestroyLock(list->lock); + } + if (list->i_alloced_arena) { + NSSArena_Destroy(list->arena); + list = NULL; + } + nss_ZFreeIf(list); + return PR_SUCCESS; +} + +NSS_IMPLEMENT void +nssList_SetCompareFunction(nssList *list, nssListCompareFunc compareFunc) +{ + list->compareFunc = compareFunc; +} + +NSS_IMPLEMENT void +nssList_SetSortFunction(nssList *list, nssListSortFunc sortFunc) +{ + /* XXX if list already has elements, sort them */ + list->sortFunc = sortFunc; +} + +NSS_IMPLEMENT nssListCompareFunc +nssList_GetCompareFunction(nssList *list) +{ + return list->compareFunc; +} + +NSS_IMPLEMENT void +nssList_Clear(nssList *list, nssListElementDestructorFunc destructor) +{ + PRCList *link; + nssListElement *node, *tmp; + if (!list) { + return; + } + NSSLIST_LOCK_IF(list); + node = list->head; + list->head = NULL; + while (node && list->count > 0) { + if (destructor) + (*destructor)(node->data); + link = &node->link; + tmp = (nssListElement *)PR_NEXT_LINK(link); + PR_REMOVE_LINK(link); + nss_ZFreeIf(node); + node = tmp; + --list->count; + } + NSSLIST_UNLOCK_IF(list); +} + +static PRStatus +nsslist_add_element(nssList *list, void *data) +{ + nssListElement *node = nss_ZNEW(list->arena, nssListElement); + if (!node) { + return PR_FAILURE; + } + PR_INIT_CLIST(&node->link); + node->data = data; + if (list->head) { + if (list->sortFunc) { + PRCList *link; + nssListElement *currNode; + currNode = list->head; + /* insert in ordered list */ + while (currNode) { + link = &currNode->link; + if (list->sortFunc(data, currNode->data) <= 0) { + /* new element goes before current node */ + PR_INSERT_BEFORE(&node->link, link); + /* reset head if this is first */ + if (currNode == list->head) + list->head = node; + break; + } + if (link == PR_LIST_TAIL(&list->head->link)) { + /* reached end of list, append */ + PR_INSERT_AFTER(&node->link, link); + break; + } + currNode = (nssListElement *)PR_NEXT_LINK(&currNode->link); + } + } else { + /* not sorting */ + PR_APPEND_LINK(&node->link, &list->head->link); + } + } else { + list->head = node; + } + ++list->count; + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +nssList_Add(nssList *list, void *data) +{ + NSSLIST_LOCK_IF(list); + (void)nsslist_add_element(list, data); + NSSLIST_UNLOCK_IF(list); + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +nssList_AddUnique(nssList *list, void *data) +{ + PRStatus nssrv; + nssListElement *node; + NSSLIST_LOCK_IF(list); + node = nsslist_get_matching_element(list, data); + if (node) { + /* already in, finish */ + NSSLIST_UNLOCK_IF(list); + return PR_SUCCESS; + } + nssrv = nsslist_add_element(list, data); + NSSLIST_UNLOCK_IF(list); + return nssrv; +} + +NSS_IMPLEMENT PRStatus +nssList_Remove(nssList *list, void *data) +{ + nssListElement *node; + NSSLIST_LOCK_IF(list); + node = nsslist_get_matching_element(list, data); + if (node) { + if (node == list->head) { + list->head = (nssListElement *)PR_NEXT_LINK(&node->link); + } + PR_REMOVE_LINK(&node->link); + nss_ZFreeIf(node); + if (--list->count == 0) { + list->head = NULL; + } + } + NSSLIST_UNLOCK_IF(list); + return PR_SUCCESS; +} + +NSS_IMPLEMENT void * +nssList_Get(nssList *list, void *data) +{ + nssListElement *node; + NSSLIST_LOCK_IF(list); + node = nsslist_get_matching_element(list, data); + NSSLIST_UNLOCK_IF(list); + return (node) ? node->data : NULL; +} + +NSS_IMPLEMENT PRUint32 +nssList_Count(nssList *list) +{ + return list->count; +} + +NSS_IMPLEMENT PRStatus +nssList_GetArray(nssList *list, void **rvArray, PRUint32 maxElements) +{ + nssListElement *node; + PRUint32 i = 0; + PR_ASSERT(maxElements > 0); + node = list->head; + if (!node) { + return PR_SUCCESS; + } + NSSLIST_LOCK_IF(list); + while (node) { + rvArray[i++] = node->data; + if (i == maxElements) + break; + node = (nssListElement *)PR_NEXT_LINK(&node->link); + if (node == list->head) { + break; + } + } + NSSLIST_UNLOCK_IF(list); + return PR_SUCCESS; +} + +NSS_IMPLEMENT nssList * +nssList_Clone(nssList *list) +{ + nssList *rvList; + nssListElement *node; + rvList = nssList_Create(NULL, (list->lock != NULL)); + if (!rvList) { + return NULL; + } + NSSLIST_LOCK_IF(list); + if (list->count > 0) { + node = list->head; + while (PR_TRUE) { + nssList_Add(rvList, node->data); + node = (nssListElement *)PR_NEXT_LINK(&node->link); + if (node == list->head) { + break; + } + } + } + NSSLIST_UNLOCK_IF(list); + return rvList; +} + +NSS_IMPLEMENT nssListIterator * +nssList_CreateIterator(nssList *list) +{ + nssListIterator *rvIterator; + rvIterator = nss_ZNEW(NULL, nssListIterator); + if (!rvIterator) { + return NULL; + } + rvIterator->list = nssList_Clone(list); + if (!rvIterator->list) { + nss_ZFreeIf(rvIterator); + return NULL; + } + rvIterator->current = rvIterator->list->head; + if (list->lock) { + rvIterator->lock = PZ_NewLock(nssILockOther); + if (!rvIterator->lock) { + nssList_Destroy(rvIterator->list); + nss_ZFreeIf(rvIterator); + rvIterator = NULL; + } + } + return rvIterator; +} + +NSS_IMPLEMENT void +nssListIterator_Destroy(nssListIterator *iter) +{ + if (iter->lock) { + (void)PZ_DestroyLock(iter->lock); + } + if (iter->list) { + nssList_Destroy(iter->list); + } + nss_ZFreeIf(iter); +} + +NSS_IMPLEMENT void * +nssListIterator_Start(nssListIterator *iter) +{ + NSSLIST_LOCK_IF(iter); + if (iter->list->count == 0) { + return NULL; + } + iter->current = iter->list->head; + return iter->current->data; +} + +NSS_IMPLEMENT void * +nssListIterator_Next(nssListIterator *iter) +{ + nssListElement *node; + PRCList *link; + if (iter->list->count == 1 || iter->current == NULL) { + /* Reached the end of the list. Don't change the state, force to + * user to call nssList_Finish to clean up. + */ + return NULL; + } + node = (nssListElement *)PR_NEXT_LINK(&iter->current->link); + link = &node->link; + if (link == PR_LIST_TAIL(&iter->list->head->link)) { + /* Signal the end of the list. */ + iter->current = NULL; + return node->data; + } + iter->current = node; + return node->data; +} + +NSS_IMPLEMENT PRStatus +nssListIterator_Finish(nssListIterator *iter) +{ + iter->current = iter->list->head; + return (iter->lock) ? PZ_Unlock(iter->lock) : PR_SUCCESS; +} diff --git a/security/nss/lib/base/manifest.mn b/security/nss/lib/base/manifest.mn new file mode 100644 index 0000000000..6c4b2db98a --- /dev/null +++ b/security/nss/lib/base/manifest.mn @@ -0,0 +1,39 @@ +# +# 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/. + +CORE_DEPTH = ../.. + +PRIVATE_EXPORTS = \ + baset.h \ + base.h \ + $(NULL) + +EXPORTS = \ + nssbaset.h \ + nssbase.h \ + $(NULL) + +MODULE = nss + +CSRCS = \ + arena.c \ + error.c \ + errorval.c \ + hashops.c \ + libc.c \ + tracker.c \ + item.c \ + utf8.c \ + list.c \ + hash.c \ + $(NULL) + +REQUIRES = nspr + +LIBRARY_NAME = nssb +SHARED_LIBRARY = $(NULL) + +# This part of the code, including all sub-dirs, can be optimized for size +export ALLOW_OPT_CODE_SIZE = 1 diff --git a/security/nss/lib/base/nssbase.h b/security/nss/lib/base/nssbase.h new file mode 100644 index 0000000000..02e285fba8 --- /dev/null +++ b/security/nss/lib/base/nssbase.h @@ -0,0 +1,233 @@ +/* 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/. */ + +#ifndef NSSBASE_H +#define NSSBASE_H + +/* + * nssbase.h + * + * This header file contains the prototypes of the basic public + * NSS routines. + */ + +#ifndef NSSBASET_H +#include "nssbaset.h" +#endif /* NSSBASET_H */ + +PR_BEGIN_EXTERN_C + +/* + * NSSArena + * + * The public methods relating to this type are: + * + * NSSArena_Create -- constructor + * NSSArena_Destroy + * NSS_ZAlloc + * NSS_ZRealloc + * NSS_ZFreeIf + */ + +/* + * 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_EXTERN NSSArena *NSSArena_Create(void); + +extern const NSSError NSS_ERROR_NO_MEMORY; + +/* + * 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_EXTERN PRStatus NSSArena_Destroy(NSSArena *arena); + +extern const NSSError NSS_ERROR_INVALID_ARENA; + +/* + * The error stack + * + * The public methods relating to the error stack are: + * + * NSS_GetError + * NSS_GetErrorStack + */ + +/* + * NSS_GetError + * + * This routine returns the highest-level (most general) error set + * by the most recent NSS library routine called by the same thread + * calling this routine. + * + * This routine cannot fail. It may return NSS_ERROR_NO_ERROR, which + * indicates that the previous NSS library call did not set an error. + * + * Return value: + * 0 if no error has been set + * A nonzero error number + */ + +NSS_EXTERN NSSError NSS_GetError(void); + +extern const NSSError NSS_ERROR_NO_ERROR; + +/* + * NSS_GetErrorStack + * + * This routine returns a pointer to an array of NSSError values, + * containingthe entire sequence or "stack" of errors set by the most + * recent NSS library routine called by the same thread calling this + * routine. NOTE: the caller DOES NOT OWN the memory pointed to by + * the return value. The pointer will remain valid until the calling + * thread calls another NSS routine. The lowest-level (most specific) + * error is first in the array, and the highest-level is last. The + * array is zero-terminated. This routine may return NULL upon error; + * this indicates a low-memory situation. + * + * Return value: + * NULL upon error, which is an implied NSS_ERROR_NO_MEMORY + * A NON-caller-owned pointer to an array of NSSError values + */ + +NSS_EXTERN NSSError *NSS_GetErrorStack(void); + +/* + * NSS_ZNEW + * + * This preprocessor macro will allocate memory for a new object + * of the specified type with nss_ZAlloc, and will cast the + * return value appropriately. 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 error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +#define NSS_ZNEW(arenaOpt, type) ((type *)NSS_ZAlloc((arenaOpt), sizeof(type))) + +/* + * NSS_ZNEWARRAY + * + * This preprocessor macro will allocate memory for an array of + * new objects, and will cast the return value appropriately. + * 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 array size may be specified as zero. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + * + * Return value: + * NULL upon error + * A pointer to the new segment of zeroed memory + */ + +#define NSS_ZNEWARRAY(arenaOpt, type, quantity) \ + ((type *)NSS_ZAlloc((arenaOpt), sizeof(type) * (quantity))) + +/* + * 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_EXTERN void *NSS_ZAlloc(NSSArena *arenaOpt, PRUint32 size); + +/* + * 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); + +/* + * 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_EXTERN PRStatus NSS_ZFreeIf(void *pointer); + +PR_END_EXTERN_C + +#endif /* NSSBASE_H */ diff --git a/security/nss/lib/base/nssbaset.h b/security/nss/lib/base/nssbaset.h new file mode 100644 index 0000000000..8bc556e6e9 --- /dev/null +++ b/security/nss/lib/base/nssbaset.h @@ -0,0 +1,118 @@ +/* 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/. */ + +#ifndef NSSBASET_H +#define NSSBASET_H + +/* + * nssbaset.h + * + * This file contains the most low-level, fundamental public types. + */ + +#include "nspr.h" +#include "nssilock.h" + +/* + * NSS_EXTERN, NSS_IMPLEMENT, NSS_EXTERN_DATA, NSS_IMPLEMENT_DATA + * + * NSS has its own versions of these NSPR macros, in a form which + * does not confuse ctags and other related utilities. NSPR + * defines these macros to take the type as an argument, because + * of certain OS requirements on platforms not supported by NSS. + */ + +#define DUMMY /* dummy */ +#define NSS_EXTERN extern +#define NSS_EXTERN_DATA extern +#define NSS_IMPLEMENT +#define NSS_IMPLEMENT_DATA + +PR_BEGIN_EXTERN_C + +/* + * NSSError + * + * Calls to NSS routines may result in one or more errors being placed + * on the calling thread's "error stack." Every possible error that + * may be returned from a function is declared where the function is + * prototyped. All errors are of the following type. + */ + +typedef PRInt32 NSSError; + +/* + * NSSArena + * + * Arenas are logical sets of heap memory, from which memory may be + * allocated. When an arena is destroyed, all memory allocated within + * that arena is implicitly freed. These arenas are thread-safe: + * an arena pointer may be used by multiple threads simultaneously. + * However, as they are not backed by shared memory, they may only be + * used within one process. + */ + +struct NSSArenaStr; +typedef struct NSSArenaStr NSSArena; + +/* + * NSSItem + * + * This is the basic type used to refer to an unconstrained datum of + * arbitrary size. + */ + +struct NSSItemStr { + void *data; + PRUint32 size; +}; +typedef struct NSSItemStr NSSItem; + +/* + * NSSBER + * + * Data packed according to the Basic Encoding Rules of ASN.1. + */ + +typedef NSSItem NSSBER; + +/* + * NSSDER + * + * Data packed according to the Distinguished Encoding Rules of ASN.1; + * this form is also known as the Canonical Encoding Rules form (CER). + */ + +typedef NSSBER NSSDER; + +/* + * NSSBitString + * + * Some ASN.1 types use "bit strings," which are passed around as + * octet strings but whose length is counted in bits. We use this + * typedef of NSSItem to point out the occasions when the length + * is counted in bits, not octets. + */ + +typedef NSSItem NSSBitString; + +/* + * NSSUTF8 + * + * Character strings encoded in UTF-8, as defined by RFC 2279. + */ + +typedef char NSSUTF8; + +/* + * NSSASCII7 + * + * Character strings guaranteed to be 7-bit ASCII. + */ + +typedef char NSSASCII7; + +PR_END_EXTERN_C + +#endif /* NSSBASET_H */ diff --git a/security/nss/lib/base/tracker.c b/security/nss/lib/base/tracker.c new file mode 100644 index 0000000000..850add7c4b --- /dev/null +++ b/security/nss/lib/base/tracker.c @@ -0,0 +1,378 @@ +/* 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/. */ + +/* + * tracker.c + * + * This file contains the code used by the pointer-tracking calls used + * in the debug builds to catch bad pointers. The entire contents are + * only available in debug builds (both internal and external builds). + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +#ifdef DEBUG +/* + * identity_hash + * + * This static callback is a PLHashFunction as defined in plhash.h + * It merely returns the value of the object pointer as its hash. + * There are no possible errors. + */ + +static PLHashNumber PR_CALLBACK +identity_hash(const void *key) +{ + return (PLHashNumber)((char *)key - (char *)NULL); +} + +/* + * trackerOnceFunc + * + * This function is called once, using the nssCallOnce function above. + * It creates a new pointer tracker object; initialising its hash + * table and protective lock. + */ + +static PRStatus +trackerOnceFunc(void *arg) +{ + nssPointerTracker *tracker = (nssPointerTracker *)arg; + + tracker->lock = PZ_NewLock(nssILockOther); + if ((PZLock *)NULL == tracker->lock) { + return PR_FAILURE; + } + + tracker->table = + PL_NewHashTable(0, identity_hash, PL_CompareValues, PL_CompareValues, + (PLHashAllocOps *)NULL, (void *)NULL); + if ((PLHashTable *)NULL == tracker->table) { + PZ_DestroyLock(tracker->lock); + tracker->lock = (PZLock *)NULL; + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +/* + * nssPointerTracker_initialize + * + * This method is only present in debug builds. + * + * This routine initializes an nssPointerTracker object. Note that + * the object must have been declared *static* to guarantee that it + * is in a zeroed state initially. This routine is idempotent, and + * may even be safely called by multiple threads simultaneously with + * the same argument. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCESS. On failure it will set an + * error on the error stack and return PR_FAILURE. + * + * The error may be one of the following values: + * NSS_ERROR_NO_MEMORY + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssPointerTracker_initialize(nssPointerTracker *tracker) +{ + PRStatus rv = PR_CallOnceWithArg(&tracker->once, trackerOnceFunc, tracker); + if (PR_SUCCESS != rv) { + nss_SetError(NSS_ERROR_NO_MEMORY); + } + + return rv; +} + +#ifdef DONT_DESTROY_EMPTY_TABLES +/* See same #ifdef below */ +/* + * count_entries + * + * This static routine is a PLHashEnumerator, as defined in plhash.h. + * It merely causes the enumeration function to count the number of + * entries. + */ + +static PRIntn PR_CALLBACK +count_entries(PLHashEntry *he, PRIntn index, void *arg) +{ + return HT_ENUMERATE_NEXT; +} +#endif /* DONT_DESTROY_EMPTY_TABLES */ + +/* + * zero_once + * + * This is a guaranteed zeroed once block. It's used to help clear + * the tracker. + */ + +static const PRCallOnceType zero_once; + +/* + * nssPointerTracker_finalize + * + * This method is only present in debug builds. + * + * This routine returns the nssPointerTracker object to the pre- + * initialized state, releasing all resources used by the object. + * It will *NOT* destroy the objects being tracked by the pointer + * (should any remain), and therefore cannot be used to "sweep up" + * remaining objects. This routine returns a PRStatus value; if + * successful, it will return PR_SUCCES. On failure it will set an + * error on the error stack and return PR_FAILURE. If any objects + * remain in the tracker when it is finalized, that will be treated + * as an error. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_TRACKER_NOT_INITIALIZED + * NSS_ERROR_TRACKER_NOT_EMPTY + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssPointerTracker_finalize(nssPointerTracker *tracker) +{ + PZLock *lock; + + if ((nssPointerTracker *)NULL == tracker) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FAILURE; + } + + if ((PZLock *)NULL == tracker->lock) { + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + lock = tracker->lock; + PZ_Lock(lock); + + if ((PLHashTable *)NULL == tracker->table) { + PZ_Unlock(lock); + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + +#ifdef DONT_DESTROY_EMPTY_TABLES + /* + * I changed my mind; I think we don't want this after all. + * Comments? + */ + count = PL_HashTableEnumerateEntries(tracker->table, count_entries, + (void *)NULL); + + if (0 != count) { + PZ_Unlock(lock); + nss_SetError(NSS_ERROR_TRACKER_NOT_EMPTY); + return PR_FAILURE; + } +#endif /* DONT_DESTROY_EMPTY_TABLES */ + + PL_HashTableDestroy(tracker->table); + /* memset(tracker, 0, sizeof(nssPointerTracker)); */ + tracker->once = zero_once; + tracker->lock = (PZLock *)NULL; + tracker->table = (PLHashTable *)NULL; + + PZ_Unlock(lock); + PZ_DestroyLock(lock); + + return PR_SUCCESS; +} + +/* + * nssPointerTracker_add + * + * This method is only present in debug builds. + * + * This routine adds the specified pointer to the nssPointerTracker + * object. It should be called in constructor objects to register + * new valid objects. The nssPointerTracker is threadsafe, but this + * call is not idempotent. This routine returns a PRStatus value; + * if successful it will return PR_SUCCESS. On failure 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 + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_TRACKER_NOT_INITIALIZED + * NSS_ERROR_DUPLICATE_POINTER + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssPointerTracker_add(nssPointerTracker *tracker, const void *pointer) +{ + void *check; + PLHashEntry *entry; + + if ((nssPointerTracker *)NULL == tracker) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FAILURE; + } + + if ((PZLock *)NULL == tracker->lock) { + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + PZ_Lock(tracker->lock); + + if ((PLHashTable *)NULL == tracker->table) { + PZ_Unlock(tracker->lock); + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + check = PL_HashTableLookup(tracker->table, pointer); + if ((void *)NULL != check) { + PZ_Unlock(tracker->lock); + nss_SetError(NSS_ERROR_DUPLICATE_POINTER); + return PR_FAILURE; + } + + entry = PL_HashTableAdd(tracker->table, pointer, (void *)pointer); + + PZ_Unlock(tracker->lock); + + if ((PLHashEntry *)NULL == entry) { + nss_SetError(NSS_ERROR_NO_MEMORY); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +/* + * nssPointerTracker_remove + * + * This method is only present in debug builds. + * + * This routine removes the specified pointer from the + * nssPointerTracker object. It does not call any destructor for the + * object; rather, this should be called from the object's destructor. + * The nssPointerTracker is threadsafe, but this call is not + * idempotent. This routine returns a PRStatus value; if successful + * it will return PR_SUCCESS. On failure 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 + * NSS_ERROR_TRACKER_NOT_INITIALIZED + * NSS_ERROR_POINTER_NOT_REGISTERED + * + * Return value: + * PR_SUCCESS + * PR_FAILURE + */ + +NSS_IMPLEMENT PRStatus +nssPointerTracker_remove(nssPointerTracker *tracker, const void *pointer) +{ + PRBool registered; + + if ((nssPointerTracker *)NULL == tracker) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FAILURE; + } + + if ((PZLock *)NULL == tracker->lock) { + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + PZ_Lock(tracker->lock); + + if ((PLHashTable *)NULL == tracker->table) { + PZ_Unlock(tracker->lock); + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + registered = PL_HashTableRemove(tracker->table, pointer); + PZ_Unlock(tracker->lock); + + if (!registered) { + nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +/* + * nssPointerTracker_verify + * + * This method is only present in debug builds. + * + * This routine verifies that the specified pointer has been registered + * with the nssPointerTracker object. The nssPointerTracker object is + * threadsafe, and this call may be safely called from multiple threads + * simultaneously with the same arguments. This routine returns a + * PRStatus value; if the pointer is registered this will return + * PR_SUCCESS. Otherwise it will set an error on the error stack and + * return PR_FAILURE. Although the error is suitable for leaving on + * the stack, callers may wish to augment the information available by + * placing a more type-specific error on the stack. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_TRACKER_NOT_INITIALIZED + * NSS_ERROR_POINTER_NOT_REGISTERED + * + * Return value: + * PR_SUCCESS + * PR_FAILRUE + */ + +NSS_IMPLEMENT PRStatus +nssPointerTracker_verify(nssPointerTracker *tracker, const void *pointer) +{ + void *check; + + if ((nssPointerTracker *)NULL == tracker) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FAILURE; + } + + if ((PZLock *)NULL == tracker->lock) { + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + PZ_Lock(tracker->lock); + + if ((PLHashTable *)NULL == tracker->table) { + PZ_Unlock(tracker->lock); + nss_SetError(NSS_ERROR_TRACKER_NOT_INITIALIZED); + return PR_FAILURE; + } + + check = PL_HashTableLookup(tracker->table, pointer); + PZ_Unlock(tracker->lock); + + if ((void *)NULL == check) { + nss_SetError(NSS_ERROR_POINTER_NOT_REGISTERED); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +#endif /* DEBUG */ diff --git a/security/nss/lib/base/utf8.c b/security/nss/lib/base/utf8.c new file mode 100644 index 0000000000..885a27afa6 --- /dev/null +++ b/security/nss/lib/base/utf8.c @@ -0,0 +1,702 @@ +/* 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/. */ + +/* + * utf8.c + * + * This file contains some additional utility routines required for + * handling UTF8 strings. + */ + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +#include "plstr.h" + +/* + * NOTES: + * + * There's an "is hex string" function in pki1/atav.c. If we need + * it in more places, pull that one out. + */ + +/* + * nssUTF8_CaseIgnoreMatch + * + * Returns true if the two UTF8-encoded strings pointed to by the + * two specified NSSUTF8 pointers differ only in typcase. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_TRUE if the strings match, ignoring case + * PR_FALSE if they don't + * PR_FALSE upon error + */ + +NSS_IMPLEMENT PRBool +nssUTF8_CaseIgnoreMatch(const NSSUTF8 *a, const NSSUTF8 *b, PRStatus *statusOpt) +{ +#ifdef NSSDEBUG + if (((const NSSUTF8 *)NULL == a) || ((const NSSUTF8 *)NULL == b)) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return PR_FALSE; + } +#endif /* NSSDEBUG */ + + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + /* + * XXX fgmr + * + * This is, like, so wrong! + */ + if (0 == PL_strcasecmp((const char *)a, (const char *)b)) { + return PR_TRUE; + } else { + return PR_FALSE; + } +} + +/* + * nssUTF8_PrintableMatch + * + * Returns true if the two Printable strings pointed to by the + * two specified NSSUTF8 pointers match when compared with the + * rules for Printable String (leading and trailing spaces are + * disregarded, extents of whitespace match irregardless of length, + * and case is not significant), then PR_TRUE will be returned. + * Otherwise, PR_FALSE will be returned. Upon failure, PR_FALSE + * will be returned. If the optional statusOpt argument is not + * NULL, then PR_SUCCESS or PR_FAILURE will be stored in that + * location. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * + * Return value: + * PR_TRUE if the strings match, ignoring case + * PR_FALSE if they don't + * PR_FALSE upon error + */ + +NSS_IMPLEMENT PRBool +nssUTF8_PrintableMatch(const NSSUTF8 *a, const NSSUTF8 *b, PRStatus *statusOpt) +{ + PRUint8 *c; + PRUint8 *d; + +#ifdef NSSDEBUG + if (((const NSSUTF8 *)NULL == a) || ((const NSSUTF8 *)NULL == b)) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return PR_FALSE; + } +#endif /* NSSDEBUG */ + + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + c = (PRUint8 *)a; + d = (PRUint8 *)b; + + while (' ' == *c) { + c++; + } + + while (' ' == *d) { + d++; + } + + while (('\0' != *c) && ('\0' != *d)) { + PRUint8 e, f; + + e = *c; + f = *d; + + if (('a' <= e) && (e <= 'z')) { + e -= ('a' - 'A'); + } + + if (('a' <= f) && (f <= 'z')) { + f -= ('a' - 'A'); + } + + if (e != f) { + return PR_FALSE; + } + + c++; + d++; + + if (' ' == *c) { + while (' ' == *c) { + c++; + } + c--; + } + + if (' ' == *d) { + while (' ' == *d) { + d++; + } + d--; + } + } + + while (' ' == *c) { + c++; + } + + while (' ' == *d) { + d++; + } + + if (*c == *d) { + /* And both '\0', btw */ + return PR_TRUE; + } else { + return PR_FALSE; + } +} + +/* + * nssUTF8_Duplicate + * + * This routine duplicates the UTF8-encoded string pointed to by the + * specified NSSUTF8 pointer. If the optional arenaOpt argument is + * not null, the memory required will be obtained from that arena; + * otherwise, the memory required will be obtained from the heap. + * A pointer to the new string will be returned. In case of error, + * an error will be placed on the error stack and NULL will be + * returned. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_INVALID_ARENA + * NSS_ERROR_NO_MEMORY + */ + +NSS_IMPLEMENT NSSUTF8 * +nssUTF8_Duplicate(const NSSUTF8 *s, NSSArena *arenaOpt) +{ + NSSUTF8 *rv; + PRUint32 len; + +#ifdef NSSDEBUG + if ((const NSSUTF8 *)NULL == s) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (NSSUTF8 *)NULL; + } + + if ((NSSArena *)NULL != arenaOpt) { + if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + return (NSSUTF8 *)NULL; + } + } +#endif /* NSSDEBUG */ + + len = PL_strlen((const char *)s); +#ifdef PEDANTIC + if ('\0' != ((const char *)s)[len]) { + /* must have wrapped, e.g., too big for PRUint32 */ + nss_SetError(NSS_ERROR_NO_MEMORY); + return (NSSUTF8 *)NULL; + } +#endif /* PEDANTIC */ + len++; /* zero termination */ + + rv = nss_ZAlloc(arenaOpt, len); + if ((void *)NULL == rv) { + return (NSSUTF8 *)NULL; + } + + (void)nsslibc_memcpy(rv, s, len); + return rv; +} + +/* + * nssUTF8_Size + * + * This routine returns the length in bytes (including the terminating + * null) of the UTF8-encoded string pointed to by the specified + * NSSUTF8 pointer. Zero is returned on error. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_VALUE_TOO_LARGE + * + * Return value: + * 0 on error + * nonzero length of the string. + */ + +NSS_IMPLEMENT PRUint32 +nssUTF8_Size(const NSSUTF8 *s, PRStatus *statusOpt) +{ + PRUint32 sv; + +#ifdef NSSDEBUG + if ((const NSSUTF8 *)NULL == s) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return 0; + } +#endif /* NSSDEBUG */ + + sv = PL_strlen((const char *)s) + 1; +#ifdef PEDANTIC + if ('\0' != ((const char *)s)[sv - 1]) { + /* wrapped */ + nss_SetError(NSS_ERROR_VALUE_TOO_LARGE); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return 0; + } +#endif /* PEDANTIC */ + + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + return sv; +} + +/* + * nssUTF8_Length + * + * This routine returns the length in characters (not including the + * terminating null) of the UTF8-encoded string pointed to by the + * specified NSSUTF8 pointer. + * + * The error may be one of the following values: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_VALUE_TOO_LARGE + * NSS_ERROR_INVALID_STRING + * + * Return value: + * length of the string (which may be zero) + * 0 on error + */ + +NSS_IMPLEMENT PRUint32 +nssUTF8_Length(const NSSUTF8 *s, PRStatus *statusOpt) +{ + PRUint32 l = 0; + const PRUint8 *c = (const PRUint8 *)s; + +#ifdef NSSDEBUG + if ((const NSSUTF8 *)NULL == s) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + goto loser; + } +#endif /* NSSDEBUG */ + + /* + * From RFC 3629: + * + * UTF8-octets = *( UTF8-char ) + * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 + * UTF8-1 = %x00-7F + * UTF8-2 = %xC2-DF UTF8-tail + * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / + * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) + * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / + * %xF4 %x80-8F 2( UTF8-tail ) + * UTF8-tail = %x80-BF + */ + + while (0 != *c) { + PRUint32 incr; + if (*c < 0x80) { + incr = 1; + } else if (*c < 0xC2) { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } else if (*c < 0xE0) { + incr = 2; + } else if (*c == 0xE0) { + if (c[1] < 0xA0) { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } + incr = 3; + } else if (*c < 0xF0) { + if (*c == 0xED && c[1] > 0x9F) { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } + incr = 3; + } else if (*c == 0xF0) { + if (c[1] < 0x90) { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } + incr = 4; + } else if (*c < 0xF4) { + incr = 4; + } else if (*c == 0xF4) { + if (c[1] > 0x8F) { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } + incr = 4; + } else { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } + + l += incr; + +#ifdef PEDANTIC + if (l < incr) { + /* Wrapped-- too big */ + nss_SetError(NSS_ERROR_VALUE_TOO_LARGE); + goto loser; + } +#endif /* PEDANTIC */ + + { + const PRUint8 *d; + for (d = &c[1]; d < &c[incr]; d++) { + if ((*d & 0xC0) != 0x80) { + nss_SetError(NSS_ERROR_INVALID_STRING); + goto loser; + } + } + } + + c += incr; + } + + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_SUCCESS; + } + + return l; + +loser: + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + + return 0; +} + +/* + * nssUTF8_Create + * + * This routine creates a UTF8 string from a string in some other + * format. Some types of string may include embedded null characters, + * so for them the length parameter must be used. For string types + * that are null-terminated, the length parameter is optional; if it + * is zero, it will be ignored. If the optional arena argument is + * non-null, the memory used for the new string will be obtained from + * that arena, otherwise it will be obtained from the heap. This + * routine may return NULL upon error, in which case it will have + * placed an error on the error stack. + * + * The error may be one of the following: + * NSS_ERROR_INVALID_POINTER + * NSS_ERROR_NO_MEMORY + * NSS_ERROR_UNSUPPORTED_TYPE + * + * Return value: + * NULL upon error + * A non-null pointer to a new UTF8 string otherwise + */ + +extern const NSSError NSS_ERROR_INTERNAL_ERROR; /* XXX fgmr */ + +NSS_IMPLEMENT NSSUTF8 * +nssUTF8_Create(NSSArena *arenaOpt, nssStringType type, const void *inputString, + PRUint32 size /* in bytes, not characters */ +) +{ + NSSUTF8 *rv = NULL; + +#ifdef NSSDEBUG + if ((NSSArena *)NULL != arenaOpt) { + if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + return (NSSUTF8 *)NULL; + } + } + + if ((const void *)NULL == inputString) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (NSSUTF8 *)NULL; + } +#endif /* NSSDEBUG */ + + switch (type) { + case nssStringType_DirectoryString: + /* This is a composite type requiring BER */ + nss_SetError(NSS_ERROR_UNSUPPORTED_TYPE); + break; + case nssStringType_TeletexString: + /* + * draft-ietf-pkix-ipki-part1-11 says in part: + * + * In addition, many legacy implementations support names encoded + * in the ISO 8859-1 character set (Latin1String) but tag them as + * TeletexString. The Latin1String includes characters used in + * Western European countries which are not part of the + * TeletexString charcter set. Implementations that process + * TeletexString SHOULD be prepared to handle the entire ISO + * 8859-1 character set.[ISO 8859-1]. + */ + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_PrintableString: + /* + * PrintableString consists of A-Za-z0-9 ,()+,-./:=? + * This is a subset of ASCII, which is a subset of UTF8. + * So we can just duplicate the string over. + */ + + if (0 == size) { + rv = nssUTF8_Duplicate((const NSSUTF8 *)inputString, arenaOpt); + } else { + rv = nss_ZAlloc(arenaOpt, size + 1); + if ((NSSUTF8 *)NULL == rv) { + return (NSSUTF8 *)NULL; + } + + (void)nsslibc_memcpy(rv, inputString, size); + } + + break; + case nssStringType_UniversalString: + /* 4-byte unicode */ + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_BMPString: + /* Base Multilingual Plane of Unicode */ + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_UTF8String: + if (0 == size) { + rv = nssUTF8_Duplicate((const NSSUTF8 *)inputString, arenaOpt); + } else { + rv = nss_ZAlloc(arenaOpt, size + 1); + if ((NSSUTF8 *)NULL == rv) { + return (NSSUTF8 *)NULL; + } + + (void)nsslibc_memcpy(rv, inputString, size); + } + + break; + case nssStringType_PHGString: + /* + * PHGString is an IA5String (with case-insensitive comparisons). + * IA5 is ~almost~ ascii; ascii has dollar-sign where IA5 has + * currency symbol. + */ + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_GeneralString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + default: + nss_SetError(NSS_ERROR_UNSUPPORTED_TYPE); + break; + } + + return rv; +} + +NSS_IMPLEMENT NSSItem * +nssUTF8_GetEncoding(NSSArena *arenaOpt, NSSItem *rvOpt, nssStringType type, + NSSUTF8 *string) +{ + NSSItem *rv = (NSSItem *)NULL; + PRStatus status = PR_SUCCESS; + +#ifdef NSSDEBUG + if ((NSSArena *)NULL != arenaOpt) { + if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) { + return (NSSItem *)NULL; + } + } + + if ((NSSUTF8 *)NULL == string) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return (NSSItem *)NULL; + } +#endif /* NSSDEBUG */ + + switch (type) { + case nssStringType_DirectoryString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_TeletexString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_PrintableString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_UniversalString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_BMPString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + case nssStringType_UTF8String: { + NSSUTF8 *dup = nssUTF8_Duplicate(string, arenaOpt); + if ((NSSUTF8 *)NULL == dup) { + return (NSSItem *)NULL; + } + + if ((NSSItem *)NULL == rvOpt) { + rv = nss_ZNEW(arenaOpt, NSSItem); + if ((NSSItem *)NULL == rv) { + (void)nss_ZFreeIf(dup); + return (NSSItem *)NULL; + } + } else { + rv = rvOpt; + } + + rv->data = dup; + dup = (NSSUTF8 *)NULL; + rv->size = nssUTF8_Size(rv->data, &status); + if ((0 == rv->size) && (PR_SUCCESS != status)) { + if ((NSSItem *)NULL == rvOpt) { + (void)nss_ZFreeIf(rv); + } + return (NSSItem *)NULL; + } + } break; + case nssStringType_PHGString: + nss_SetError(NSS_ERROR_INTERNAL_ERROR); /* unimplemented */ + break; + default: + nss_SetError(NSS_ERROR_UNSUPPORTED_TYPE); + break; + } + + return rv; +} + +/* + * nssUTF8_CopyIntoFixedBuffer + * + * This will copy a UTF8 string into a fixed-length buffer, making + * sure that the all characters are valid. Any remaining space will + * be padded with the specified ASCII character, typically either + * null or space. + * + * Blah, blah, blah. + */ + +NSS_IMPLEMENT PRStatus +nssUTF8_CopyIntoFixedBuffer(NSSUTF8 *string, char *buffer, PRUint32 bufferSize, + char pad) +{ + PRUint32 stringSize = 0; + +#ifdef NSSDEBUG + if ((char *)NULL == buffer) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + return PR_FALSE; + } + + if (0 == bufferSize) { + nss_SetError(NSS_ERROR_INVALID_ARGUMENT); + return PR_FALSE; + } + + if ((pad & 0x80) != 0x00) { + nss_SetError(NSS_ERROR_INVALID_ARGUMENT); + return PR_FALSE; + } +#endif /* NSSDEBUG */ + + if ((NSSUTF8 *)NULL == string) { + string = (NSSUTF8 *)""; + } + + stringSize = nssUTF8_Size(string, (PRStatus *)NULL); + stringSize--; /* don't count the trailing null */ + if (stringSize > bufferSize) { + PRUint32 bs = bufferSize; + (void)nsslibc_memcpy(buffer, string, bufferSize); + + if ((((buffer[bs - 1] & 0x80) == 0x00)) || + ((bs > 1) && ((buffer[bs - 2] & 0xE0) == 0xC0)) || + ((bs > 2) && ((buffer[bs - 3] & 0xF0) == 0xE0)) || + ((bs > 3) && ((buffer[bs - 4] & 0xF8) == 0xF0)) || + ((bs > 4) && ((buffer[bs - 5] & 0xFC) == 0xF8)) || + ((bs > 5) && ((buffer[bs - 6] & 0xFE) == 0xFC))) { + /* It fit exactly */ + return PR_SUCCESS; + } + + /* Too long. We have to trim the last character */ + for (/*bs*/; bs != 0; bs--) { + if ((buffer[bs - 1] & 0xC0) != 0x80) { + buffer[bs - 1] = pad; + break; + } else { + buffer[bs - 1] = pad; + } + } + } else { + (void)nsslibc_memset(buffer, pad, bufferSize); + (void)nsslibc_memcpy(buffer, string, stringSize); + } + + return PR_SUCCESS; +} + +/* + * nssUTF8_Equal + * + */ + +NSS_IMPLEMENT PRBool +nssUTF8_Equal(const NSSUTF8 *a, const NSSUTF8 *b, PRStatus *statusOpt) +{ + PRUint32 la, lb; + +#ifdef NSSDEBUG + if (((const NSSUTF8 *)NULL == a) || ((const NSSUTF8 *)NULL == b)) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + if ((PRStatus *)NULL != statusOpt) { + *statusOpt = PR_FAILURE; + } + return PR_FALSE; + } +#endif /* NSSDEBUG */ + + la = nssUTF8_Size(a, statusOpt); + if (0 == la) { + return PR_FALSE; + } + + lb = nssUTF8_Size(b, statusOpt); + if (0 == lb) { + return PR_FALSE; + } + + if (la != lb) { + return PR_FALSE; + } + + return nsslibc_memequal(a, b, la, statusOpt); +} |