tor-browser

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

arena.c (31222B)


      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 * arena.c
      7 *
      8 * This contains the implementation of NSS's thread-safe arenas.
      9 */
     10 
     11 #ifndef BASE_H
     12 #include "base.h"
     13 #endif /* BASE_H */
     14 
     15 #ifdef ARENA_THREADMARK
     16 #include "prthread.h"
     17 #endif /* ARENA_THREADMARK */
     18 
     19 #include "prlock.h"
     20 #include "plarena.h"
     21 
     22 #include <string.h>
     23 
     24 /*
     25 * NSSArena
     26 *
     27 * This is based on NSPR's arena code, but it is threadsafe.
     28 *
     29 * The public methods relating to this type are:
     30 *
     31 *  NSSArena_Create  -- constructor
     32 *  NSSArena_Destroy
     33 *  NSS_ZAlloc
     34 *  NSS_ZRealloc
     35 *  NSS_ZFreeIf
     36 *
     37 * The nonpublic methods relating to this type are:
     38 *
     39 *  nssArena_Create  -- constructor
     40 *  nssArena_Destroy
     41 *  nssArena_Mark
     42 *  nssArena_Release
     43 *  nssArena_Unmark
     44 *
     45 *  nss_ZAlloc
     46 *  nss_ZFreeIf
     47 *  nss_ZRealloc
     48 *
     49 * In debug builds, the following calls are available:
     50 *
     51 *  nssArena_verifyPointer
     52 *  nssArena_registerDestructor
     53 *  nssArena_deregisterDestructor
     54 */
     55 
     56 struct NSSArenaStr {
     57    PLArenaPool pool;
     58    PRLock *lock;
     59 #ifdef ARENA_THREADMARK
     60    PRThread *marking_thread;
     61    nssArenaMark *first_mark;
     62    nssArenaMark *last_mark;
     63 #endif /* ARENA_THREADMARK */
     64 #ifdef ARENA_DESTRUCTOR_LIST
     65    struct arena_destructor_node *first_destructor;
     66    struct arena_destructor_node *last_destructor;
     67 #endif /* ARENA_DESTRUCTOR_LIST */
     68 };
     69 
     70 /*
     71 * nssArenaMark
     72 *
     73 * This type is used to mark the current state of an NSSArena.
     74 */
     75 
     76 struct nssArenaMarkStr {
     77    PRUint32 magic;
     78    void *mark;
     79 #ifdef ARENA_THREADMARK
     80    nssArenaMark *next;
     81 #endif /* ARENA_THREADMARK */
     82 #ifdef ARENA_DESTRUCTOR_LIST
     83    struct arena_destructor_node *next_destructor;
     84    struct arena_destructor_node *prev_destructor;
     85 #endif /* ARENA_DESTRUCTOR_LIST */
     86 };
     87 
     88 #define MARK_MAGIC 0x4d41524b /* "MARK" how original */
     89 
     90 /*
     91 * But first, the pointer-tracking code
     92 */
     93 #ifdef DEBUG
     94 extern const NSSError NSS_ERROR_INTERNAL_ERROR;
     95 
     96 static nssPointerTracker arena_pointer_tracker;
     97 
     98 static PRStatus
     99 arena_add_pointer(const NSSArena *arena)
    100 {
    101    PRStatus rv;
    102 
    103    rv = nssPointerTracker_initialize(&arena_pointer_tracker);
    104    if (PR_SUCCESS != rv) {
    105        return rv;
    106    }
    107 
    108    rv = nssPointerTracker_add(&arena_pointer_tracker, arena);
    109    if (PR_SUCCESS != rv) {
    110        NSSError e = NSS_GetError();
    111        if (NSS_ERROR_NO_MEMORY != e) {
    112            nss_SetError(NSS_ERROR_INTERNAL_ERROR);
    113        }
    114 
    115        return rv;
    116    }
    117 
    118    return PR_SUCCESS;
    119 }
    120 
    121 static PRStatus
    122 arena_remove_pointer(const NSSArena *arena)
    123 {
    124    PRStatus rv;
    125 
    126    rv = nssPointerTracker_remove(&arena_pointer_tracker, arena);
    127    if (PR_SUCCESS != rv) {
    128        nss_SetError(NSS_ERROR_INTERNAL_ERROR);
    129    }
    130 
    131    return rv;
    132 }
    133 
    134 /*
    135 * nssArena_verifyPointer
    136 *
    137 * This method is only present in debug builds.
    138 *
    139 * If the specified pointer is a valid pointer to an NSSArena object,
    140 * this routine will return PR_SUCCESS.  Otherwise, it will put an
    141 * error on the error stack and return PR_FAILURE.
    142 *
    143 * The error may be one of the following values:
    144 *  NSS_ERROR_INVALID_ARENA
    145 *
    146 * Return value:
    147 *  PR_SUCCESS if the pointer is valid
    148 *  PR_FAILURE if it isn't
    149 */
    150 
    151 NSS_IMPLEMENT PRStatus
    152 nssArena_verifyPointer(const NSSArena *arena)
    153 {
    154    PRStatus rv;
    155 
    156    rv = nssPointerTracker_initialize(&arena_pointer_tracker);
    157    if (PR_SUCCESS != rv) {
    158        /*
    159         * This is a little disingenious.  We have to initialize the
    160         * tracker, because someone could "legitimately" try to verify
    161         * an arena pointer before one is ever created.  And this step
    162         * might fail, due to lack of memory.  But the only way that
    163         * this step can fail is if it's doing the call_once stuff,
    164         * (later calls just no-op).  And if it didn't no-op, there
    165         * aren't any valid arenas.. so the argument certainly isn't one.
    166         */
    167        nss_SetError(NSS_ERROR_INVALID_ARENA);
    168        return PR_FAILURE;
    169    }
    170 
    171    rv = nssPointerTracker_verify(&arena_pointer_tracker, arena);
    172    if (PR_SUCCESS != rv) {
    173        nss_SetError(NSS_ERROR_INVALID_ARENA);
    174        return PR_FAILURE;
    175    }
    176 
    177    return PR_SUCCESS;
    178 }
    179 #endif /* DEBUG */
    180 
    181 #ifdef ARENA_DESTRUCTOR_LIST
    182 
    183 struct arena_destructor_node {
    184    struct arena_destructor_node *next;
    185    struct arena_destructor_node *prev;
    186    void (*destructor)(void *argument);
    187    void *arg;
    188 };
    189 
    190 /*
    191 * nssArena_registerDestructor
    192 *
    193 * This routine stores a pointer to a callback and an arbitrary
    194 * pointer-sized argument in the arena, at the current point in
    195 * the mark stack.  If the arena is destroyed, or an "earlier"
    196 * mark is released, then this destructor will be called at that
    197 * time.  Note that the destructor will be called with the arena
    198 * locked, which means the destructor may free memory in that
    199 * arena, but it may not allocate or cause to be allocated any
    200 * memory.  This callback facility was included to support our
    201 * debug-version pointer-tracker feature; overuse runs counter to
    202 * the the original intent of arenas.  This routine returns a
    203 * PRStatus value; if successful, it will return PR_SUCCESS.  If
    204 * unsuccessful, it will set an error on the error stack and
    205 * return PR_FAILURE.
    206 *
    207 * The error may be one of the following values:
    208 *  NSS_ERROR_INVALID_ARENA
    209 *  NSS_ERROR_NO_MEMORY
    210 *
    211 * Return value:
    212 *  PR_SUCCESS
    213 *  PR_FAILURE
    214 */
    215 
    216 NSS_IMPLEMENT PRStatus
    217 nssArena_registerDestructor(NSSArena *arena, void (*destructor)(void *argument),
    218                            void *arg)
    219 {
    220    struct arena_destructor_node *it;
    221 
    222 #ifdef NSSDEBUG
    223    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
    224        return PR_FAILURE;
    225    }
    226 #endif /* NSSDEBUG */
    227 
    228    it = nss_ZNEW(arena, struct arena_destructor_node);
    229    if ((struct arena_destructor_node *)NULL == it) {
    230        return PR_FAILURE;
    231    }
    232 
    233    it->prev = arena->last_destructor;
    234    arena->last_destructor->next = it;
    235    arena->last_destructor = it;
    236    it->destructor = destructor;
    237    it->arg = arg;
    238 
    239    if ((nssArenaMark *)NULL != arena->last_mark) {
    240        arena->last_mark->prev_destructor = it->prev;
    241        arena->last_mark->next_destructor = it->next;
    242    }
    243 
    244    return PR_SUCCESS;
    245 }
    246 
    247 NSS_IMPLEMENT PRStatus
    248 nssArena_deregisterDestructor(NSSArena *arena,
    249                              void (*destructor)(void *argument), void *arg)
    250 {
    251    struct arena_destructor_node *it;
    252 
    253 #ifdef NSSDEBUG
    254    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
    255        return PR_FAILURE;
    256    }
    257 #endif /* NSSDEBUG */
    258 
    259    for (it = arena->first_destructor; it; it = it->next) {
    260        if ((it->destructor == destructor) && (it->arg == arg)) {
    261            break;
    262        }
    263    }
    264 
    265    if ((struct arena_destructor_node *)NULL == it) {
    266        nss_SetError(NSS_ERROR_NOT_FOUND);
    267        return PR_FAILURE;
    268    }
    269 
    270    if (it == arena->first_destructor) {
    271        arena->first_destructor = it->next;
    272    }
    273 
    274    if (it == arena->last_destructor) {
    275        arena->last_destructor = it->prev;
    276    }
    277 
    278    if ((struct arena_destructor_node *)NULL != it->prev) {
    279        it->prev->next = it->next;
    280    }
    281 
    282    if ((struct arena_destructor_node *)NULL != it->next) {
    283        it->next->prev = it->prev;
    284    }
    285 
    286    {
    287        nssArenaMark *m;
    288        for (m = arena->first_mark; m; m = m->next) {
    289            if (m->next_destructor == it) {
    290                m->next_destructor = it->next;
    291            }
    292            if (m->prev_destructor == it) {
    293                m->prev_destructor = it->prev;
    294            }
    295        }
    296    }
    297 
    298    nss_ZFreeIf(it);
    299    return PR_SUCCESS;
    300 }
    301 
    302 static void
    303 nss_arena_call_destructor_chain(struct arena_destructor_node *it)
    304 {
    305    for (; it; it = it->next) {
    306        (*(it->destructor))(it->arg);
    307    }
    308 }
    309 
    310 #endif /* ARENA_DESTRUCTOR_LIST */
    311 
    312 /*
    313 * NSSArena_Create
    314 *
    315 * This routine creates a new memory arena.  This routine may return
    316 * NULL upon error, in which case it will have created an error stack.
    317 *
    318 * The top-level error may be one of the following values:
    319 *  NSS_ERROR_NO_MEMORY
    320 *
    321 * Return value:
    322 *  NULL upon error
    323 *  A pointer to an NSSArena upon success
    324 */
    325 
    326 NSS_IMPLEMENT NSSArena *
    327 NSSArena_Create(void)
    328 {
    329    nss_ClearErrorStack();
    330    return nssArena_Create();
    331 }
    332 
    333 /*
    334 * nssArena_Create
    335 *
    336 * This routine creates a new memory arena.  This routine may return
    337 * NULL upon error, in which case it will have set an error on the
    338 * error stack.
    339 *
    340 * The error may be one of the following values:
    341 *  NSS_ERROR_NO_MEMORY
    342 *
    343 * Return value:
    344 *  NULL upon error
    345 *  A pointer to an NSSArena upon success
    346 */
    347 
    348 NSS_IMPLEMENT NSSArena *
    349 nssArena_Create(void)
    350 {
    351    NSSArena *rv = (NSSArena *)NULL;
    352 
    353    rv = nss_ZNEW((NSSArena *)NULL, NSSArena);
    354    if ((NSSArena *)NULL == rv) {
    355        nss_SetError(NSS_ERROR_NO_MEMORY);
    356        return (NSSArena *)NULL;
    357    }
    358 
    359    rv->lock = PR_NewLock();
    360    if ((PRLock *)NULL == rv->lock) {
    361        (void)nss_ZFreeIf(rv);
    362        nss_SetError(NSS_ERROR_NO_MEMORY);
    363        return (NSSArena *)NULL;
    364    }
    365 
    366    /*
    367     * Arena sizes.  The current security code has 229 occurrences of
    368     * PORT_NewArena.  The default chunksizes specified break down as
    369     *
    370     *  Size    Mult.   Specified as
    371     *   512       1    512
    372     *  1024       7    1024
    373     *  2048       5    2048
    374     *  2048       5    CRMF_DEFAULT_ARENA_SIZE
    375     *  2048     190    DER_DEFAULT_CHUNKSIZE
    376     *  2048      20    SEC_ASN1_DEFAULT_ARENA_SIZE
    377     *  4096       1    4096
    378     *
    379     * Obviously this "default chunksize" flexibility isn't very
    380     * useful to us, so I'll just pick 2048.
    381     */
    382 
    383    PL_InitArenaPool(&rv->pool, "NSS", 2048, sizeof(double));
    384 
    385 #ifdef DEBUG
    386    {
    387        PRStatus st;
    388        st = arena_add_pointer(rv);
    389        if (PR_SUCCESS != st) {
    390            PL_FinishArenaPool(&rv->pool);
    391            PR_DestroyLock(rv->lock);
    392            (void)nss_ZFreeIf(rv);
    393            return (NSSArena *)NULL;
    394        }
    395    }
    396 #endif /* DEBUG */
    397 
    398    return rv;
    399 }
    400 
    401 /*
    402 * NSSArena_Destroy
    403 *
    404 * This routine will destroy the specified arena, freeing all memory
    405 * allocated from it.  This routine returns a PRStatus value; if
    406 * successful, it will return PR_SUCCESS.  If unsuccessful, it will
    407 * create an error stack and return PR_FAILURE.
    408 *
    409 * The top-level error may be one of the following values:
    410 *  NSS_ERROR_INVALID_ARENA
    411 *
    412 * Return value:
    413 *  PR_SUCCESS upon success
    414 *  PR_FAILURE upon failure
    415 */
    416 
    417 NSS_IMPLEMENT PRStatus
    418 NSSArena_Destroy(NSSArena *arena)
    419 {
    420    nss_ClearErrorStack();
    421 
    422 #ifdef DEBUG
    423    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
    424        return PR_FAILURE;
    425    }
    426 #endif /* DEBUG */
    427 
    428    return nssArena_Destroy(arena);
    429 }
    430 
    431 /*
    432 * nssArena_Destroy
    433 *
    434 * This routine will destroy the specified arena, freeing all memory
    435 * allocated from it.  This routine returns a PRStatus value; if
    436 * successful, it will return PR_SUCCESS.  If unsuccessful, it will
    437 * set an error on the error stack and return PR_FAILURE.
    438 *
    439 * The error may be one of the following values:
    440 *  NSS_ERROR_INVALID_ARENA
    441 *
    442 * Return value:
    443 *  PR_SUCCESS
    444 *  PR_FAILURE
    445 */
    446 
    447 NSS_IMPLEMENT PRStatus
    448 nssArena_Destroy(NSSArena *arena)
    449 {
    450    PRLock *lock;
    451 
    452 #ifdef NSSDEBUG
    453    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
    454        return PR_FAILURE;
    455    }
    456 #endif /* NSSDEBUG */
    457 
    458    if ((PRLock *)NULL == arena->lock) {
    459        /* Just got destroyed */
    460        nss_SetError(NSS_ERROR_INVALID_ARENA);
    461        return PR_FAILURE;
    462    }
    463    PR_Lock(arena->lock);
    464 
    465 #ifdef DEBUG
    466    if (PR_SUCCESS != arena_remove_pointer(arena)) {
    467        PR_Unlock(arena->lock);
    468        return PR_FAILURE;
    469    }
    470 #endif /* DEBUG */
    471 
    472 #ifdef ARENA_DESTRUCTOR_LIST
    473    /* Note that the arena is locked at this time */
    474    nss_arena_call_destructor_chain(arena->first_destructor);
    475 #endif /* ARENA_DESTRUCTOR_LIST */
    476 
    477    PL_FinishArenaPool(&arena->pool);
    478    lock = arena->lock;
    479    arena->lock = (PRLock *)NULL;
    480    PR_Unlock(lock);
    481    PR_DestroyLock(lock);
    482    (void)nss_ZFreeIf(arena);
    483    return PR_SUCCESS;
    484 }
    485 
    486 static void *nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size);
    487 
    488 /*
    489 * nssArena_Mark
    490 *
    491 * This routine "marks" the current state of an arena.  Space
    492 * allocated after the arena has been marked can be freed by
    493 * releasing the arena back to the mark with nssArena_Release,
    494 * or committed by calling nssArena_Unmark.  When successful,
    495 * this routine returns a valid nssArenaMark pointer.  This
    496 * routine may return NULL upon error, in which case it will
    497 * have set an error on the error stack.
    498 *
    499 * The error may be one of the following values:
    500 *  NSS_ERROR_INVALID_ARENA
    501 *  NSS_ERROR_NO_MEMORY
    502 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
    503 *
    504 * Return value:
    505 *  NULL upon failure
    506 *  An nssArenaMark pointer upon success
    507 */
    508 
    509 NSS_IMPLEMENT nssArenaMark *
    510 nssArena_Mark(NSSArena *arena)
    511 {
    512    nssArenaMark *rv;
    513    void *p;
    514 
    515 #ifdef NSSDEBUG
    516    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
    517        return (nssArenaMark *)NULL;
    518    }
    519 #endif /* NSSDEBUG */
    520 
    521    if ((PRLock *)NULL == arena->lock) {
    522        /* Just got destroyed */
    523        nss_SetError(NSS_ERROR_INVALID_ARENA);
    524        return (nssArenaMark *)NULL;
    525    }
    526    PR_Lock(arena->lock);
    527 
    528 #ifdef ARENA_THREADMARK
    529    if ((PRThread *)NULL == arena->marking_thread) {
    530        /* Unmarked.  Store our thread ID */
    531        arena->marking_thread = PR_GetCurrentThread();
    532        /* This call never fails. */
    533    } else {
    534        /* Marked.  Verify it's the current thread */
    535        if (PR_GetCurrentThread() != arena->marking_thread) {
    536            PR_Unlock(arena->lock);
    537            nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
    538            return (nssArenaMark *)NULL;
    539        }
    540    }
    541 #endif /* ARENA_THREADMARK */
    542 
    543    p = PL_ARENA_MARK(&arena->pool);
    544    /* No error possible */
    545 
    546    /* Do this after the mark */
    547    rv = (nssArenaMark *)nss_zalloc_arena_locked(arena, sizeof(nssArenaMark));
    548    if ((nssArenaMark *)NULL == rv) {
    549        PR_Unlock(arena->lock);
    550        nss_SetError(NSS_ERROR_NO_MEMORY);
    551        return (nssArenaMark *)NULL;
    552    }
    553 
    554 #ifdef ARENA_THREADMARK
    555    if ((nssArenaMark *)NULL == arena->first_mark) {
    556        arena->first_mark = rv;
    557        arena->last_mark = rv;
    558    } else {
    559        arena->last_mark->next = rv;
    560        arena->last_mark = rv;
    561    }
    562 #endif /* ARENA_THREADMARK */
    563 
    564    rv->mark = p;
    565    rv->magic = MARK_MAGIC;
    566 
    567 #ifdef ARENA_DESTRUCTOR_LIST
    568    rv->prev_destructor = arena->last_destructor;
    569 #endif /* ARENA_DESTRUCTOR_LIST */
    570 
    571    PR_Unlock(arena->lock);
    572 
    573    return rv;
    574 }
    575 
    576 /*
    577 * nss_arena_unmark_release
    578 *
    579 * This static routine implements the routines nssArena_Release
    580 * ans nssArena_Unmark, which are almost identical.
    581 */
    582 
    583 static PRStatus
    584 nss_arena_unmark_release(NSSArena *arena, nssArenaMark *arenaMark,
    585                         PRBool release)
    586 {
    587    void *inner_mark;
    588 
    589 #ifdef NSSDEBUG
    590    if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
    591        return PR_FAILURE;
    592    }
    593 #endif /* NSSDEBUG */
    594 
    595    if (MARK_MAGIC != arenaMark->magic) {
    596        nss_SetError(NSS_ERROR_INVALID_ARENA_MARK);
    597        return PR_FAILURE;
    598    }
    599 
    600    if ((PRLock *)NULL == arena->lock) {
    601        /* Just got destroyed */
    602        nss_SetError(NSS_ERROR_INVALID_ARENA);
    603        return PR_FAILURE;
    604    }
    605    PR_Lock(arena->lock);
    606 
    607 #ifdef ARENA_THREADMARK
    608    if ((PRThread *)NULL != arena->marking_thread) {
    609        if (PR_GetCurrentThread() != arena->marking_thread) {
    610            PR_Unlock(arena->lock);
    611            nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
    612            return PR_FAILURE;
    613        }
    614    }
    615 #endif /* ARENA_THREADMARK */
    616 
    617    if (MARK_MAGIC != arenaMark->magic) {
    618        /* Just got released */
    619        PR_Unlock(arena->lock);
    620        nss_SetError(NSS_ERROR_INVALID_ARENA_MARK);
    621        return PR_FAILURE;
    622    }
    623 
    624    arenaMark->magic = 0;
    625    inner_mark = arenaMark->mark;
    626 
    627 #ifdef ARENA_THREADMARK
    628    {
    629        nssArenaMark **pMark = &arena->first_mark;
    630        nssArenaMark *rest;
    631        nssArenaMark *last = (nssArenaMark *)NULL;
    632 
    633        /* Find this mark */
    634        while (*pMark != arenaMark) {
    635            last = *pMark;
    636            pMark = &(*pMark)->next;
    637        }
    638 
    639        /* Remember the pointer, then zero it */
    640        rest = (*pMark)->next;
    641        *pMark = (nssArenaMark *)NULL;
    642 
    643        arena->last_mark = last;
    644 
    645        /* Invalidate any later marks being implicitly released */
    646        for (; (nssArenaMark *)NULL != rest; rest = rest->next) {
    647            rest->magic = 0;
    648        }
    649 
    650        /* If we just got rid of the first mark, clear the thread ID */
    651        if ((nssArenaMark *)NULL == arena->first_mark) {
    652            arena->marking_thread = (PRThread *)NULL;
    653        }
    654    }
    655 #endif /* ARENA_THREADMARK */
    656 
    657    if (release) {
    658 #ifdef ARENA_DESTRUCTOR_LIST
    659        if ((struct arena_destructor_node *)NULL !=
    660            arenaMark->prev_destructor) {
    661            arenaMark->prev_destructor->next =
    662                (struct arena_destructor_node *)NULL;
    663        }
    664        arena->last_destructor = arenaMark->prev_destructor;
    665 
    666        /* Note that the arena is locked at this time */
    667        nss_arena_call_destructor_chain(arenaMark->next_destructor);
    668 #endif /* ARENA_DESTRUCTOR_LIST */
    669 
    670        PL_ARENA_RELEASE(&arena->pool, inner_mark);
    671        /* No error return */
    672    }
    673 
    674    PR_Unlock(arena->lock);
    675    return PR_SUCCESS;
    676 }
    677 
    678 /*
    679 * nssArena_Release
    680 *
    681 * This routine invalidates and releases all memory allocated from
    682 * the specified arena after the point at which the specified mark
    683 * was obtained.  This routine returns a PRStatus value; if successful,
    684 * it will return PR_SUCCESS.  If unsuccessful, it will set an error
    685 * on the error stack and return PR_FAILURE.
    686 *
    687 * The error may be one of the following values:
    688 *  NSS_ERROR_INVALID_ARENA
    689 *  NSS_ERROR_INVALID_ARENA_MARK
    690 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
    691 *
    692 * Return value:
    693 *  PR_SUCCESS
    694 *  PR_FAILURE
    695 */
    696 
    697 NSS_IMPLEMENT PRStatus
    698 nssArena_Release(NSSArena *arena, nssArenaMark *arenaMark)
    699 {
    700    return nss_arena_unmark_release(arena, arenaMark, PR_TRUE);
    701 }
    702 
    703 /*
    704 * nssArena_Unmark
    705 *
    706 * This routine "commits" the indicated mark and any marks after
    707 * it, making them unreleasable.  Note that any earlier marks can
    708 * still be released, and such a release will invalidate these
    709 * later unmarked regions.  If an arena is to be safely shared by
    710 * more than one thread, all marks must be either released or
    711 * unmarked.  This routine returns a PRStatus value; if successful,
    712 * it will return PR_SUCCESS.  If unsuccessful, it will set an error
    713 * on the error stack and return PR_FAILURE.
    714 *
    715 * The error may be one of the following values:
    716 *  NSS_ERROR_INVALID_ARENA
    717 *  NSS_ERROR_INVALID_ARENA_MARK
    718 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
    719 *
    720 * Return value:
    721 *  PR_SUCCESS
    722 *  PR_FAILURE
    723 */
    724 
    725 NSS_IMPLEMENT PRStatus
    726 nssArena_Unmark(NSSArena *arena, nssArenaMark *arenaMark)
    727 {
    728    return nss_arena_unmark_release(arena, arenaMark, PR_FALSE);
    729 }
    730 
    731 /*
    732 * We prefix this header to all allocated blocks.  It is a multiple
    733 * of the alignment size.  Note that this usage of a header may make
    734 * purify spew bogus warnings about "potentially leaked blocks" of
    735 * memory; if that gets too annoying we can add in a pointer to the
    736 * header in the header itself.  There's not a lot of safety here;
    737 * maybe we should add a magic value?
    738 */
    739 struct pointer_header {
    740    NSSArena *arena;
    741    PRUint32 size;
    742 };
    743 
    744 static void *
    745 nss_zalloc_arena_locked(NSSArena *arena, PRUint32 size)
    746 {
    747    void *p;
    748    void *rv;
    749    struct pointer_header *h;
    750    PRUint32 my_size = size + sizeof(struct pointer_header);
    751    PL_ARENA_ALLOCATE(p, &arena->pool, my_size);
    752    if ((void *)NULL == p) {
    753        nss_SetError(NSS_ERROR_NO_MEMORY);
    754        return (void *)NULL;
    755    }
    756    /*
    757     * Do this before we unlock.  This way if the user is using
    758     * an arena in one thread while destroying it in another, he'll
    759     * fault/FMR in his code, not ours.
    760     */
    761    h = (struct pointer_header *)p;
    762    h->arena = arena;
    763    h->size = size;
    764    rv = (void *)((char *)h + sizeof(struct pointer_header));
    765    (void)nsslibc_memset(rv, 0, size);
    766    return rv;
    767 }
    768 
    769 /*
    770 * NSS_ZAlloc
    771 *
    772 * This routine allocates and zeroes a section of memory of the
    773 * size, and returns to the caller a pointer to that memory.  If
    774 * the optional arena argument is non-null, the memory will be
    775 * obtained from that arena; otherwise, the memory will be obtained
    776 * from the heap.  This routine may return NULL upon error, in
    777 * which case it will have set an error upon the error stack.  The
    778 * value specified for size may be zero; in which case a valid
    779 * zero-length block of memory will be allocated.  This block may
    780 * be expanded by calling NSS_ZRealloc.
    781 *
    782 * The error may be one of the following values:
    783 *  NSS_ERROR_INVALID_ARENA
    784 *  NSS_ERROR_NO_MEMORY
    785 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
    786 *
    787 * Return value:
    788 *  NULL upon error
    789 *  A pointer to the new segment of zeroed memory
    790 */
    791 
    792 NSS_IMPLEMENT void *
    793 NSS_ZAlloc(NSSArena *arenaOpt, PRUint32 size)
    794 {
    795    return nss_ZAlloc(arenaOpt, size);
    796 }
    797 
    798 /*
    799 * nss_ZAlloc
    800 *
    801 * This routine allocates and zeroes a section of memory of the
    802 * size, and returns to the caller a pointer to that memory.  If
    803 * the optional arena argument is non-null, the memory will be
    804 * obtained from that arena; otherwise, the memory will be obtained
    805 * from the heap.  This routine may return NULL upon error, in
    806 * which case it will have set an error upon the error stack.  The
    807 * value specified for size may be zero; in which case a valid
    808 * zero-length block of memory will be allocated.  This block may
    809 * be expanded by calling nss_ZRealloc.
    810 *
    811 * The error may be one of the following values:
    812 *  NSS_ERROR_INVALID_ARENA
    813 *  NSS_ERROR_NO_MEMORY
    814 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
    815 *
    816 * Return value:
    817 *  NULL upon error
    818 *  A pointer to the new segment of zeroed memory
    819 */
    820 
    821 NSS_IMPLEMENT void *
    822 nss_ZAlloc(NSSArena *arenaOpt, PRUint32 size)
    823 {
    824    struct pointer_header *h;
    825    PRUint32 my_size = size + sizeof(struct pointer_header);
    826 
    827    if (my_size < sizeof(struct pointer_header)) {
    828        /* Wrapped */
    829        nss_SetError(NSS_ERROR_NO_MEMORY);
    830        return (void *)NULL;
    831    }
    832 
    833    if ((NSSArena *)NULL == arenaOpt) {
    834        /* Heap allocation, no locking required. */
    835        h = (struct pointer_header *)PR_Calloc(1, my_size);
    836        if ((struct pointer_header *)NULL == h) {
    837            nss_SetError(NSS_ERROR_NO_MEMORY);
    838            return (void *)NULL;
    839        }
    840 
    841        h->arena = (NSSArena *)NULL;
    842        h->size = size;
    843        /* We used calloc: it's already zeroed */
    844 
    845        return (void *)((char *)h + sizeof(struct pointer_header));
    846    } else {
    847        void *rv;
    848 /* Arena allocation */
    849 #ifdef NSSDEBUG
    850        if (PR_SUCCESS != nssArena_verifyPointer(arenaOpt)) {
    851            return (void *)NULL;
    852        }
    853 #endif /* NSSDEBUG */
    854 
    855        if ((PRLock *)NULL == arenaOpt->lock) {
    856            /* Just got destroyed */
    857            nss_SetError(NSS_ERROR_INVALID_ARENA);
    858            return (void *)NULL;
    859        }
    860        PR_Lock(arenaOpt->lock);
    861 
    862 #ifdef ARENA_THREADMARK
    863        if ((PRThread *)NULL != arenaOpt->marking_thread) {
    864            if (PR_GetCurrentThread() != arenaOpt->marking_thread) {
    865                nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
    866                PR_Unlock(arenaOpt->lock);
    867                return (void *)NULL;
    868            }
    869        }
    870 #endif /* ARENA_THREADMARK */
    871 
    872        rv = nss_zalloc_arena_locked(arenaOpt, size);
    873 
    874        PR_Unlock(arenaOpt->lock);
    875        return rv;
    876    }
    877    /*NOTREACHED*/
    878 }
    879 
    880 /*
    881 * NSS_ZFreeIf
    882 *
    883 * If the specified pointer is non-null, then the region of memory
    884 * to which it points -- which must have been allocated with
    885 * NSS_ZAlloc -- will be zeroed and released.  This routine
    886 * returns a PRStatus value; if successful, it will return PR_SUCCESS.
    887 * If unsuccessful, it will set an error on the error stack and return
    888 * PR_FAILURE.
    889 *
    890 * The error may be one of the following values:
    891 *  NSS_ERROR_INVALID_POINTER
    892 *
    893 * Return value:
    894 *  PR_SUCCESS
    895 *  PR_FAILURE
    896 */
    897 NSS_IMPLEMENT PRStatus
    898 NSS_ZFreeIf(void *pointer)
    899 {
    900    return nss_ZFreeIf(pointer);
    901 }
    902 
    903 /*
    904 * nss_ZFreeIf
    905 *
    906 * If the specified pointer is non-null, then the region of memory
    907 * to which it points -- which must have been allocated with
    908 * nss_ZAlloc -- will be zeroed and released.  This routine
    909 * returns a PRStatus value; if successful, it will return PR_SUCCESS.
    910 * If unsuccessful, it will set an error on the error stack and return
    911 * PR_FAILURE.
    912 *
    913 * The error may be one of the following values:
    914 *  NSS_ERROR_INVALID_POINTER
    915 *
    916 * Return value:
    917 *  PR_SUCCESS
    918 *  PR_FAILURE
    919 */
    920 
    921 NSS_IMPLEMENT PRStatus
    922 nss_ZFreeIf(void *pointer)
    923 {
    924    struct pointer_header *h;
    925 
    926    if ((void *)NULL == pointer) {
    927        return PR_SUCCESS;
    928    }
    929 
    930    h = (struct pointer_header *)((char *)pointer -
    931                                  sizeof(struct pointer_header));
    932 
    933    /* Check any magic here */
    934 
    935    if ((NSSArena *)NULL == h->arena) {
    936        /* Heap */
    937        (void)nsslibc_memset(pointer, 0, h->size);
    938        PR_Free(h);
    939        return PR_SUCCESS;
    940    } else {
    941 /* Arena */
    942 #ifdef NSSDEBUG
    943        if (PR_SUCCESS != nssArena_verifyPointer(h->arena)) {
    944            return PR_FAILURE;
    945        }
    946 #endif /* NSSDEBUG */
    947 
    948        if ((PRLock *)NULL == h->arena->lock) {
    949            /* Just got destroyed.. so this pointer is invalid */
    950            nss_SetError(NSS_ERROR_INVALID_POINTER);
    951            return PR_FAILURE;
    952        }
    953        PR_Lock(h->arena->lock);
    954 
    955        (void)nsslibc_memset(pointer, 0, h->size);
    956 
    957        /* No way to "free" it within an NSPR arena. */
    958 
    959        PR_Unlock(h->arena->lock);
    960        return PR_SUCCESS;
    961    }
    962    /*NOTREACHED*/
    963 }
    964 
    965 /*
    966 * NSS_ZRealloc
    967 *
    968 * This routine reallocates a block of memory obtained by calling
    969 * nss_ZAlloc or nss_ZRealloc.  The portion of memory
    970 * between the new and old sizes -- which is either being newly
    971 * obtained or released -- is in either case zeroed.  This routine
    972 * may return NULL upon failure, in which case it will have placed
    973 * an error on the error stack.
    974 *
    975 * The error may be one of the following values:
    976 *  NSS_ERROR_INVALID_POINTER
    977 *  NSS_ERROR_NO_MEMORY
    978 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
    979 *
    980 * Return value:
    981 *  NULL upon error
    982 *  A pointer to the replacement segment of memory
    983 */
    984 
    985 NSS_EXTERN void *
    986 NSS_ZRealloc(void *pointer, PRUint32 newSize)
    987 {
    988    return nss_ZRealloc(pointer, newSize);
    989 }
    990 
    991 /*
    992 * nss_ZRealloc
    993 *
    994 * This routine reallocates a block of memory obtained by calling
    995 * nss_ZAlloc or nss_ZRealloc.  The portion of memory
    996 * between the new and old sizes -- which is either being newly
    997 * obtained or released -- is in either case zeroed.  This routine
    998 * may return NULL upon failure, in which case it will have placed
    999 * an error on the error stack.
   1000 *
   1001 * The error may be one of the following values:
   1002 *  NSS_ERROR_INVALID_POINTER
   1003 *  NSS_ERROR_NO_MEMORY
   1004 *  NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD
   1005 *
   1006 * Return value:
   1007 *  NULL upon error
   1008 *  A pointer to the replacement segment of memory
   1009 */
   1010 
   1011 NSS_EXTERN void *
   1012 nss_ZRealloc(void *pointer, PRUint32 newSize)
   1013 {
   1014    NSSArena *arena;
   1015    struct pointer_header *h, *new_h;
   1016    PRUint32 my_newSize = newSize + sizeof(struct pointer_header);
   1017    void *rv;
   1018 
   1019    if (my_newSize < sizeof(struct pointer_header)) {
   1020        /* Wrapped */
   1021        nss_SetError(NSS_ERROR_NO_MEMORY);
   1022        return (void *)NULL;
   1023    }
   1024 
   1025    if ((void *)NULL == pointer) {
   1026        nss_SetError(NSS_ERROR_INVALID_POINTER);
   1027        return (void *)NULL;
   1028    }
   1029 
   1030    h = (struct pointer_header *)((char *)pointer -
   1031                                  sizeof(struct pointer_header));
   1032 
   1033    /* Check any magic here */
   1034 
   1035    if (newSize == h->size) {
   1036        /* saves thrashing */
   1037        return pointer;
   1038    }
   1039 
   1040    arena = h->arena;
   1041    if (!arena) {
   1042        /* Heap */
   1043        new_h = (struct pointer_header *)PR_Calloc(1, my_newSize);
   1044        if ((struct pointer_header *)NULL == new_h) {
   1045            nss_SetError(NSS_ERROR_NO_MEMORY);
   1046            return (void *)NULL;
   1047        }
   1048 
   1049        new_h->arena = (NSSArena *)NULL;
   1050        new_h->size = newSize;
   1051        rv = (void *)((char *)new_h + sizeof(struct pointer_header));
   1052 
   1053        if (newSize > h->size) {
   1054            (void)nsslibc_memcpy(rv, pointer, h->size);
   1055            (void)nsslibc_memset(&((char *)rv)[h->size], 0,
   1056                                 (newSize - h->size));
   1057        } else {
   1058            (void)nsslibc_memcpy(rv, pointer, newSize);
   1059        }
   1060 
   1061        (void)nsslibc_memset(pointer, 0, h->size);
   1062        h->size = 0;
   1063        PR_Free(h);
   1064 
   1065        return rv;
   1066    } else {
   1067        void *p;
   1068 /* Arena */
   1069 #ifdef NSSDEBUG
   1070        if (PR_SUCCESS != nssArena_verifyPointer(arena)) {
   1071            return (void *)NULL;
   1072        }
   1073 #endif /* NSSDEBUG */
   1074 
   1075        if (!arena->lock) {
   1076            /* Just got destroyed.. so this pointer is invalid */
   1077            nss_SetError(NSS_ERROR_INVALID_POINTER);
   1078            return (void *)NULL;
   1079        }
   1080        PR_Lock(arena->lock);
   1081 
   1082 #ifdef ARENA_THREADMARK
   1083        if (arena->marking_thread) {
   1084            if (PR_GetCurrentThread() != arena->marking_thread) {
   1085                PR_Unlock(arena->lock);
   1086                nss_SetError(NSS_ERROR_ARENA_MARKED_BY_ANOTHER_THREAD);
   1087                return (void *)NULL;
   1088            }
   1089        }
   1090 #endif /* ARENA_THREADMARK */
   1091 
   1092        if (newSize < h->size) {
   1093            /*
   1094             * We have no general way of returning memory to the arena
   1095             * (mark/release doesn't work because things may have been
   1096             * allocated after this object), so the memory is gone
   1097             * anyway.  We might as well just return the same pointer to
   1098             * the user, saying "yeah, uh-hunh, you can only use less of
   1099             * it now."  We'll zero the leftover part, of course.  And
   1100             * in fact we might as well *not* adjust h->size-- this way,
   1101             * if the user reallocs back up to something not greater than
   1102             * the original size, then voila, there's the memory!  This
   1103             * way a thrash big/small/big/small doesn't burn up the arena.
   1104             */
   1105            char *extra = &((char *)pointer)[newSize];
   1106            (void)nsslibc_memset(extra, 0, (h->size - newSize));
   1107            PR_Unlock(arena->lock);
   1108            return pointer;
   1109        }
   1110 
   1111        PL_ARENA_ALLOCATE(p, &arena->pool, my_newSize);
   1112        if ((void *)NULL == p) {
   1113            PR_Unlock(arena->lock);
   1114            nss_SetError(NSS_ERROR_NO_MEMORY);
   1115            return (void *)NULL;
   1116        }
   1117 
   1118        new_h = (struct pointer_header *)p;
   1119        new_h->arena = arena;
   1120        new_h->size = newSize;
   1121        rv = (void *)((char *)new_h + sizeof(struct pointer_header));
   1122        if (rv != pointer) {
   1123            (void)nsslibc_memcpy(rv, pointer, h->size);
   1124            (void)nsslibc_memset(pointer, 0, h->size);
   1125        }
   1126        (void)nsslibc_memset(&((char *)rv)[h->size], 0, (newSize - h->size));
   1127        h->arena = (NSSArena *)NULL;
   1128        h->size = 0;
   1129        PR_Unlock(arena->lock);
   1130        return rv;
   1131    }
   1132    /*NOTREACHED*/
   1133 }
   1134 
   1135 PRStatus
   1136 nssArena_Shutdown(void)
   1137 {
   1138    PRStatus rv = PR_SUCCESS;
   1139 #ifdef DEBUG
   1140    rv = nssPointerTracker_finalize(&arena_pointer_tracker);
   1141 #endif
   1142    return rv;
   1143 }