Bug 1253160 (part 3) - Introduce PORTCheapArenaPool and init/deinit functions. r=mt.
authorNicholas Nethercote <nnethercote@mozilla.com>
Thu, 17 Mar 2016 14:13:33 +1100
changeset 12019 eb039754d734744efa8bfd2fb14b183f7705c2c5
parent 12018 396480150b3f5237173ea38c8c8a7aec6de40238
child 12020 cf8b26d827e24f4f07ba4348de30ba3f48ea1bd5
push id1086
push usermartin.thomson@gmail.com
push dateThu, 17 Mar 2016 03:22:27 +0000
reviewersmt
bugs1253160
Bug 1253160 (part 3) - Introduce PORTCheapArenaPool and init/deinit functions. r=mt. This avoids *many* heap allocations in places where arena pools are used in a function-bounded, single-threaded way. MozReview-Commit-ID: JLYhpvEXEa1
lib/certdb/alg1485.c
lib/certdb/certdb.c
lib/certdb/certv3.c
lib/certdb/certxutl.c
lib/certdb/secname.c
lib/certdb/xbsconst.c
lib/pk11wrap/pk11obj.c
lib/softoken/legacydb/pcertdb.c
lib/util/nssutil.def
lib/util/secport.c
lib/util/secport.h
--- a/lib/certdb/alg1485.c
+++ b/lib/certdb/alg1485.c
@@ -1393,60 +1393,61 @@ appendItemToBuf(char* dest, SECItem* src
 ** This function is intended to be internal to NSS.
 */
 char*
 cert_GetCertificateEmailAddresses(CERTCertificate* cert)
 {
     char* rawEmailAddr = NULL;
     char* addrBuf = NULL;
     char* pBuf = NULL;
-    PLArenaPool* tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    PORTCheapArenaPool tmpArena;
     PRUint32 maxLen = 0;
     PRInt32 finalLen = 0;
     SECStatus rv;
     SECItem subAltName;
 
-    if (!tmpArena)
-        return addrBuf;
+    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
 
     subAltName.data = NULL;
     maxLen = cert->derCert.len;
     PORT_Assert(maxLen);
     if (!maxLen)
         maxLen = 2000; /* a guess, should never happen */
 
-    pBuf = addrBuf = (char*)PORT_ArenaZAlloc(tmpArena, maxLen + 1);
+    pBuf = addrBuf = (char*)PORT_ArenaZAlloc(&tmpArena.arena, maxLen + 1);
     if (!addrBuf)
         goto loser;
 
-    rawEmailAddr =
-        CERT_GetNameElement(tmpArena, &cert->subject, SEC_OID_PKCS9_EMAIL_ADDRESS);
+    rawEmailAddr = CERT_GetNameElement(&tmpArena.arena, &cert->subject,
+                                       SEC_OID_PKCS9_EMAIL_ADDRESS);
     pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
 
-    rawEmailAddr =
-        CERT_GetNameElement(tmpArena, &cert->subject, SEC_OID_RFC1274_MAIL);
+    rawEmailAddr = CERT_GetNameElement(&tmpArena.arena, &cert->subject,
+                                       SEC_OID_RFC1274_MAIL);
     pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
 
     rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, &subAltName);
     if (rv == SECSuccess && subAltName.data) {
         CERTGeneralName* nameList = NULL;
 
-        if (!!(nameList = CERT_DecodeAltNameExtension(tmpArena, &subAltName))) {
+        if (!!(nameList = CERT_DecodeAltNameExtension(&tmpArena.arena, &subAltName))) {
             CERTGeneralName* current = nameList;
             do {
                 if (current->type == certDirectoryName) {
                     rawEmailAddr =
-                        CERT_GetNameElement(tmpArena, &current->name.directoryName,
+                        CERT_GetNameElement(&tmpArena.arena,
+                                            &current->name.directoryName,
                                             SEC_OID_PKCS9_EMAIL_ADDRESS);
                     pBuf =
                         appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
 
                     rawEmailAddr =
-                        CERT_GetNameElement(
-                            tmpArena, &current->name.directoryName, SEC_OID_RFC1274_MAIL);
+                        CERT_GetNameElement(&tmpArena.arena,
+                                            &current->name.directoryName,
+                                            SEC_OID_RFC1274_MAIL);
                     pBuf =
                         appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
                 } else if (current->type == certRFC822Name) {
                     pBuf =
                         appendItemToBuf(pBuf, &current->name.other, &maxLen);
                 }
                 current = CERT_GetNextGeneralName(current);
             } while (current != nameList);
@@ -1459,18 +1460,17 @@ cert_GetCertificateEmailAddresses(CERTCe
     pBuf = NULL;
     if (finalLen > 1) {
         pBuf = PORT_ArenaAlloc(cert->arena, finalLen);
         if (pBuf) {
             PORT_Memcpy(pBuf, addrBuf, finalLen);
         }
     }
 loser:
-    if (tmpArena)
-        PORT_FreeArena(tmpArena, PR_FALSE);
+    PORT_DestroyCheapArena(&tmpArena);
 
     return pBuf;
 }
 
 /* returns pointer to storage in cert's arena.  Storage remains valid
 ** as long as cert's reference count doesn't go to zero.
 ** Caller should strdup or otherwise copy.
 */
--- a/lib/certdb/certdb.c
+++ b/lib/certdb/certdb.c
@@ -252,99 +252,89 @@ loser:
     PORT_FreeArena(arena, PR_FALSE);
     return (SECFailure);
 }
 
 SECStatus
 CERT_IssuerNameFromDERCert(SECItem *derCert, SECItem *derName)
 {
     int rv;
-    PLArenaPool *arena;
+    PORTCheapArenaPool tmpArena;
     CERTSignedData sd;
     void *tmpptr;
 
-    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-
-    if (!arena) {
-        return (SECFailure);
-    }
+    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
 
     PORT_Memset(&sd, 0, sizeof(CERTSignedData));
-    rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert);
-
+    rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &sd, CERT_SignedDataTemplate,
+                                derCert);
     if (rv) {
         goto loser;
     }
 
     PORT_Memset(derName, 0, sizeof(SECItem));
-    rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertIssuerTemplate,
-                                &sd.data);
-
+    rv = SEC_QuickDERDecodeItem(&tmpArena.arena, derName,
+                                SEC_CertIssuerTemplate, &sd.data);
     if (rv) {
         goto loser;
     }
 
     tmpptr = derName->data;
     derName->data = (unsigned char *)PORT_Alloc(derName->len);
     if (derName->data == NULL) {
         goto loser;
     }
 
     PORT_Memcpy(derName->data, tmpptr, derName->len);
 
-    PORT_FreeArena(arena, PR_FALSE);
+    PORT_DestroyCheapArena(&tmpArena);
     return (SECSuccess);
 
 loser:
-    PORT_FreeArena(arena, PR_FALSE);
+    PORT_DestroyCheapArena(&tmpArena);
     return (SECFailure);
 }
 
 SECStatus
 CERT_SerialNumberFromDERCert(SECItem *derCert, SECItem *derName)
 {
     int rv;
-    PLArenaPool *arena;
+    PORTCheapArenaPool tmpArena;
     CERTSignedData sd;
     void *tmpptr;
 
-    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-
-    if (!arena) {
-        return (SECFailure);
-    }
+    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
 
     PORT_Memset(&sd, 0, sizeof(CERTSignedData));
-    rv = SEC_QuickDERDecodeItem(arena, &sd, CERT_SignedDataTemplate, derCert);
-
+    rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &sd, CERT_SignedDataTemplate,
+                                derCert);
     if (rv) {
         goto loser;
     }
 
     PORT_Memset(derName, 0, sizeof(SECItem));
-    rv = SEC_QuickDERDecodeItem(arena, derName, SEC_CertSerialNumberTemplate,
-                                &sd.data);
-
+    rv = SEC_QuickDERDecodeItem(&tmpArena.arena, derName,
+                                SEC_CertSerialNumberTemplate, &sd.data);
     if (rv) {
         goto loser;
     }
 
     tmpptr = derName->data;
     derName->data = (unsigned char *)PORT_Alloc(derName->len);
     if (derName->data == NULL) {
         goto loser;
     }
 
     PORT_Memcpy(derName->data, tmpptr, derName->len);
 
-    PORT_FreeArena(arena, PR_FALSE);
+    PORT_DestroyCheapArena(&tmpArena);
     return (SECSuccess);
 
 loser:
-    PORT_FreeArena(arena, PR_FALSE);
+    PORT_DestroyCheapArena(&tmpArena);
     return (SECFailure);
 }
 
 /*
  * Generate a database key, based on serial number and issuer, from a
  * DER certificate.
  */
 SECStatus
