Coverage Report

Created: 2026-08-18 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/util/secport.c
Line
Count
Source
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
 * secport.c - portability interfaces for security libraries
7
 *
8
 * This file abstracts out libc functionality that libsec depends on
9
 *
10
 * NOTE - These are not public interfaces
11
 */
12
13
#include "seccomon.h"
14
#include "prmem.h"
15
#include "prerror.h"
16
#include "plarena.h"
17
#include "secerr.h"
18
#include "prmon.h"
19
#include "prlock.h"
20
#include "secport.h"
21
#include "prenv.h"
22
#include "prinit.h"
23
24
#include <stdint.h>
25
26
#ifdef DEBUG
27
#define THREADMARK
28
#endif /* DEBUG */
29
30
#ifdef THREADMARK
31
#include "prthread.h"
32
#endif /* THREADMARK */
33
34
#if defined(XP_UNIX)
35
#include <stdlib.h>
36
#else
37
#include "wtypes.h"
38
#endif
39
40
#define SET_ERROR_CODE /* place holder for code to set PR error code. */
41
42
#ifdef THREADMARK
43
typedef struct threadmark_mark_str {
44
    struct threadmark_mark_str *next;
45
    void *mark;
46
} threadmark_mark;
47
48
#endif /* THREADMARK */
49
50
/* The value of this magic must change each time PORTArenaPool changes. */
51
219M
#define ARENAPOOL_MAGIC 0xB8AC9BDF
52
53
1.45M
#define CHEAP_ARENAPOOL_MAGIC 0x3F16BB09
54
55
typedef struct PORTArenaPool_str {
56
    PLArenaPool arena;
57
    PRUint32 magic;
58
    PRLock *lock;
59
#ifdef THREADMARK
60
    PRThread *marking_thread;
61
    threadmark_mark *first_mark;
62
#endif
63
} PORTArenaPool;
64
65
/* locations for registering Unicode conversion functions.
66
 * XXX is this the appropriate location?  or should they be
67
 *     moved to client/server specific locations?
68
 */
69
PORTCharConversionFunc ucs4Utf8ConvertFunc;
70
PORTCharConversionFunc ucs2Utf8ConvertFunc;
71
PORTCharConversionWSwapFunc ucs2AsciiConvertFunc;
72
73
/* NSPR memory allocation functions (PR_Malloc, PR_Calloc, and PR_Realloc)
74
 * use the PRUint32 type for the size parameter. Before we pass a size_t or
75
 * unsigned long size to these functions, we need to ensure it is <= half of
76
 * the maximum PRUint32 value to avoid truncation and catch a negative size.
77
 */
