security/nss/lib/base/arena.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

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

mercurial