--- a/lib/certdb/certv3.c
+++ b/lib/certdb/certv3.c
@@ -124,28 +124,25 @@ CERT_FindSubjectKeyIDExtension(CERTCerti
 
     SECStatus rv;
     SECItem encodedValue = { siBuffer, NULL, 0 };
     SECItem decodedValue = { siBuffer, NULL, 0 };
 
     rv = cert_FindExtension(cert->extensions, SEC_OID_X509_SUBJECT_KEY_ID,
                             &encodedValue);
     if (rv == SECSuccess) {
-        PLArenaPool *tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-        if (tmpArena) {
-            rv = SEC_QuickDERDecodeItem(tmpArena, &decodedValue,
-                                        SEC_ASN1_GET(SEC_OctetStringTemplate),
-                                        &encodedValue);
-            if (rv == SECSuccess) {
-                rv = SECITEM_CopyItem(NULL, retItem, &decodedValue);
-            }
-            PORT_FreeArena(tmpArena, PR_FALSE);
-        } else {
-            rv = SECFailure;
+        PORTCheapArenaPool tmpArena;
+        PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+        rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &decodedValue,
+                                    SEC_ASN1_GET(SEC_OctetStringTemplate),
+                                    &encodedValue);
+        if (rv == SECSuccess) {
+            rv = SECITEM_CopyItem(NULL, retItem, &decodedValue);
         }
+        PORT_DestroyCheapArena(&tmpArena);
     }
     SECITEM_FreeItem(&encodedValue, PR_FALSE);
     return rv;
 }
 
 SECStatus
 CERT_FindBasicConstraintExten(CERTCertificate *cert,
                               CERTBasicConstraints *value)