78
248M
#define MAX_SIZE (PR_UINT32_MAX >> 1)
79
80
void *
81
PORT_Alloc(size_t bytes)
82
30.6M
{
83
30.6M
    void *rv = NULL;
84
85
30.6M
    if (bytes <= MAX_SIZE) {
86
        /* Always allocate a non-zero amount of bytes */
87
30.6M
        rv = PR_Malloc(bytes ? bytes : 1);
88
30.6M
    }
89
30.6M
    if (!rv) {
90
0
        PORT_SetError(SEC_ERROR_NO_MEMORY);
91
0
    }
92
30.6M
    return rv;
93
30.6M
}
94
95
void *
96
PORT_Realloc(void *oldptr, size_t bytes)
97
18.4k
{
98
18.4k
    void *rv = NULL;
99
100
18.4k
    if (bytes <= MAX_SIZE) {
101
18.4k
        rv = PR_Realloc(oldptr, bytes);
102
18.4k
    }
103
18.4k
    if (!rv) {
104
0
        PORT_SetError(SEC_ERROR_NO_MEMORY);
105
0
    }
106
18.4k
    return rv;
107
18.4k
}
108
109
void *
110
PORT_ZAlloc(size_t bytes)
111
8.90M
{
112
8.90M
    void *rv = NULL;
113
114
8.90M
    if (bytes <= MAX_SIZE) {
115
        /* Always allocate a non-zero amount of bytes */
116
8.90M
        rv = PR_Calloc(1, bytes ? bytes : 1);
117
8.90M
    }
118
8.90M
    if (!rv) {
119
0
        PORT_SetError(SEC_ERROR_NO_MEMORY);
120
0
    }
121
8.90M
    return rv;
122
8.90M
}
123
124
/* aligned_alloc is C11. This is an alternative to get aligned memory. */
125
void *
126
PORT_ZAllocAligned(size_t bytes, size_t alignment, void **mem)
127
251k
{
128
251k
    size_t x = alignment - 1;
129
130
    /* This only works if alignment is a power of 2. */
131
251k
    if ((alignment == 0) || (alignment & (alignment - 1))) {
132
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
133
0
        return NULL;
134
0
    }
135
136
251k
    if (!mem) {
137
0
        return NULL;
138
0
    }
139
140
    /* Always allocate a non-zero amount of bytes */
141
251k
    *mem = PORT_ZAlloc((bytes ? bytes : 1) + x);
142
251k
    if (!*mem) {
143
0
        PORT_SetError(SEC_ERROR_NO_MEMORY);
144
0
        return NULL;
145
0
    }
146
147
251k
    return (void *)(((uintptr_t)*mem + x) & ~(uintptr_t)x);
148
251k
}
149
150
void *
151
PORT_ZAllocAlignedOffset(size_t size, size_t alignment, size_t offset)
152
251k
{
153
251k
    PORT_Assert(offset < size);
154
251k
    if (offset > size) {
155
0
        return NULL;
156
0
    }
157
158
251k
    void *mem = NULL;
159
251k
    void *v = PORT_ZAllocAligned(size, alignment, &mem);
160
251k
    if (!v) {
161
0
        return NULL;
162
0
    }
163
164
251k
    PORT_Assert(mem);
165
251k
    *((void **)((uintptr_t)v + offset)) = mem;
166
251k
    return v;
167
251k
}
168
169
void
170
PORT_Free(void *ptr)
171
39.6M
{
172
39.6M
    if (ptr) {
173
35.3M
        PR_Free(ptr);
174
35.3M
    }
175
39.6M
}
176
177
void
178
PORT_ZFree(void *ptr, size_t len)
179
4.27M
{
180
4.27M
    if (ptr) {
181
4.14M
        memset(ptr, 0, len);
182
4.14M
        PR_Free(ptr);
183
4.14M
    }
184
4.27M
}
185
186
char *
187
PORT_Strdup(const char *str)
188
315k
{
189
315k
    size_t len = PORT_Strlen(str) + 1;
190
315k
    char *newstr;
191
192
315k
    newstr = (char *)PORT_Alloc(len);
193
315k
    if (newstr) {
194
315k
        PORT_Memcpy(newstr, str, len);
195
315k
    }
196
315k
    return newstr;
197
315k
}
198
199
void
200
PORT_SetError(int value)
201
5.66M
{
202
5.66M
    PR_SetError(value, 0);
203
5.66M
    return;
204
5.66M
}
205
206
int
207
PORT_GetError(void)
208
493k
{
209
493k
    return (PR_GetError());
210
493k
}
211
212
void
213
PORT_SafeZero(void *p, size_t n)
214
1.74M
{
215
    /* there are cases where the compiler optimizes away our attempt to clear
216
     * out our stack variables. There are multiple solutions for this problem,
217
     * but they aren't universally accepted on all platforms. This attempts
218
     * to select the best solution available given our os, compilier, and
219
     * libc */
220
#ifdef __STDC_LIB_EXT1__
221
    /* if the os implements C11 annex K, use memset_s */
222
    memset_s(p, n, 0, n);
223
#elif (defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE)) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))
224
    /* _DEFAULT_SOURCE == BSD source in GCC based environments
225
     * if other environmens support explicit_bzero, their defines
226
     * should be added here */
227
1.74M
    explicit_bzero(p, n);
228
#elif defined(XP_WIN)
229
    /* windows has a secure zero funtion */
230
    SecureZeroMemory(p, n);
231
#elif defined(__GNUC__) || defined(__clang__)
232
    /* Platforms without memset_s/explicit_bzero (notably macOS/AArch64):
233
     * use the libc's optimized (vectorized) memset, and follow it with an
234
     * inline-asm memory clobber so the compiler cannot treat the zeroing as
235
     * a dead store and elide it. */
236
    if (p != NULL) {
237
        memset(p, 0, n);
238
        __asm__ __volatile__(""
239
                             :
240
                             : "r"(p)
241
                             : "memory");
242
    }
243
#else
244
    /* Last resort for toolchains that support none of the above and lack GNU/
245
     * Clang inline asm: write through a volatile pointer so the compiler is
246
     * (best-effort) not permitted to elide the zeroing. If the OS provides
247
     * another secure-zero primitive (e.g. memset_explicit), add a branch for
248
     * it above with the appropriate define check. */
249
    if (p != NULL) {
250
        volatile unsigned char *__vl = (unsigned char *)p;
251
        size_t __nl = n;
252
        while (__nl--)
253
            *__vl++ = 0;
254
    }
255
#endif
256
1.74M
}
257
258
/********************* Arena code follows *****************************
259
 * ArenaPools are like heaps.  The memory in them consists of large blocks,
260
 * called arenas, which are allocated from the/a system heap.  Inside an
261
 * ArenaPool, the arenas are organized as if they were in a stack.  Newly
262
 * allocated arenas are "pushed" on that stack.  When you attempt to
263
 * allocate memory from an ArenaPool, the code first looks to see if there
264
 * is enough unused space in the top arena on the stack to satisfy your
265
 * request, and if so, your request is satisfied from that arena.
266
 * Otherwise, a new arena is allocated (or taken from NSPR's list of freed
267
 * arenas) and pushed on to the stack.  The new arena is always big enough
268
 * to satisfy the request, and is also at least a minimum size that is
269
 * established at the time that the ArenaPool is created.
270
 *
271
 * The ArenaMark function returns the address of a marker in the arena at
272
 * the top of the arena stack.  It is the address of the place in the arena
273
 * on the top of the arena stack from which the next block of memory will
274
 * be allocated.  Each ArenaPool has its own separate stack, and hence
275
 * marks are only relevant to the ArenaPool from which they are gotten.
276
 * Marks may be nested.  That is, a thread can get a mark, and then get
277
 * another mark.
278
 *
279
 * It is intended that all the marks in an ArenaPool may only be owned by a
280
 * single thread.  In DEBUG builds, this is enforced.  In non-DEBUG builds,
281
 * it is not.  In DEBUG builds, when a thread gets a mark from an
282
 * ArenaPool, no other thread may acquire a mark in that ArenaPool while
283
 * that mark exists, that is, until that mark is unmarked or released.
284
 * Therefore, it is important that every mark be unmarked or released when
285
 * the creating thread has no further need for exclusive ownership of the
286
 * right to manage the ArenaPool.
287
 *
288
 * The ArenaUnmark function discards the ArenaMark at the address given,
289
 * and all marks nested inside that mark (that is, acquired from that same
290
 * ArenaPool while that mark existed).   It is an error for a thread other
291
 * than the mark's creator to try to unmark it.  When a thread has unmarked
292
 * all its marks from an ArenaPool, then another thread is able to set
293
 * marks in that ArenaPool.  ArenaUnmark does not deallocate (or "pop") any
294
 * memory allocated from the ArenaPool since the mark was created.
295
 *
296
 * ArenaRelease "pops" the stack back to the mark, deallocating all the
297
 * memory allocated from the arenas in the ArenaPool since that mark was
298
 * created, and removing any arenas from the ArenaPool that have no
299
 * remaining active allocations when that is done.  It implicitly releases
300
 * any marks nested inside the mark being explicitly released.  It is the
301
 * only operation, other than destroying the arenapool, that potentially
302
 * reduces the number of arenas on the stack.  Otherwise, the stack grows
303
 * until the arenapool is destroyed, at which point all the arenas are
304
 * freed or returned to a "free arena list", depending on their sizes.
305
 */
