summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/base/error.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/base/error.c')
-rw-r--r--security/nss/lib/base/error.c301
1 files changed, 301 insertions, 0 deletions
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;
+}