--- a/lib/certdb/certxutl.c
+++ b/lib/certdb/certxutl.c
@@ -389,34 +389,31 @@ CERT_MergeExtensions(void *exthandle, CE
  * get the value of the Netscape Certificate Type Extension
  */
 SECStatus
 CERT_FindBitStringExtension(CERTCertExtension **extensions, int tag,
                             SECItem *retItem)
 {
     SECItem wrapperItem, tmpItem = { siBuffer, 0 };
     SECStatus rv;
-    PLArenaPool *arena = NULL;
+    PORTCheapArenaPool tmpArena;
 
     wrapperItem.data = NULL;
     tmpItem.data = NULL;
 
-    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-
-    if (!arena) {
-        return (SECFailure);
-    }
+    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
 
     rv = cert_FindExtension(extensions, tag, &wrapperItem);
     if (rv != SECSuccess) {
         goto loser;
     }
 
-    rv = SEC_QuickDERDecodeItem(
-        arena, &tmpItem, SEC_ASN1_GET(SEC_BitStringTemplate), &wrapperItem);
+    rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &tmpItem,
+                                SEC_ASN1_GET(SEC_BitStringTemplate),
+                                &wrapperItem);
 
     if (rv != SECSuccess) {
         goto loser;
     }
 
     retItem->data = (unsigned char *)PORT_Alloc((tmpItem.len + 7) >> 3);
     if (retItem->data == NULL) {
         goto loser;
@@ -427,19 +424,17 @@ CERT_FindBitStringExtension(CERTCertExte
 
     rv = SECSuccess;
     goto done;
 
 loser:
     rv = SECFailure;
 
 done:
-    if (arena) {
-        PORT_FreeArena(arena, PR_FALSE);
-    }
+    PORT_DestroyCheapArena(&tmpArena);
 
     if (wrapperItem.data) {
         PORT_Free(wrapperItem.data);
     }
 
     return (rv);
 }
 
--- a/lib/certdb/secname.c
+++ b/lib/certdb/secname.c
@@ -603,17 +603,17 @@ CERT_CompareName(const CERTName *a, cons
 /* Moved from certhtml.c */
 SECItem *
 CERT_DecodeAVAValue(const SECItem *derAVAValue)
 {
     SECItem *retItem;
     const SEC_ASN1Template *theTemplate = NULL;
     enum { conv_none, conv_ucs4, conv_ucs2, conv_iso88591 } convert = conv_none;
     SECItem avaValue = { siBuffer, 0 };
-    PLArenaPool *newarena = NULL;
+    PORTCheapArenaPool tmpArena;
 
     if (!derAVAValue || !derAVAValue->len || !derAVAValue->data) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return NULL;
     }
 
     switch (derAVAValue->data[0]) {
         case SEC_ASN1_UNIVERSAL_STRING:
@@ -643,66 +643,63 @@ CERT_DecodeAVAValue(const SECItem *derAV
             theTemplate = SEC_ASN1_GET(SEC_UTF8StringTemplate);
             break;
         default:
             PORT_SetError(SEC_ERROR_INVALID_AVA);
             return NULL;
     }
 
     PORT_Memset(&avaValue, 0, sizeof(SECItem));
-    newarena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-    if (!newarena) {
-        return NULL;
-    }
-    if (SEC_QuickDERDecodeItem(newarena, &avaValue, theTemplate, derAVAValue) !=
-        SECSuccess) {
-        PORT_FreeArena(newarena, PR_FALSE);
+    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+    if (SEC_QuickDERDecodeItem(&tmpArena.arena, &avaValue, theTemplate,
+                               derAVAValue) != SECSuccess) {
+        PORT_DestroyCheapArena(&tmpArena);
         return NULL;
     }
 
     if (convert != conv_none) {
         unsigned int utf8ValLen = avaValue.len * 3;
         unsigned char *utf8Val =
-            (unsigned char *)PORT_ArenaZAlloc(newarena, utf8ValLen);
+            (unsigned char *)PORT_ArenaZAlloc(&tmpArena.arena, utf8ValLen);
 
         switch (convert) {
             case conv_ucs4:
                 if (avaValue.len % 4 != 0 ||
                     !PORT_UCS4_UTF8Conversion(PR_FALSE, avaValue.data,
                                               avaValue.len, utf8Val, utf8ValLen,
                                               &utf8ValLen)) {
-                    PORT_FreeArena(newarena, PR_FALSE);
+                    PORT_DestroyCheapArena(&tmpArena);
                     PORT_SetError(SEC_ERROR_INVALID_AVA);
                     return NULL;
                 }
                 break;
             case conv_ucs2:
                 if (avaValue.len % 2 != 0 ||
                     !PORT_UCS2_UTF8Conversion(PR_FALSE, avaValue.data,
                                               avaValue.len, utf8Val, utf8ValLen,
                                               &utf8ValLen)) {
-                    PORT_FreeArena(newarena, PR_FALSE);
+                    PORT_DestroyCheapArena(&tmpArena);
                     PORT_SetError(SEC_ERROR_INVALID_AVA);
                     return NULL;
                 }
                 break;
             case conv_iso88591:
                 if (!PORT_ISO88591_UTF8Conversion(avaValue.data, avaValue.len,
                                                   utf8Val, utf8ValLen,
                                                   &utf8ValLen)) {
-                    PORT_FreeArena(newarena, PR_FALSE);
+                    PORT_DestroyCheapArena(&tmpArena);
                     PORT_SetError(SEC_ERROR_INVALID_AVA);
                     return NULL;
                 }
                 break;
             case conv_none:
                 PORT_Assert(0); /* not reached */
                 break;
         }
 
         avaValue.data = utf8Val;
         avaValue.len = utf8ValLen;
     }
 
     retItem = SECITEM_DupItem(&avaValue);
-    PORT_FreeArena(newarena, PR_FALSE);
+    PORT_DestroyCheapArena(&tmpArena);
     return retItem;
 }
--- a/lib/certdb/xbsconst.c
+++ b/lib/certdb/xbsconst.c
@@ -88,34 +88,30 @@ CERT_EncodeBasicConstraintValue(PLArenaP
     return (rv);
 }
 
 SECStatus
 CERT_DecodeBasicConstraintValue(CERTBasicConstraints *value,
                                 const SECItem *encodedValue)
 {
     EncodedContext decodeContext;
-    PLArenaPool *our_pool;
+    PORTCheapArenaPool tmpArena;
     SECStatus rv = SECSuccess;
 
     do {
         PORT_Memset(&decodeContext, 0, sizeof(decodeContext));
         /* initialize the value just in case we got "0x30 00", or when the
            pathLenConstraint is omitted.
          */
         decodeContext.isCA.data = &hexFalse;
         decodeContext.isCA.len = 1;
 
-        our_pool = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
-        if (our_pool == NULL) {
-            PORT_SetError(SEC_ERROR_NO_MEMORY);
-            GEN_BREAK(SECFailure);
-        }
+        PORT_InitCheapArena(&tmpArena, SEC_ASN1_DEFAULT_ARENA_SIZE);
 
-        rv = SEC_QuickDERDecodeItem(our_pool, &decodeContext,
+        rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &decodeContext,
                                     CERTBasicConstraintsTemplate, encodedValue);
         if (rv == SECFailure)
             break;
 
         value->isCA = decodeContext.isCA.data
                           ? (PRBool)(decodeContext.isCA.data[0] != 0)
                           : PR_FALSE;
         if (decodeContext.pathLenConstraint.data == NULL) {
@@ -135,13 +131,13 @@ CERT_DecodeBasicConstraintValue(CERTBasi
             value->pathLenConstraint = len;
         } else {
             /* here we get an error where the subject is not a CA, but
                the pathLenConstraint is set */
             PORT_SetError(SEC_ERROR_BAD_DER);
             GEN_BREAK(SECFailure);
             break;
         }
+    } while (0);
 
-    } while (0);
-    PORT_FreeArena(our_pool, PR_FALSE);
+    PORT_DestroyCheapArena(&tmpArena);
     return (rv);
 }
--- a/lib/pk11wrap/pk11obj.c
+++ b/lib/pk11wrap/pk11obj.c
@@ -1809,48 +1809,47 @@ PK11_MatchItem(PK11SlotInfo *slot, CK_OB
 	{ CKA_ID, NULL, 0 },
 	{ CKA_CLASS, NULL, 0 }
     };
     /* if you change the array, change the variable below as well */
     CK_ATTRIBUTE *keyclass = &theTemplate[1];
     int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]);
     /* if you change the array, change the variable below as well */
     CK_OBJECT_HANDLE peerID;
-    PLArenaPool *arena;
+    PORTCheapArenaPool tmpArena;
     CK_RV crv;
 
     /* now we need to create space for the public key */
-    arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
-    if (arena == NULL) return CK_INVALID_HANDLE;
+    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
 
-    crv = PK11_GetAttributes(arena,slot,searchID,theTemplate,tsize);
+    crv = PK11_GetAttributes(&tmpArena.arena,slot,searchID,theTemplate,tsize);
     if (crv != CKR_OK) {
-	PORT_FreeArena(arena,PR_FALSE);
+	PORT_DestroyCheapArena(&tmpArena);
 	PORT_SetError( PK11_MapError(crv) );
 	return CK_INVALID_HANDLE;
     }
 
     if ((theTemplate[0].ulValueLen == 0) || (theTemplate[0].ulValueLen == -1)) {
-	PORT_FreeArena(arena,PR_FALSE);
+	PORT_DestroyCheapArena(&tmpArena);
 	if (matchclass == CKO_CERTIFICATE)
 	    PORT_SetError(SEC_ERROR_BAD_KEY);
 	else
 	    PORT_SetError(SEC_ERROR_NO_KEY);
 	return CK_INVALID_HANDLE;
      }
 	
 	
 
     /*
      * issue the find
      */
     *(CK_OBJECT_CLASS *)(keyclass->pValue) = matchclass;
 
     peerID = pk11_FindObjectByTemplate(slot,theTemplate,tsize);
-    PORT_FreeArena(arena,PR_FALSE);
+    PORT_DestroyCheapArena(&tmpArena);
 
     return peerID;
 }
 
 /*
  * count the number of objects that match the template.
  */
 int
--- a/lib/softoken/legacydb/pcertdb.c
+++ b/lib/softoken/legacydb/pcertdb.c
@@ -2553,66 +2553,62 @@ loser:
 }
 
 /*
  * Read the subject entry
  */
 static certDBEntrySubject *
 ReadDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
 {
+    /* |arena| isn't function-bounded, so cannot be a PORTCheapArenaPool. */
     PLArenaPool *arena = NULL;
-    PLArenaPool *tmparena = NULL;
+    PORTCheapArenaPool tmpArena;
+
     certDBEntrySubject *entry;
     SECItem dbkey;
     SECItem dbentry;
     SECStatus rv;
     
     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
     if ( arena == NULL ) {
 	PORT_SetError(SEC_ERROR_NO_MEMORY);
 	goto loser;
     }
 
-    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
-    if ( tmparena == NULL ) {
-	PORT_SetError(SEC_ERROR_NO_MEMORY);
-	goto loser;
-    }
-    
+    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
+
     entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
 						sizeof(certDBEntrySubject));
     if ( entry == NULL ) {
 	PORT_SetError(SEC_ERROR_NO_MEMORY);
 	goto loser;
     }
     entry->common.arena = arena;
     entry->common.type = certDBEntryTypeSubject;
 