306
PLArenaPool *
307
PORT_NewArena(unsigned long chunksize)
308
1.79M
{
309
1.79M
    PORTArenaPool *pool;
310
311
1.79M
    if (chunksize > MAX_SIZE) {
312
0
        PORT_SetError(SEC_ERROR_NO_MEMORY);
313
0
        return NULL;
314
0
    }
315
1.79M
    pool = PORT_ZNew(PORTArenaPool);
316
1.79M
    if (!pool) {
317
0
        return NULL;
318
0
    }
319
1.79M
    pool->magic = ARENAPOOL_MAGIC;
320
1.79M
    pool->lock = PR_NewLock();
321
1.79M
    if (!pool->lock) {
322
0
        PORT_Free(pool);
323
0
        return NULL;
324
0
    }
325
1.79M
    PL_InitArenaPool(&pool->arena, "security", chunksize, sizeof(double));
326
1.79M
    return (&pool->arena);
327
1.79M
}
328
329
void
330
PORT_InitCheapArena(PORTCheapArenaPool *pool, unsigned long chunksize)
331
1.45M
{
332
1.45M
    pool->magic = CHEAP_ARENAPOOL_MAGIC;
333
1.45M
    PL_InitArenaPool(&pool->arena, "security", chunksize, sizeof(double));
334
1.45M
}
335
336
void *
337
PORT_ArenaAlloc(PLArenaPool *arena, size_t size)
338
206M
{
339
206M
    void *p = NULL;
340
341
206M
    PORTArenaPool *pool = (PORTArenaPool *)arena;
342
343
206M
    if (size <= 0) {
344
2.21k
        size = 1;
345
2.21k
    }
346
347
206M
    if (size > MAX_SIZE) {
348
        /* you lose. */
349
5
    } else
350
        /* Is it one of ours?  Assume so and check the magic */
351
206M
        if (ARENAPOOL_MAGIC == pool->magic) {
352
191M
            PR_Lock(pool->lock);
353
191M
#ifdef THREADMARK
354
            /* Most likely one of ours.  Is there a thread id? */
355
191M
            if (pool->marking_thread &&
356
164M
                pool->marking_thread != PR_GetCurrentThread()) {
357
                /* Another thread holds a mark in this arena */
358
0
                PR_Unlock(pool->lock);
359
0
                PORT_SetError(SEC_ERROR_NO_MEMORY);
360
0
                PORT_Assert(0);
361
0
                return NULL;
362
0
            } /* tid != null */
363
191M
#endif        /* THREADMARK */
364
191M
            PL_ARENA_ALLOCATE(p, arena, size);
365
191M
            PR_Unlock(pool->lock);
366
191M
        } else {
367
15.6M
            PL_ARENA_ALLOCATE(p, arena, size);
368
15.6M
        }
369
370
206M
    if (!p) {
371
5
        PORT_SetError(SEC_ERROR_NO_MEMORY);
372
5
    }
373
374
206M
    return (p);
375
206M
}
376
377
void *
378
PORT_ArenaZAlloc(PLArenaPool *arena, size_t size)
379
2.64M
{
380
2.64M
    void *p;
381
382
2.64M
    if (size <= 0)
383
2.92k
        size = 1;
384
385
2.64M
    p = PORT_ArenaAlloc(arena, size);
386
387
2.64M
    if (p) {
388
2.64M
        PORT_Memset(p, 0, size);
389
2.64M
    }
390
391
2.64M
    return (p);
392
2.64M
}
393
394
static PRCallOnceType setupUseFreeListOnce;
395
static PRBool useFreeList;
396
397
static PRStatus
398
SetupUseFreeList(void)
399
18
{
400
18
    useFreeList = (PR_GetEnvSecure("NSS_DISABLE_ARENA_FREE_LIST") == NULL);
401
18
    return PR_SUCCESS;
402
18
}
403
404
/*
405
 * If zero is true, zeroize the arena memory before freeing it.
406
 */
