tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

error.c (7714B)


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