-    rv = EncodeDBSubjectKey(derSubject, tmparena, &dbkey);
+    rv = EncodeDBSubjectKey(derSubject, &tmpArena.arena, &dbkey);
     if ( rv != SECSuccess ) {
 	goto loser;
     }
     
-    rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
+    rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, &tmpArena.arena);
     if ( rv == SECFailure ) {
 	goto loser;
     }
 
     rv = DecodeDBSubjectEntry(entry, &dbentry, derSubject);
     if ( rv == SECFailure ) {
 	goto loser;
     }
     
-    PORT_FreeArena(tmparena, PR_FALSE);
+    PORT_DestroyCheapArena(&tmpArena);
     return(entry);
     
 loser:
-    if ( tmparena ) {
-	PORT_FreeArena(tmparena, PR_FALSE);
-    }
+    PORT_DestroyCheapArena(&tmpArena);
     if ( arena ) {
 	PORT_FreeArena(arena, PR_FALSE);
     }
     
     return(NULL);
 }
 
 /*
--- a/lib/util/nssutil.def
+++ b/lib/util/nssutil.def
@@ -278,8 +278,15 @@ SECITEM_ZfreeArray;
 ;+       *;
 ;+};
 ;+NSSUTIL_3.21 {         # NSS Utilities 3.21 release
 ;+    global:
 NSSUTIL_ArgParseModuleSpecEx;
 ;+    local:
 ;+       *;
 ;+};
+;+NSSUTIL_3.24 {       # NSS Utilities 3.24 release
+;+    global:
+PORT_InitCheapArena;
+PORT_DestroyCheapArena;
+;+    local:
+;+       *;
+;+};
--- a/lib/util/secport.c
+++ b/lib/util/secport.c
@@ -14,16 +14,17 @@
 #include "prmem.h"
 #include "prerror.h"
 #include "plarena.h"
 #include "secerr.h"
 #include "prmon.h"
 #include "nssilock.h"
 #include "secport.h"
 #include "prenv.h"
+#include "prinit.h"
 
 #ifdef DEBUG
 #define THREADMARK
 #endif /* DEBUG */
 
 #ifdef THREADMARK
 #include "prthread.h"
 #endif /* THREADMARK */