407
void
408
PORT_FreeArena(PLArenaPool *arena, PRBool zero)
409
1.79M
{
410
1.79M
    PORTArenaPool *pool = (PORTArenaPool *)arena;
411
1.79M
    PRLock *lock = (PRLock *)0;
412
1.79M
    size_t len = sizeof *arena;
413
414
1.79M
    if (!pool)
415
0
        return;
416
1.79M
    if (ARENAPOOL_MAGIC == pool->magic) {
417
1.79M
        len = sizeof *pool;
418
1.79M
        lock = pool->lock;
419
1.79M
        PR_Lock(lock);
420
1.79M
    }
421
1.79M
    if (zero) {
422
716k
        PL_ClearArenaPool(arena, 0);
423
716k
    }
424
1.79M
    (void)PR_CallOnce(&setupUseFreeListOnce, &SetupUseFreeList);
425
1.79M
    if (useFreeList) {
426
1.79M
        PL_FreeArenaPool(arena);
427
1.79M
    } else {
428
0
        PL_FinishArenaPool(arena);
429
0
    }
430
1.79M
    PORT_ZFree(arena, len);
431
1.79M
    if (lock) {
432
1.79M
        PR_Unlock(lock);
433
1.79M
        PR_DestroyLock(lock);
434
1.79M
    }
435
1.79M
}
436
437
void
438
PORT_DestroyCheapArena(PORTCheapArenaPool *pool)
439
1.45M
{
440
1.45M
    (void)PR_CallOnce(&setupUseFreeListOnce, &SetupUseFreeList);
441
1.45M
    if (useFreeList) {
442
1.45M
        PL_FreeArenaPool(&pool->arena);
443
1.45M
    } else {
444
0
        PL_FinishArenaPool(&pool->arena);
445
0
    }
446
1.45M
}
447
448
void *
449
PORT_ArenaGrow(PLArenaPool *arena, void *ptr, size_t oldsize, size_t newsize)
450
283k
{
451
283k
    PORTArenaPool *pool = (PORTArenaPool *)arena;
452
283k
    PORT_Assert(newsize >= oldsize);
453
454
283k
    if (newsize > MAX_SIZE) {
455
0
        PORT_SetError(SEC_ERROR_NO_MEMORY);
456
0
        return NULL;
457
0
    }
458
459
283k
    if (ARENAPOOL_MAGIC == pool->magic) {
460
200k
        PR_Lock(pool->lock);
461
        /* Do we do a THREADMARK check here? */
462
200k
        PL_ARENA_GROW(ptr, arena, oldsize, (newsize - oldsize));
463
200k
        PR_Unlock(pool->lock);
464
200k
    } else {
465
82.9k
        PL_ARENA_GROW(ptr, arena, oldsize, (newsize - oldsize));
466
82.9k
    }
467
468
283k
    return (ptr);
469
283k
}
470
471
void *
472
PORT_ArenaMark(PLArenaPool *arena)
473
4.55M
{
474
4.55M
    void *result;
475
476
4.55M
    PORTArenaPool *pool = (PORTArenaPool *)arena;
477
4.55M
    if (ARENAPOOL_MAGIC == pool->magic) {
478
4.52M
        PR_Lock(pool->lock);
479
4.52M
#ifdef THREADMARK
480
4.52M
        {
481
4.52M
            threadmark_mark *tm, **pw;
482
4.52M
            PRThread *currentThread = PR_GetCurrentThread();
483
484
4.52M
            if (!pool->marking_thread) {
485
                /* First mark */
486
1.25M
                pool->marking_thread = currentThread;
487
3.27M
            } else if (currentThread != pool->marking_thread) {
488
0
                PR_Unlock(pool->lock);
489
0
                PORT_SetError(SEC_ERROR_NO_MEMORY);
490
0
                PORT_Assert(0);
491
0
                return NULL;
492
0
            }
493
494
4.52M
            result = PL_ARENA_MARK(arena);
495
4.52M
            PL_ARENA_ALLOCATE(tm, arena, sizeof(threadmark_mark));
496
4.52M
            if (!tm) {
497
0
                PR_Unlock(pool->lock);
498
0
                PORT_SetError(SEC_ERROR_NO_MEMORY);
499
0
                return NULL;
500
0
            }
501
502
4.52M
            tm->mark = result;
503
4.52M
            tm->next = (threadmark_mark *)NULL;
504
505
4.52M
            pw = &pool->first_mark;
506
44.1M
            while (*pw) {
507
39.6M
                pw = &(*pw)->next;
508
39.6M
            }
509
510
4.52M
            *pw = tm;
511
4.52M
        }
512
#else  /* THREADMARK */
513
        result = PL_ARENA_MARK(arena);
514
#endif /* THREADMARK */
515
0
        PR_Unlock(pool->lock);
516
4.52M
    } else {
517
        /* a "pure" NSPR arena */
518
21.8k
        result = PL_ARENA_MARK(arena);
519
21.8k
    }
520
4.55M
    return result;
521
4.55M
}
522
523
/*
524
 * This function accesses the internals of PLArena, which is why it needs
525
 * to use the NSPR internal macro PL_MAKE_MEM_UNDEFINED before the memset
526
 * calls.
527
 *
528
 * We should move this function to NSPR as PL_ClearArenaAfterMark or add
529
 * a PL_ARENA_CLEAR_AND_RELEASE macro.
530
 *
531
 * TODO: remove the #ifdef PL_MAKE_MEM_UNDEFINED tests when NSPR 4.10+ is
532
 * widely available.
533
 */
