Coverage Report

Created: 2024-11-21 07:03

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