@@ -42,16 +43,18 @@ typedef struct threadmark_mark_str {
   void *mark;
 } threadmark_mark;
 
 #endif /* THREADMARK */
 
 /* The value of this magic must change each time PORTArenaPool changes. */
 #define ARENAPOOL_MAGIC 0xB8AC9BDF 
 
+#define CHEAP_ARENAPOOL_MAGIC 0x3F16BB09
+
 typedef struct PORTArenaPool_str {
   PLArenaPool arena;
   PRUint32    magic;
   PRLock *    lock;
 #ifdef THREADMARK
   PRThread *marking_thread;
   threadmark_mark *first_mark;
 #endif
@@ -229,16 +232,23 @@ PORT_NewArena(unsigned long chunksize)
     if (!pool->lock) {
 	PORT_Free(pool);
 	return NULL;
     }
     PL_InitArenaPool(&pool->arena, "security", chunksize, sizeof(double));
     return(&pool->arena);
 }
 
+void
+PORT_InitCheapArena(PORTCheapArenaPool* pool, unsigned long chunksize)
+{
+    pool->magic = CHEAP_ARENAPOOL_MAGIC;
+    PL_InitArenaPool(&pool->arena, "security", chunksize, sizeof(double));
+}
+
 void *
 PORT_ArenaAlloc(PLArenaPool *arena, size_t size)
 {
     void *p = NULL;
 
     PORTArenaPool *pool = (PORTArenaPool *)arena;
 
     if (size <= 0) {
@@ -287,55 +297,70 @@ PORT_ArenaZAlloc(PLArenaPool *arena, siz
 
     if (p) {
 	PORT_Memset(p, 0, size);
     }
 
     return(p);
 }
 
+static PRCallOnceType setupUseFreeListOnce;
+static PRBool useFreeList;
+
+static PRStatus
+SetupUseFreeList(void)
+{
+    useFreeList = (PR_GetEnvSecure("NSS_DISABLE_ARENA_FREE_LIST") == NULL);
+    return PR_SUCCESS;
+}
+
 /*
  * If zero is true, zeroize the arena memory before freeing it.
  */
 void
 PORT_FreeArena(PLArenaPool *arena, PRBool zero)
 {
     PORTArenaPool *pool = (PORTArenaPool *)arena;
     PRLock *       lock = (PRLock *)0;
     size_t         len  = sizeof *arena;
-    static PRBool  checkedEnv = PR_FALSE;
-    static PRBool  doFreeArenaPool = PR_FALSE;
 
     if (!pool)
     	return;
     if (ARENAPOOL_MAGIC == pool->magic ) {
 	len  = sizeof *pool;
 	lock = pool->lock;
 	PZ_Lock(lock);
     }
-    if (!checkedEnv) {
-	/* no need for thread protection here */
-	doFreeArenaPool = (PR_GetEnvSecure("NSS_DISABLE_ARENA_FREE_LIST") == NULL);
-	checkedEnv = PR_TRUE;
-    }
     if (zero) {
 	PL_ClearArenaPool(arena, 0);
     }
-    if (doFreeArenaPool) {
+    PR_CallOnce(&setupUseFreeListOnce, &SetupUseFreeList);
+    if (useFreeList) {
 	PL_FreeArenaPool(arena);
     } else {
 	PL_FinishArenaPool(arena);
     }
     PORT_ZFree(arena, len);
     if (lock) {
 	PZ_Unlock(lock);
 	PZ_DestroyLock(lock);
     }
 }
 
+void
+PORT_DestroyCheapArena(PORTCheapArenaPool* pool)
+{
+    PR_CallOnce(&setupUseFreeListOnce, &SetupUseFreeList);
+    if (useFreeList) {
+	PL_FreeArenaPool(&pool->arena);
+    } else {
+	PL_FinishArenaPool(&pool->arena);
+    }
+}
+
 void *
 PORT_ArenaGrow(PLArenaPool *arena, void *ptr, size_t oldsize, size_t newsize)
 {
     PORTArenaPool *pool = (PORTArenaPool *)arena;
     PORT_Assert(newsize >= oldsize);
     
     if (newsize > MAX_SIZE) {
 	PORT_SetError(SEC_ERROR_NO_MEMORY);
--- a/lib/util/secport.h
+++ b/lib/util/secport.h
@@ -52,31 +52,65 @@
 
 /*
  * HACK for NSS 2.8 to allow Admin to compile without source changes.
  */
 #ifndef SEC_BEGIN_PROTOS
 #include "seccomon.h"
 #endif
 
+/*
+ * The PORT_*Arena* function signatures mostly involve PLArenaPool* arguments.
+ * But this is misleading! It's not actually safe to use vanilla PLArenaPools
+ * with them. There are two "subclasses" of PLArenaPool that should be used
+ * instead.
+ *
+ * - PORTArenaPool (defined in secport.c): this "subclass" is always
+ *   heap-allocated and uses a (heap-allocated) lock to protect all accesses.
+ *   Use PORT_NewArena() and PORT_FreeArena() to create and destroy
+ *   PORTArenaPools.
+ *
+ * - PORTCheapArenaPool (defined here): this "subclass" can be stack-allocated
+ *   and does not use a lock to protect accesses. This makes it cheaper but
+ *   less general. It is best used for arena pools that (a) are hot, (b) have
+ *   lifetimes bounded within a single function, and (c) don't need locking.
+ *   Use PORT_InitArena() and PORT_DestroyArena() to initialize and finalize
+ *   PORTCheapArenaPools.
+ *
+ * All the other PORT_Arena* functions will operate safely with either
+ * subclass.
+ */
+typedef struct PORTCheapArenaPool_str {
+  PLArenaPool arena;
+  PRUint32    magic;    /* This is used to distinguish the two subclasses. */
+} PORTCheapArenaPool;
+
 SEC_BEGIN_PROTOS
 
 extern void *PORT_Alloc(size_t len);
 extern void *PORT_Realloc(void *old, size_t len);
 extern void *PORT_ZAlloc(size_t len);
 extern void PORT_Free(void *ptr);
 extern void PORT_ZFree(void *ptr, size_t len);
 extern char *PORT_Strdup(const char *s);
 extern void PORT_SetError(int value);
 extern int PORT_GetError(void);
 
+/* These functions are for use with PORTArenaPools. */
 extern PLArenaPool *PORT_NewArena(unsigned long chunksize);
+extern void PORT_FreeArena(PLArenaPool *arena, PRBool zero);
+
+/* These functions are for use with PORTCheapArenaPools. */
+extern void PORT_InitCheapArena(PORTCheapArenaPool* arena,
+                                unsigned long chunksize);
+extern void PORT_DestroyCheapArena(PORTCheapArenaPool* arena);
+
+/* These functions work with both kinds of arena pool. */
 extern void *PORT_ArenaAlloc(PLArenaPool *arena, size_t size);
 extern void *PORT_ArenaZAlloc(PLArenaPool *arena, size_t size);
-extern void PORT_FreeArena(PLArenaPool *arena, PRBool zero);
 extern void *PORT_ArenaGrow(PLArenaPool *arena, void *ptr,
 			    size_t oldsize, size_t newsize);
 extern void *PORT_ArenaMark(PLArenaPool *arena);
 extern void PORT_ArenaRelease(PLArenaPool *arena, void *mark);
 extern void PORT_ArenaZRelease(PLArenaPool *arena, void *mark);
 extern void PORT_ArenaUnmark(PLArenaPool *arena, void *mark);
 extern char *PORT_ArenaStrdup(PLArenaPool *arena, const char *str);