Coverage Report

Created: 2025-07-11 07:04

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