534
static void
535
port_ArenaZeroAfterMark(PLArenaPool *arena, void *mark)
536
3.22M
{
537
3.22M
    PLArena *a = arena->current;
538
3.22M
    if (a->base <= (PRUword)mark && (PRUword)mark <= a->avail) {
539
/* fast path: mark falls in the current arena */
540
2.77M
#ifdef PL_MAKE_MEM_UNDEFINED
541
2.77M
        PL_MAKE_MEM_UNDEFINED(mark, a->avail - (PRUword)mark);
542
2.77M
#endif
543
2.77M
        memset(mark, 0, a->avail - (PRUword)mark);
544
2.77M
    } else {
545
        /* slow path: need to find the arena that mark falls in */
546
122M
        for (a = arena->first.next; a; a = a->next) {
547
122M
            PR_ASSERT(a->base <= a->avail && a->avail <= a->limit);
548
122M
            if (a->base <= (PRUword)mark && (PRUword)mark <= a->avail) {
549
444k
#ifdef PL_MAKE_MEM_UNDEFINED
550
444k
                PL_MAKE_MEM_UNDEFINED(mark, a->avail - (PRUword)mark);
551
444k
#endif
552
444k
                memset(mark, 0, a->avail - (PRUword)mark);
553
444k
                a = a->next;
554
444k
                break;
555
444k
            }
556
122M
        }
557
1.59M
        for (; a; a = a->next) {
558
1.15M
            PR_ASSERT(a->base <= a->avail && a->avail <= a->limit);
559
1.15M
#ifdef PL_MAKE_MEM_UNDEFINED
560
1.15M
            PL_MAKE_MEM_UNDEFINED((void *)a->base, a->avail - a->base);
561
1.15M
#endif
562
1.15M
            memset((void *)a->base, 0, a->avail - a->base);
563
1.15M
        }
564
444k
    }
565
3.22M
}
566
567
static void
568
port_ArenaRelease(PLArenaPool *arena, void *mark, PRBool zero)
569
3.22M
{
570
3.22M
    PORTArenaPool *pool = (PORTArenaPool *)arena;
571
3.22M
    if (ARENAPOOL_MAGIC == pool->magic) {
572
3.22M
        PR_Lock(pool->lock);
573
3.22M
#ifdef THREADMARK
574
3.22M
        {
575
3.22M
            threadmark_mark **pw;
576
577
3.22M
            if (PR_GetCurrentThread() != pool->marking_thread) {
578
0
                PR_Unlock(pool->lock);
579
0
                PORT_SetError(SEC_ERROR_NO_MEMORY);
580
0
                PORT_Assert(0);
581
0
                return /* no error indication available */;
582
0
            }
583
584
3.22M
            pw = &pool->first_mark;
585
42.3M
            while (*pw && (mark != (*pw)->mark)) {
586
39.1M
                pw = &(*pw)->next;
587
39.1M
            }
588
589
3.22M
            if (!*pw) {
590
                /* bad mark */
591
0
                PR_Unlock(pool->lock);
592
0
                PORT_SetError(SEC_ERROR_NO_MEMORY);
593
0
                PORT_Assert(0);
594
0
                return /* no error indication available */;
595
0
            }
596
597
3.22M
            *pw = (threadmark_mark *)NULL;
598
599
3.22M
            if (zero) {
600
3.22M
                port_ArenaZeroAfterMark(arena, mark);
601
3.22M
            }
602
3.22M
            PL_ARENA_RELEASE(arena, mark);
603
604
3.22M
            if (!pool->first_mark) {
605
71.3k
                pool->marking_thread = (PRThread *)NULL;
606
71.3k
            }
607
3.22M
        }
608
#else  /* THREADMARK */
609
        if (zero) {
610
            port_ArenaZeroAfterMark(arena, mark);
611
        }
612
        PL_ARENA_RELEASE(arena, mark);
613
#endif /* THREADMARK */
614
0
        PR_Unlock(pool->lock);
615
3.22M
    } else {
616
0
        if (zero) {
617
0
            port_ArenaZeroAfterMark(arena, mark);
618
0
        }
619
0
        PL_ARENA_RELEASE(arena, mark);
620
0
    }
621
3.22M
}
622
623
void
624
PORT_ArenaRelease(PLArenaPool *arena, void *mark)
625
2.60k
{
626
2.60k
    port_ArenaRelease(arena, mark, PR_FALSE);
627
2.60k
}
628
629
/*
630
 * Zeroize the arena memory before releasing it.
631
 */
