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
arena_add_pointer(const NSSArena * arena)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
arena_remove_pointer(const NSSArena * arena)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
nssArena_verifyPointer(const NSSArena * arena)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
nssArena_registerDestructor(NSSArena * arena,void (* destructor)(void * argument),void * arg)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
nssArena_deregisterDestructor(NSSArena * arena,void (* destructor)(void * argument),void * arg)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
nss_arena_call_destructor_chain(struct arena_destructor_node * it)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 *
NSSArena_Create(void)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 *
nssArena_Create(void)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
NSSArena_Destroy(NSSArena * arena)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
nssArena_Destroy(NSSArena * arena)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 *
nssArena_Mark(NSSArena * arena)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
nss_arena_unmark_release(NSSArena * arena,nssArenaMark * arenaMark,PRBool release)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
nssArena_Release(NSSArena * arena,nssArenaMark * arenaMark)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
nssArena_Unmark(NSSArena * arena,nssArenaMark * arenaMark)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 *
nss_zalloc_arena_locked(NSSArena * arena,PRUint32 size)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 *
NSS_ZAlloc(NSSArena * arenaOpt,PRUint32 size)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 *
nss_ZAlloc(NSSArena * arenaOpt,PRUint32 size)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
NSS_ZFreeIf(void * pointer)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
nss_ZFreeIf(void * pointer)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 *
NSS_ZRealloc(void * pointer,PRUint32 newSize)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 *
nss_ZRealloc(void * pointer,PRUint32 newSize)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
nssArena_Shutdown(void)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 }
1144