tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

secport.c (27469B)


      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 #define ARENAPOOL_MAGIC 0xB8AC9BDF
     52 
     53 #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 #define MAX_SIZE (PR_UINT32_MAX >> 1)
     79 
     80 void *
     81 PORT_Alloc(size_t bytes)
     82 {
     83    void *rv = NULL;
     84 
     85    if (bytes <= MAX_SIZE) {
     86        /* Always allocate a non-zero amount of bytes */
     87        rv = PR_Malloc(bytes ? bytes : 1);
     88    }
     89    if (!rv) {
     90        PORT_SetError(SEC_ERROR_NO_MEMORY);
     91    }
     92    return rv;
     93 }
     94 
     95 void *
     96 PORT_Realloc(void *oldptr, size_t bytes)
     97 {
     98    void *rv = NULL;
     99 
    100    if (bytes <= MAX_SIZE) {
    101        rv = PR_Realloc(oldptr, bytes);
    102    }
    103    if (!rv) {
    104        PORT_SetError(SEC_ERROR_NO_MEMORY);
    105    }
    106    return rv;
    107 }
    108 
    109 void *
    110 PORT_ZAlloc(size_t bytes)
    111 {
    112    void *rv = NULL;
    113 
    114    if (bytes <= MAX_SIZE) {
    115        /* Always allocate a non-zero amount of bytes */
    116        rv = PR_Calloc(1, bytes ? bytes : 1);
    117    }
    118    if (!rv) {
    119        PORT_SetError(SEC_ERROR_NO_MEMORY);
    120    }
    121    return rv;
    122 }
    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 {
    128    size_t x = alignment - 1;
    129 
    130    /* This only works if alignment is a power of 2. */
    131    if ((alignment == 0) || (alignment & (alignment - 1))) {
    132        PORT_SetError(SEC_ERROR_INVALID_ARGS);
    133        return NULL;
    134    }
    135 
    136    if (!mem) {
    137        return NULL;
    138    }
    139 
    140    /* Always allocate a non-zero amount of bytes */
    141    *mem = PORT_ZAlloc((bytes ? bytes : 1) + x);
    142    if (!*mem) {
    143        PORT_SetError(SEC_ERROR_NO_MEMORY);
    144        return NULL;
    145    }
    146 
    147    return (void *)(((uintptr_t)*mem + x) & ~(uintptr_t)x);
    148 }
    149 
    150 void *
    151 PORT_ZAllocAlignedOffset(size_t size, size_t alignment, size_t offset)
    152 {
    153    PORT_Assert(offset < size);
    154    if (offset > size) {
    155        return NULL;
    156    }
    157 
    158    void *mem = NULL;
    159    void *v = PORT_ZAllocAligned(size, alignment, &mem);
    160    if (!v) {
    161        return NULL;
    162    }
    163 
    164    PORT_Assert(mem);
    165    *((void **)((uintptr_t)v + offset)) = mem;
    166    return v;
    167 }
    168 
    169 void
    170 PORT_Free(void *ptr)
    171 {
    172    if (ptr) {
    173        PR_Free(ptr);
    174    }
    175 }
    176 
    177 void
    178 PORT_ZFree(void *ptr, size_t len)
    179 {
    180    if (ptr) {
    181        memset(ptr, 0, len);
    182        PR_Free(ptr);
    183    }
    184 }
    185 
    186 char *
    187 PORT_Strdup(const char *str)
    188 {
    189    size_t len = PORT_Strlen(str) + 1;
    190    char *newstr;
    191 
    192    newstr = (char *)PORT_Alloc(len);
    193    if (newstr) {
    194        PORT_Memcpy(newstr, str, len);
    195    }
    196    return newstr;
    197 }
    198 
    199 void
    200 PORT_SetError(int value)
    201 {
    202    PR_SetError(value, 0);
    203    return;
    204 }
    205 
    206 int
    207 PORT_GetError(void)
    208 {
    209    return (PR_GetError());
    210 }
    211 
    212 void
    213 PORT_SafeZero(void *p, size_t n)
    214 {
    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 #if (defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE)) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))
    228    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 #endif /* no memset_s */
    248 }
    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 {
    301    PORTArenaPool *pool;
    302 
    303    if (chunksize > MAX_SIZE) {
    304        PORT_SetError(SEC_ERROR_NO_MEMORY);
    305        return NULL;
    306    }
    307    pool = PORT_ZNew(PORTArenaPool);
    308    if (!pool) {
    309        return NULL;
    310    }
    311    pool->magic = ARENAPOOL_MAGIC;
    312    pool->lock = PZ_NewLock(nssILockArena);
    313    if (!pool->lock) {
    314        PORT_Free(pool);
    315        return NULL;
    316    }
    317    PL_InitArenaPool(&pool->arena, "security", chunksize, sizeof(double));
    318    return (&pool->arena);
    319 }
    320 
    321 void
    322 PORT_InitCheapArena(PORTCheapArenaPool *pool, unsigned long chunksize)
    323 {
    324    pool->magic = CHEAP_ARENAPOOL_MAGIC;
    325    PL_InitArenaPool(&pool->arena, "security", chunksize, sizeof(double));
    326 }
    327 
    328 void *
    329 PORT_ArenaAlloc(PLArenaPool *arena, size_t size)
    330 {
    331    void *p = NULL;
    332 
    333    PORTArenaPool *pool = (PORTArenaPool *)arena;
    334 
    335    if (size <= 0) {
    336        size = 1;
    337    }
    338 
    339    if (size > MAX_SIZE) {
    340        /* you lose. */
    341    } else
    342        /* Is it one of ours?  Assume so and check the magic */
    343        if (ARENAPOOL_MAGIC == pool->magic) {
    344            PZ_Lock(pool->lock);
    345 #ifdef THREADMARK
    346            /* Most likely one of ours.  Is there a thread id? */
    347            if (pool->marking_thread &&
    348                pool->marking_thread != PR_GetCurrentThread()) {
    349                /* Another thread holds a mark in this arena */
    350                PZ_Unlock(pool->lock);
    351                PORT_SetError(SEC_ERROR_NO_MEMORY);
    352                PORT_Assert(0);
    353                return NULL;
    354            } /* tid != null */
    355 #endif        /* THREADMARK */
    356            PL_ARENA_ALLOCATE(p, arena, size);
    357            PZ_Unlock(pool->lock);
    358        } else {
    359            PL_ARENA_ALLOCATE(p, arena, size);
    360        }
    361 
    362    if (!p) {
    363        PORT_SetError(SEC_ERROR_NO_MEMORY);
    364    }
    365 
    366    return (p);
    367 }
    368 
    369 void *
    370 PORT_ArenaZAlloc(PLArenaPool *arena, size_t size)
    371 {
    372    void *p;
    373 
    374    if (size <= 0)
    375        size = 1;
    376 
    377    p = PORT_ArenaAlloc(arena, size);
    378 
    379    if (p) {
    380        PORT_Memset(p, 0, size);
    381    }
    382 
    383    return (p);
    384 }
    385 
    386 static PRCallOnceType setupUseFreeListOnce;
    387 static PRBool useFreeList;
    388 
    389 static PRStatus
    390 SetupUseFreeList(void)
    391 {
    392    useFreeList = (PR_GetEnvSecure("NSS_DISABLE_ARENA_FREE_LIST") == NULL);
    393    return PR_SUCCESS;
    394 }
    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 {
    402    PORTArenaPool *pool = (PORTArenaPool *)arena;
    403    PRLock *lock = (PRLock *)0;
    404    size_t len = sizeof *arena;
    405 
    406    if (!pool)
    407        return;
    408    if (ARENAPOOL_MAGIC == pool->magic) {
    409        len = sizeof *pool;
    410        lock = pool->lock;
    411        PZ_Lock(lock);
    412    }
    413    if (zero) {
    414        PL_ClearArenaPool(arena, 0);
    415    }
    416    (void)PR_CallOnce(&setupUseFreeListOnce, &SetupUseFreeList);
    417    if (useFreeList) {
    418        PL_FreeArenaPool(arena);
    419    } else {
    420        PL_FinishArenaPool(arena);
    421    }
    422    PORT_ZFree(arena, len);
    423    if (lock) {
    424        PZ_Unlock(lock);
    425        PZ_DestroyLock(lock);
    426    }
    427 }
    428 
    429 void
    430 PORT_DestroyCheapArena(PORTCheapArenaPool *pool)
    431 {
    432    (void)PR_CallOnce(&setupUseFreeListOnce, &SetupUseFreeList);
    433    if (useFreeList) {
    434        PL_FreeArenaPool(&pool->arena);
    435    } else {
    436        PL_FinishArenaPool(&pool->arena);
    437    }
    438 }
    439 
    440 void *
    441 PORT_ArenaGrow(PLArenaPool *arena, void *ptr, size_t oldsize, size_t newsize)
    442 {
    443    PORTArenaPool *pool = (PORTArenaPool *)arena;
    444    PORT_Assert(newsize >= oldsize);
    445 
    446    if (newsize > MAX_SIZE) {
    447        PORT_SetError(SEC_ERROR_NO_MEMORY);
    448        return NULL;
    449    }
    450 
    451    if (ARENAPOOL_MAGIC == pool->magic) {
    452        PZ_Lock(pool->lock);
    453        /* Do we do a THREADMARK check here? */
    454        PL_ARENA_GROW(ptr, arena, oldsize, (newsize - oldsize));
    455        PZ_Unlock(pool->lock);
    456    } else {
    457        PL_ARENA_GROW(ptr, arena, oldsize, (newsize - oldsize));
    458    }
    459 
    460    return (ptr);
    461 }
    462 
    463 void *
    464 PORT_ArenaMark(PLArenaPool *arena)
    465 {
    466    void *result;
    467 
    468    PORTArenaPool *pool = (PORTArenaPool *)arena;
    469    if (ARENAPOOL_MAGIC == pool->magic) {
    470        PZ_Lock(pool->lock);
    471 #ifdef THREADMARK
    472        {
    473            threadmark_mark *tm, **pw;
    474            PRThread *currentThread = PR_GetCurrentThread();
    475 
    476            if (!pool->marking_thread) {
    477                /* First mark */
    478                pool->marking_thread = currentThread;
    479            } else if (currentThread != pool->marking_thread) {
    480                PZ_Unlock(pool->lock);
    481                PORT_SetError(SEC_ERROR_NO_MEMORY);
    482                PORT_Assert(0);
    483                return NULL;
    484            }
    485 
    486            result = PL_ARENA_MARK(arena);
    487            PL_ARENA_ALLOCATE(tm, arena, sizeof(threadmark_mark));
    488            if (!tm) {
    489                PZ_Unlock(pool->lock);
    490                PORT_SetError(SEC_ERROR_NO_MEMORY);
    491                return NULL;
    492            }
    493 
    494            tm->mark = result;
    495            tm->next = (threadmark_mark *)NULL;
    496 
    497            pw = &pool->first_mark;
    498            while (*pw) {
    499                pw = &(*pw)->next;
    500            }
    501 
    502            *pw = tm;
    503        }
    504 #else  /* THREADMARK */
    505        result = PL_ARENA_MARK(arena);
    506 #endif /* THREADMARK */
    507        PZ_Unlock(pool->lock);
    508    } else {
    509        /* a "pure" NSPR arena */
    510        result = PL_ARENA_MARK(arena);
    511    }
    512    return result;
    513 }
    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 {
    529    PLArena *a = arena->current;
    530    if (a->base <= (PRUword)mark && (PRUword)mark <= a->avail) {
    531 /* fast path: mark falls in the current arena */
    532 #ifdef PL_MAKE_MEM_UNDEFINED
    533        PL_MAKE_MEM_UNDEFINED(mark, a->avail - (PRUword)mark);
    534 #endif
    535        memset(mark, 0, a->avail - (PRUword)mark);
    536    } else {
    537        /* slow path: need to find the arena that mark falls in */
    538        for (a = arena->first.next; a; a = a->next) {
    539            PR_ASSERT(a->base <= a->avail && a->avail <= a->limit);
    540            if (a->base <= (PRUword)mark && (PRUword)mark <= a->avail) {
    541 #ifdef PL_MAKE_MEM_UNDEFINED
    542                PL_MAKE_MEM_UNDEFINED(mark, a->avail - (PRUword)mark);
    543 #endif
    544                memset(mark, 0, a->avail - (PRUword)mark);
    545                a = a->next;
    546                break;
    547            }
    548        }
    549        for (; a; a = a->next) {
    550            PR_ASSERT(a->base <= a->avail && a->avail <= a->limit);
    551 #ifdef PL_MAKE_MEM_UNDEFINED
    552            PL_MAKE_MEM_UNDEFINED((void *)a->base, a->avail - a->base);
    553 #endif
    554            memset((void *)a->base, 0, a->avail - a->base);
    555        }
    556    }
    557 }
    558 
    559 static void
    560 port_ArenaRelease(PLArenaPool *arena, void *mark, PRBool zero)
    561 {
    562    PORTArenaPool *pool = (PORTArenaPool *)arena;
    563    if (ARENAPOOL_MAGIC == pool->magic) {
    564        PZ_Lock(pool->lock);
    565 #ifdef THREADMARK
    566        {
    567            threadmark_mark **pw;
    568 
    569            if (PR_GetCurrentThread() != pool->marking_thread) {
    570                PZ_Unlock(pool->lock);
    571                PORT_SetError(SEC_ERROR_NO_MEMORY);
    572                PORT_Assert(0);
    573                return /* no error indication available */;
    574            }
    575 
    576            pw = &pool->first_mark;
    577            while (*pw && (mark != (*pw)->mark)) {
    578                pw = &(*pw)->next;
    579            }
    580 
    581            if (!*pw) {
    582                /* bad mark */
    583                PZ_Unlock(pool->lock);
    584                PORT_SetError(SEC_ERROR_NO_MEMORY);
    585                PORT_Assert(0);
    586                return /* no error indication available */;
    587            }
    588 
    589            *pw = (threadmark_mark *)NULL;
    590 
    591            if (zero) {
    592                port_ArenaZeroAfterMark(arena, mark);
    593            }
    594            PL_ARENA_RELEASE(arena, mark);
    595 
    596            if (!pool->first_mark) {
    597                pool->marking_thread = (PRThread *)NULL;
    598            }
    599        }
    600 #else  /* THREADMARK */
    601        if (zero) {
    602            port_ArenaZeroAfterMark(arena, mark);
    603        }
    604        PL_ARENA_RELEASE(arena, mark);
    605 #endif /* THREADMARK */
    606        PZ_Unlock(pool->lock);
    607    } else {
    608        if (zero) {
    609            port_ArenaZeroAfterMark(arena, mark);
    610        }
    611        PL_ARENA_RELEASE(arena, mark);
    612    }
    613 }
    614 
    615 void
    616 PORT_ArenaRelease(PLArenaPool *arena, void *mark)
    617 {
    618    port_ArenaRelease(arena, mark, PR_FALSE);
    619 }
    620 
    621 /*
    622 * Zeroize the arena memory before releasing it.
    623 */
    624 void
    625 PORT_ArenaZRelease(PLArenaPool *arena, void *mark)
    626 {
    627    port_ArenaRelease(arena, mark, PR_TRUE);
    628 }
    629 
    630 void
    631 PORT_ArenaUnmark(PLArenaPool *arena, void *mark)
    632 {
    633 #ifdef THREADMARK
    634    PORTArenaPool *pool = (PORTArenaPool *)arena;
    635    if (ARENAPOOL_MAGIC == pool->magic) {
    636        threadmark_mark **pw;
    637 
    638        PZ_Lock(pool->lock);
    639 
    640        if (PR_GetCurrentThread() != pool->marking_thread) {
    641            PZ_Unlock(pool->lock);
    642            PORT_SetError(SEC_ERROR_NO_MEMORY);
    643            PORT_Assert(0);
    644            return /* no error indication available */;
    645        }
    646 
    647        pw = &pool->first_mark;
    648        while (((threadmark_mark *)NULL != *pw) && (mark != (*pw)->mark)) {
    649            pw = &(*pw)->next;
    650        }
    651 
    652        if ((threadmark_mark *)NULL == *pw) {
    653            /* bad mark */
    654            PZ_Unlock(pool->lock);
    655            PORT_SetError(SEC_ERROR_NO_MEMORY);
    656            PORT_Assert(0);
    657            return /* no error indication available */;
    658        }
    659 
    660        *pw = (threadmark_mark *)NULL;
    661 
    662        if (!pool->first_mark) {
    663            pool->marking_thread = (PRThread *)NULL;
    664        }
    665 
    666        PZ_Unlock(pool->lock);
    667    }
    668 #endif /* THREADMARK */
    669 }
    670 
    671 char *
    672 PORT_ArenaStrdup(PLArenaPool *arena, const char *str)
    673 {
    674    int len = PORT_Strlen(str) + 1;
    675    char *newstr;
    676 
    677    newstr = (char *)PORT_ArenaAlloc(arena, len);
    678    if (newstr) {
    679        PORT_Memcpy(newstr, str, len);
    680    }
    681    return newstr;
    682 }
    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 {
    698    ucs4Utf8ConvertFunc = convFunc;
    699 }
    700 
    701 void
    702 PORT_SetUCS2_ASCIIConversionFunction(PORTCharConversionWSwapFunc convFunc)
    703 {
    704    ucs2AsciiConvertFunc = convFunc;
    705 }
    706 
    707 void
    708 PORT_SetUCS2_UTF8ConversionFunction(PORTCharConversionFunc convFunc)
    709 {
    710    ucs2Utf8ConvertFunc = convFunc;
    711 }
    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 {
    718    if (!ucs4Utf8ConvertFunc) {
    719        return sec_port_ucs4_utf8_conversion_function(toUnicode,
    720                                                      inBuf, inBufLen, outBuf, maxOutBufLen, outBufLen);
    721    }
    722 
    723    return (*ucs4Utf8ConvertFunc)(toUnicode, inBuf, inBufLen, outBuf,
    724                                  maxOutBufLen, outBufLen);
    725 }
    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 {
    732    if (!ucs2Utf8ConvertFunc) {
    733        return sec_port_ucs2_utf8_conversion_function(toUnicode,
    734                                                      inBuf, inBufLen, outBuf, maxOutBufLen, outBufLen);
    735    }
    736 
    737    return (*ucs2Utf8ConvertFunc)(toUnicode, inBuf, inBufLen, outBuf,
    738                                  maxOutBufLen, outBufLen);
    739 }
    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 {
    746    return sec_port_iso88591_utf8_conversion_function(inBuf, inBufLen,
    747                                                      outBuf, maxOutBufLen, outBufLen);
    748 }
    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 {
    756    if (!ucs2AsciiConvertFunc) {
    757        return PR_FALSE;
    758    }
    759 
    760    return (*ucs2AsciiConvertFunc)(toUnicode, inBuf, inBufLen, outBuf,
    761                                   maxOutBufLen, outBufLen, swapBytes);
    762 }
    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 {
    770    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    char *encoded = (char *)PORT_ZAlloc(strlen(envVarName) + 2 + strlen(envValue));
    788    if (!encoded) {
    789        return SECFailure;
    790    }
    791    strcpy(encoded, envVarName);
    792    strcat(encoded, "=");
    793    strcat(encoded, envValue);
    794    int putEnvFailed = putenv(encoded); /* adopt. */
    795 
    796    if (putEnvFailed) {
    797        SET_ERROR_CODE
    798        result = SECFailure;
    799        PORT_Free(encoded);
    800    }
    801 #endif
    802    return result;
    803 }
    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 {
    812    const unsigned char *a = (const unsigned char *)ia;
    813    const unsigned char *b = (const unsigned char *)ib;
    814    int r = 0;
    815 
    816    for (size_t i = 0; i < n; ++i) {
    817        r |= a[i] ^ b[i];
    818    }
    819 
    820    /* 0 <= r < 256, so -r has bit 8 set when r != 0 */
    821    return 1 & (-r >> 8);
    822 }
    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 {
    831    const unsigned char *a = (const unsigned char *)mem;
    832    int r = 0;
    833 
    834    for (size_t i = 0; i < n; ++i) {
    835        r |= a[i];
    836    }
    837 
    838    /* 0 <= r < 256, so -r has bit 8 set when r != 0 */
    839    return 1 & (-r >> 8);
    840 }
    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 {
    857 #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    __asm__(""
    867            : "+r"(x)
    868            : /* no other inputs */);
    869    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 }
    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 {
    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    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    unsigned char mask = 0xff & (-w >> 8);
    909 
    910    for (size_t i = 0; i < n; ++i) {
    911        unsigned char s0i = ((unsigned char *)src0)[i];
    912        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        ((unsigned char *)dest)[i] = s0i ^ (mask & (s0i ^ s1i));
    916    }
    917 }
    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 {
    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    return PR_FALSE;
    963 }