632
void
633
PORT_ArenaZRelease(PLArenaPool *arena, void *mark)
634
3.22M
{
635
3.22M
    port_ArenaRelease(arena, mark, PR_TRUE);
636
3.22M
}
637
638
void
639
PORT_ArenaUnmark(PLArenaPool *arena, void *mark)
640
1.24M
{
641
1.24M
#ifdef THREADMARK
642
1.24M
    PORTArenaPool *pool = (PORTArenaPool *)arena;
643
1.24M
    if (ARENAPOOL_MAGIC == pool->magic) {
644
1.22M
        threadmark_mark **pw;
645
646
1.22M
        PR_Lock(pool->lock);
647
648
1.22M
        if (PR_GetCurrentThread() != pool->marking_thread) {
649
0
            PR_Unlock(pool->lock);
650
0
            PORT_SetError(SEC_ERROR_NO_MEMORY);
651
0
            PORT_Assert(0);
652
0
            return /* no error indication available */;
653
0
        }
654
655
1.22M
        pw = &pool->first_mark;
656
1.28M
        while (((threadmark_mark *)NULL != *pw) && (mark != (*pw)->mark)) {
657
57.8k
            pw = &(*pw)->next;
658
57.8k
        }
659
660
1.22M
        if ((threadmark_mark *)NULL == *pw) {
661
            /* bad mark */
662
0
            PR_Unlock(pool->lock);
663
0
            PORT_SetError(SEC_ERROR_NO_MEMORY);
664
0
            PORT_Assert(0);
665
0
            return /* no error indication available */;
666
0
        }
667
668
1.22M
        *pw = (threadmark_mark *)NULL;
669
670
1.22M
        if (!pool->first_mark) {
671
1.16M
            pool->marking_thread = (PRThread *)NULL;
672
1.16M
        }
673
674
1.22M
        PR_Unlock(pool->lock);
675
1.22M
    }
676
1.24M
#endif /* THREADMARK */
677
1.24M
}
678
679
char *
680
PORT_ArenaStrdup(PLArenaPool *arena, const char *str)
681
182k
{
682
182k
    int len = PORT_Strlen(str) + 1;
683
182k
    char *newstr;
684
685
182k
    newstr = (char *)PORT_ArenaAlloc(arena, len);
686
182k
    if (newstr) {
687
182k
        PORT_Memcpy(newstr, str, len);
688
182k
    }
689
182k
    return newstr;
690
182k
}
691
692
/********************** end of arena functions ***********************/
693
694
/****************** unicode conversion functions ***********************/
695
/*
696
 * NOTE: These conversion functions all assume that the multibyte
697
 * characters are going to be in NETWORK BYTE ORDER, not host byte
698
 * order.  This is because the only time we deal with UCS-2 and UCS-4
699
 * are when the data was received from or is going to be sent out
700
 * over the wire (in, e.g. certificates).
701
 */
702
703
void
704
PORT_SetUCS4_UTF8ConversionFunction(PORTCharConversionFunc convFunc)
705
0
{
706
0
    ucs4Utf8ConvertFunc = convFunc;
707
0
}
708
709
void
710
PORT_SetUCS2_ASCIIConversionFunction(PORTCharConversionWSwapFunc convFunc)
711
0
{
712
0
    ucs2AsciiConvertFunc = convFunc;
713
0
}
714
715
void
716
PORT_SetUCS2_UTF8ConversionFunction(PORTCharConversionFunc convFunc)
717
0
{
718
0
    ucs2Utf8ConvertFunc = convFunc;
719
0
}
720
721
PRBool
722
PORT_UCS4_UTF8Conversion(PRBool toUnicode, unsigned char *inBuf,
723
                         unsigned int inBufLen, unsigned char *outBuf,
724
                         unsigned int maxOutBufLen, unsigned int *outBufLen)
