security/nss/lib/base/error.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /*
michael@0 6 * error.c
michael@0 7 *
michael@0 8 * This file contains the code implementing the per-thread error
michael@0 9 * stacks upon which most NSS routines report their errors.
michael@0 10 */
michael@0 11
michael@0 12 #ifndef BASE_H
michael@0 13 #include "base.h"
michael@0 14 #endif /* BASE_H */
michael@0 15 #include <limits.h> /* for UINT_MAX */
michael@0 16 #include <string.h> /* for memmove */
michael@0 17
michael@0 18 #define NSS_MAX_ERROR_STACK_COUNT 16 /* error codes */
michael@0 19
michael@0 20 /*
michael@0 21 * The stack itself has a header, and a sequence of integers.
michael@0 22 * The header records the amount of space (as measured in stack
michael@0 23 * slots) already allocated for the stack, and the count of the
michael@0 24 * number of records currently being used.
michael@0 25 */
michael@0 26
michael@0 27 struct stack_header_str {
michael@0 28 PRUint16 space;
michael@0 29 PRUint16 count;
michael@0 30 };
michael@0 31
michael@0 32 struct error_stack_str {
michael@0 33 struct stack_header_str header;
michael@0 34 PRInt32 stack[1];
michael@0 35 };
michael@0 36 typedef struct error_stack_str error_stack;
michael@0 37
michael@0 38 /*
michael@0 39 * error_stack_index
michael@0 40 *
michael@0 41 * Thread-private data must be indexed. This is that index.
michael@0 42 * See PR_NewThreadPrivateIndex for more information.
michael@0 43 *
michael@0 44 * Thread-private data indexes are in the range [0, 127].
michael@0 45 */
michael@0 46
michael@0 47 #define INVALID_TPD_INDEX UINT_MAX
michael@0 48 static PRUintn error_stack_index = INVALID_TPD_INDEX;
michael@0 49
michael@0 50 /*
michael@0 51 * call_once
michael@0 52 *
michael@0 53 * The thread-private index must be obtained (once!) at runtime.
michael@0 54 * This block is used for that one-time call.
michael@0 55 */
michael@0 56
michael@0 57 static PRCallOnceType error_call_once;
michael@0 58
michael@0 59 /*
michael@0 60 * error_once_function
michael@0 61 *
michael@0 62 * This is the once-called callback.
michael@0 63 */
michael@0 64 static PRStatus
michael@0 65 error_once_function ( void)
michael@0 66 {
michael@0 67 return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free);
michael@0 68 }
michael@0 69
michael@0 70 /*
michael@0 71 * error_get_my_stack
michael@0 72 *
michael@0 73 * This routine returns the calling thread's error stack, creating
michael@0 74 * it if necessary. It may return NULL upon error, which implicitly
michael@0 75 * means that it ran out of memory.
michael@0 76 */
michael@0 77
michael@0 78 static error_stack *
michael@0 79 error_get_my_stack ( void)
michael@0 80 {
michael@0 81 PRStatus st;
michael@0 82 error_stack *rv;
michael@0 83 PRUintn new_size;
michael@0 84 PRUint32 new_bytes;
michael@0 85 error_stack *new_stack;
michael@0 86
michael@0 87 if( INVALID_TPD_INDEX == error_stack_index ) {
michael@0 88 st = PR_CallOnce(&error_call_once, error_once_function);
michael@0 89 if( PR_SUCCESS != st ) {
michael@0 90 return (error_stack *)NULL;
michael@0 91 }
michael@0 92 }
michael@0 93
michael@0 94 rv = (error_stack *)PR_GetThreadPrivate(error_stack_index);
michael@0 95 if( (error_stack *)NULL == rv ) {
michael@0 96 /* Doesn't exist; create one */
michael@0 97 new_size = 16;
michael@0 98 } else if( rv->header.count == rv->header.space &&
michael@0 99 rv->header.count < NSS_MAX_ERROR_STACK_COUNT ) {
michael@0 100 /* Too small, expand it */
michael@0 101 new_size = PR_MIN( rv->header.space * 2, NSS_MAX_ERROR_STACK_COUNT);
michael@0 102 } else {
michael@0 103 /* Okay, return it */
michael@0 104 return rv;
michael@0 105 }
michael@0 106
michael@0 107 new_bytes = (new_size * sizeof(PRInt32)) + sizeof(error_stack);
michael@0 108 /* Use NSPR's calloc/realloc, not NSS's, to avoid loops! */
michael@0 109 new_stack = PR_Calloc(1, new_bytes);
michael@0 110
michael@0 111 if( (error_stack *)NULL != new_stack ) {
michael@0 112 if( (error_stack *)NULL != rv ) {
michael@0 113 (void)nsslibc_memcpy(new_stack,rv,rv->header.space);
michael@0 114 }
michael@0 115 new_stack->header.space = new_size;
michael@0 116 }
michael@0 117
michael@0 118 /* Set the value, whether or not the allocation worked */
michael@0 119 PR_SetThreadPrivate(error_stack_index, new_stack);
michael@0 120 return new_stack;
michael@0 121 }
michael@0 122
michael@0 123 /*
michael@0 124 * The error stack
michael@0 125 *
michael@0 126 * The public methods relating to the error stack are:
michael@0 127 *
michael@0 128 * NSS_GetError
michael@0 129 * NSS_GetErrorStack
michael@0 130 *
michael@0 131 * The nonpublic methods relating to the error stack are:
michael@0 132 *
michael@0 133 * nss_SetError
michael@0 134 * nss_ClearErrorStack
michael@0 135 *
michael@0 136 */
michael@0 137
michael@0 138 /*
michael@0 139 * NSS_GetError
michael@0 140 *
michael@0 141 * This routine returns the highest-level (most general) error set
michael@0 142 * by the most recent NSS library routine called by the same thread
michael@0 143 * calling this routine.
michael@0 144 *
michael@0 145 * This routine cannot fail. However, it may return zero, which
michael@0 146 * indicates that the previous NSS library call did not set an error.
michael@0 147 *
michael@0 148 * Return value:
michael@0 149 * 0 if no error has been set
michael@0 150 * A nonzero error number
michael@0 151 */
michael@0 152
michael@0 153 NSS_IMPLEMENT PRInt32
michael@0 154 NSS_GetError ( void)
michael@0 155 {
michael@0 156 error_stack *es = error_get_my_stack();
michael@0 157
michael@0 158 if( (error_stack *)NULL == es ) {
michael@0 159 return NSS_ERROR_NO_MEMORY; /* Good guess! */
michael@0 160 }
michael@0 161
michael@0 162 if( 0 == es->header.count ) {
michael@0 163 return 0;
michael@0 164 }
michael@0 165
michael@0 166 return es->stack[ es->header.count-1 ];
michael@0 167 }
michael@0 168
michael@0 169 /*
michael@0 170 * NSS_GetErrorStack
michael@0 171 *
michael@0 172 * This routine returns a pointer to an array of integers, containing
michael@0 173 * the entire sequence or "stack" of errors set by the most recent NSS
michael@0 174 * library routine called by the same thread calling this routine.
michael@0 175 * NOTE: the caller DOES NOT OWN the memory pointed to by the return
michael@0 176 * value. The pointer will remain valid until the calling thread
michael@0 177 * calls another NSS routine. The lowest-level (most specific) error
michael@0 178 * is first in the array, and the highest-level is last. The array is
michael@0 179 * zero-terminated. This routine may return NULL upon error; this
michael@0 180 * indicates a low-memory situation.
michael@0 181 *
michael@0 182 * Return value:
michael@0 183 * NULL upon error, which is an implied NSS_ERROR_NO_MEMORY
michael@0 184 * A NON-caller-owned pointer to an array of integers
michael@0 185 */
michael@0 186
michael@0 187 NSS_IMPLEMENT PRInt32 *
michael@0 188 NSS_GetErrorStack ( void)
michael@0 189 {
michael@0 190 error_stack *es = error_get_my_stack();
michael@0 191
michael@0 192 if( (error_stack *)NULL == es ) {
michael@0 193 return (PRInt32 *)NULL;
michael@0 194 }
michael@0 195
michael@0 196 /* Make sure it's terminated */
michael@0 197 es->stack[ es->header.count ] = 0;
michael@0 198
michael@0 199 return es->stack;
michael@0 200 }
michael@0 201
michael@0 202 /*
michael@0 203 * nss_SetError
michael@0 204 *
michael@0 205 * This routine places a new error code on the top of the calling
michael@0 206 * thread's error stack. Calling this routine wiht an error code
michael@0 207 * of zero will clear the error stack.
michael@0 208 */
michael@0 209
michael@0 210 NSS_IMPLEMENT void
michael@0 211 nss_SetError ( PRUint32 error)
michael@0 212 {
michael@0 213 error_stack *es;
michael@0 214
michael@0 215 if( 0 == error ) {
michael@0 216 nss_ClearErrorStack();
michael@0 217 return;
michael@0 218 }
michael@0 219
michael@0 220 es = error_get_my_stack();
michael@0 221 if( (error_stack *)NULL == es ) {
michael@0 222 /* Oh, well. */
michael@0 223 return;
michael@0 224 }
michael@0 225
michael@0 226 if (es->header.count < es->header.space) {
michael@0 227 es->stack[ es->header.count++ ] = error;
michael@0 228 } else {
michael@0 229 memmove(es->stack, es->stack + 1,
michael@0 230 (es->header.space - 1) * (sizeof es->stack[0]));
michael@0 231 es->stack[ es->header.space - 1 ] = error;
michael@0 232 }
michael@0 233 return;
michael@0 234 }
michael@0 235
michael@0 236 /*
michael@0 237 * nss_ClearErrorStack
michael@0 238 *
michael@0 239 * This routine clears the calling thread's error stack.
michael@0 240 */
michael@0 241
michael@0 242 NSS_IMPLEMENT void
michael@0 243 nss_ClearErrorStack ( void)
michael@0 244 {
michael@0 245 error_stack *es = error_get_my_stack();
michael@0 246 if( (error_stack *)NULL == es ) {
michael@0 247 /* Oh, well. */
michael@0 248 return;
michael@0 249 }
michael@0 250
michael@0 251 es->header.count = 0;
michael@0 252 es->stack[0] = 0;
michael@0 253 return;
michael@0 254 }
michael@0 255
michael@0 256 /*
michael@0 257 * nss_DestroyErrorStack
michael@0 258 *
michael@0 259 * This routine frees the calling thread's error stack.
michael@0 260 */
michael@0 261
michael@0 262 NSS_IMPLEMENT void
michael@0 263 nss_DestroyErrorStack ( void)
michael@0 264 {
michael@0 265 if( INVALID_TPD_INDEX != error_stack_index ) {
michael@0 266 PR_SetThreadPrivate(error_stack_index, NULL);
michael@0 267 }
michael@0 268 return;
michael@0 269 }

mercurial