725
2.92k
{
726
2.92k
    if (!ucs4Utf8ConvertFunc) {
727
2.92k
        return sec_port_ucs4_utf8_conversion_function(toUnicode,
728
2.92k
                                                      inBuf, inBufLen, outBuf, maxOutBufLen, outBufLen);
729
2.92k
    }
730
731
0
    return (*ucs4Utf8ConvertFunc)(toUnicode, inBuf, inBufLen, outBuf,
732
0
                                  maxOutBufLen, outBufLen);
733
2.92k
}
734
735
PRBool
736
PORT_UCS2_UTF8Conversion(PRBool toUnicode, unsigned char *inBuf,
737
                         unsigned int inBufLen, unsigned char *outBuf,
738
                         unsigned int maxOutBufLen, unsigned int *outBufLen)
739
5.98k
{
740
5.98k
    if (!ucs2Utf8ConvertFunc) {
741
5.98k
        return sec_port_ucs2_utf8_conversion_function(toUnicode,
742
5.98k
                                                      inBuf, inBufLen, outBuf, maxOutBufLen, outBufLen);
743
5.98k
    }
744
745
0
    return (*ucs2Utf8ConvertFunc)(toUnicode, inBuf, inBufLen, outBuf,
746
0
                                  maxOutBufLen, outBufLen);
747
5.98k
}
748
749
PRBool
750
PORT_ISO88591_UTF8Conversion(const unsigned char *inBuf,
751
                             unsigned int inBufLen, unsigned char *outBuf,
752
                             unsigned int maxOutBufLen, unsigned int *outBufLen)
753
6.25k
{
754
6.25k
    return sec_port_iso88591_utf8_conversion_function(inBuf, inBufLen,
755
6.25k
                                                      outBuf, maxOutBufLen, outBufLen);
756
6.25k
}
757
758
PRBool
759
PORT_UCS2_ASCIIConversion(PRBool toUnicode, unsigned char *inBuf,
760
                          unsigned int inBufLen, unsigned char *outBuf,
761
                          unsigned int maxOutBufLen, unsigned int *outBufLen,
762
                          PRBool swapBytes)
763
0
{
764
0
    if (!ucs2AsciiConvertFunc) {
765
0
        return PR_FALSE;
766
0
    }
767
768
0
    return (*ucs2AsciiConvertFunc)(toUnicode, inBuf, inBufLen, outBuf,
769
0
                                   maxOutBufLen, outBufLen, swapBytes);
770
0
}
771
772
/* Portable putenv.  Creates/replaces an environment variable of the form
773
 *  envVarName=envValue
774
 */
775
int
776
NSS_PutEnv(const char *envVarName, const char *envValue)
777
0
{
778
0
    SECStatus result = SECSuccess;
779
#ifdef _WIN32
780
    PRBool setOK;
781
782
    setOK = SetEnvironmentVariable(envVarName, envValue);
783
    if (!setOK) {
784
        SET_ERROR_CODE
785
        return SECFailure;
786
    }
787
#elif defined(__GNUC__) && __GNUC__ >= 7
788
    int setEnvFailed;
789
    setEnvFailed = setenv(envVarName, envValue, 1);
790
    if (setEnvFailed) {
791
        SET_ERROR_CODE
792
        return SECFailure;
793
    }
794
#else
795
0
    char *encoded = (char *)PORT_ZAlloc(strlen(envVarName) + 2 + strlen(envValue));
796
0
    if (!encoded) {
797
0
        return SECFailure;
798
0
    }
799
0
    strcpy(encoded, envVarName);
800
0
    strcat(encoded, "=");
801
0
    strcat(encoded, envValue);
802
0
    int putEnvFailed = putenv(encoded); /* adopt. */
803
804
0
    if (putEnvFailed) {
805
0
        SET_ERROR_CODE
806
0
        result = SECFailure;
807
0
        PORT_Free(encoded);
808
0
    }
809
0
#endif
810
0
    return result;
811
0
}
812
813
/*
814
 * Perform a constant-time compare of two memory regions. The return value is
815
 * 0 if the memory regions are equal and non-zero otherwise.
816
 */
817
int
818
NSS_SecureMemcmp(const void *ia, const void *ib, size_t n)
819
557k
{
820
557k
    const unsigned char *a = (const unsigned char *)ia;
821
557k
    const unsigned char *b = (const unsigned char *)ib;
822
557k
    int r = 0;
823
824
11.3M
    for (size_t i = 0; i < n; ++i) {
825
10.8M
        r |= a[i] ^ b[i];
826
10.8M
    }
827
828
    /* 0 <= r < 256, so -r has bit 8 set when r != 0 */
829
557k
    return 1 & (-r >> 8);
830
557k
}
831
832
/*
833
 * Perform a constant-time check if a memory region is all 0. The return value
834
 * is 0 if the memory region is all zero.
835
 */
836
unsigned int
837
NSS_SecureMemcmpZero(const void *mem, size_t n)
838
27.5k
{
839
27.5k
    const unsigned char *a = (const unsigned char *)mem;
840
27.5k
    int r = 0;
841
842
907k
    for (size_t i = 0; i < n; ++i) {
843
880k
        r |= a[i];
844
880k
    }
845
846
    /* 0 <= r < 256, so -r has bit 8 set when r != 0 */
847
27.5k
    return 1 & (-r >> 8);
848
27.5k
}
849
850
/*
851
 * A "value barrier" prevents the compiler from making optimizations based on
852
 * the value that a variable takes.
853
 *
854
 * Standard C does not have value barriers, so C implementations of them are
855
 * compiler-specific and are not guaranteed to be effective. Thus, the value
856
 * barriers here are a best-effort, defense-in-depth, strategy. They are not a
857
 * substitute for standard constant-time programming discipline.
858
 *
859
 * Some implementations have a performance penalty, so value barriers should
860
 * be used sparingly.
861
 */
862
static inline int
863
value_barrier_int(int x)
864
0
{
865
0
#if defined(__GNUC__) || defined(__clang__)
866
    /* This inline assembly trick from Chandler Carruth's CppCon 2015 talk
867
     * generates no instructions.
868
     *
869
     * "+r"(x) means that x will be mapped to a register that is both an input
870
     * and an output to the assembly routine (""). The compiler will not
871
     * inspect the assembly routine itself, so it cannot assume anything about
872
     * the value of x after this line.
873
     */
874
0
    __asm__(""
875
0
            : "+r"(x)
876
0
            : /* no other inputs */);
877
0
    return x;
878
#else
879
    /* If the compiler does not support the inline assembly trick above, we can
880
     * put x in `volatile` storage and read it out again. This will generate
881
     * explict store and load instructions, and possibly more depending on the
882
     * target.
883
     */
884
    volatile int y = x;
885
    return y;
886
#endif
887
0
}
888
889
/*
890
 * A branch-free implementation of
891
 *      if (!b) {
892
 *           memmove(dest, src0, n);
893
 *      } else {
894
 *           memmove(dest, src1, n);
895
 *      }
896
 *
897
 * The memmove is performed with src0 if `b == 0` and with src1
898
 * otherwise.
899
 *
900
 * As with memmove, the selected src can overlap dest.
901
 *
902
 * Each of dest, src0, and src1 must point to an allocated buffer
903
 * of at least n bytes.
904
 */
905
void
906
NSS_SecureSelect(void *dest, const void *src0, const void *src1, size_t n, unsigned char b)
907
908
0
{
909
    // This value barrier makes it safe for the compiler to inline
910
    // NSS_SecureSelect into a routine where it could otherwise infer something
911
    // about the value of b, e.g. that b is 0/1 valued.
912
0
    int w = value_barrier_int(b);
913
914
    // 0 <= b < 256, and int is at least 16 bits, so -w has bits 8-15
915
    // set when w != 0.
916
0
    unsigned char mask = 0xff & (-w >> 8);
917
918
0
    for (size_t i = 0; i < n; ++i) {
919
0
        unsigned char s0i = ((unsigned char *)src0)[i];
920
0
        unsigned char s1i = ((unsigned char *)src1)[i];
921
        // if mask == 0 this simplifies to s0 ^ 0
922
        // if mask == -1 this simplifies to s0 ^ s0 ^ s1
923
0
        ((unsigned char *)dest)[i] = s0i ^ (mask & (s0i ^ s1i));
924
0
    }
925
0
}
926
927
/*
928
 * consolidate all the calls to get the system FIPS status in one spot.
929
 * This function allows an environment variable to override what is returned.
930
 */
931
PRBool
932
NSS_GetSystemFIPSEnabled(void)
933
32
{
934
/* if FIPS is disabled in NSS, always return FALSE, even if the environment
935
 * variable is set, or the system is in FIPS mode */
936
#ifndef NSS_FIPS_DISABLED
937
    const char *env;
938
939
    /* The environment variable is active for all platforms */
940
    env = PR_GetEnvSecure("NSS_FIPS");
941
    /* we generally accept y, Y, 1, FIPS, TRUE, and ON as turning on FIPS
942
     * mode. Anything else is considered 'off' */
943
    if (env && (*env == 'y' || *env == '1' || *env == 'Y' ||
944
                (PORT_Strcasecmp(env, "fips") == 0) ||
945
                (PORT_Strcasecmp(env, "true") == 0) ||
946
                (PORT_Strcasecmp(env, "on") == 0))) {
947
        return PR_TRUE;
948
    }
949
950
/* currently only Linux has a system FIPS indicator. Add others here
951
 * as they become available/known */
952
#ifdef LINUX
953
    {
954
        FILE *f;
955
        char d;
956
        size_t size;
957
        f = fopen("/proc/sys/crypto/fips_enabled", "r");
958
        if (!f)
959
            return PR_FALSE;
960
961
        size = fread(&d, 1, 1, f);
962
        fclose(f);
963
        if (size != 1)
964
            return PR_FALSE;
965
        if (d == '1')
966
            return PR_TRUE;
967
    }
968
#endif /* LINUX */
969
#endif /* NSS_FIPS_DISABLED == 0 */
970
32
    return PR_FALSE;
971
32
}