commit e8551cc818ff2b527059ef8a9046acf45d901cf7
Author: Hans Petter Jansson <hpj@cl.no>
Date:   Fri Jun 26 00:12:15 2020 +0200

    Patch 37: nss-fips-fix-missing-nspr.patch

diff --git a/lib/freebl/drbg.c b/lib/freebl/drbg.c
--- a/lib/freebl/drbg.c
+++ b/lib/freebl/drbg.c
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef FREEBL_NO_DEPEND
 #include "stubs.h"
 #endif
 
+#include <unistd.h>
+
 #include "prerror.h"
 #include "secerr.h"
 
 #include "prtypes.h"
 #include "prinit.h"
 #include "blapi.h"
 #include "blapii.h"
 #include "nssilock.h"
@@ -176,45 +178,68 @@ static PRCallOnceType coRNGInitEntropy;
 
 static PRStatus
 prng_initEntropy(void)
 {
     size_t length;
     PRUint8 block[PRNG_ENTROPY_BLOCK_SIZE];
     SHA256Context ctx;
 
+    /* Don't have NSPR, so can't use the real PR_CallOnce. Implement a stripped
+     * down version. This is similar to freebl_RunLoaderOnce(). */
+    if (coRNGInitEntropy.initialized) {
+        return coRNGInitEntropy.status;
+    }
+    if (__sync_lock_test_and_set(&coRNGInitEntropy.inProgress, 1) != 0) {
+        /* Shouldn't have a lot of takers here, which is good
+         * since we don't have condition variables yet.
+         * 'initialized' only ever gets set (not cleared) so we don't
+         * need the traditional locks. */
+        while (!coRNGInitEntropy.initialized) {
+            sleep(1); /* don't have condition variables, just give up the CPU */
+        }
+        return coRNGInitEntropy.status;
+    }
+
     /* For FIPS 140-2 4.9.2 continuous random number generator test,
      * fetch the initial entropy from the system RNG and keep it for
      * later comparison. */
     length = RNG_SystemRNG(block, sizeof(block));
     if (length == 0) {
+        coRNGInitEntropy.status = PR_FAILURE;
+        __sync_synchronize ();
+        coRNGInitEntropy.initialized = 1;
         return PR_FAILURE; /* error is already set */
     }
     PORT_Assert(length == sizeof(block));
 
     /* Store the hash of the entropy block rather than the block
      * itself for backward secrecy. */
     SHA256_Begin(&ctx);
     SHA256_Update(&ctx, block, sizeof(block));
     SHA256_End(&ctx, globalrng->previousEntropyHash, NULL,
                sizeof(globalrng->previousEntropyHash));
     PORT_Memset(block, 0, sizeof(block));
+
+    coRNGInitEntropy.status = PR_SUCCESS;
+    __sync_synchronize ();
+    coRNGInitEntropy.initialized = 1;
     return PR_SUCCESS;
 }
 
 static SECStatus
 prng_getEntropy(PRUint8 *buffer, size_t requestLength)
 {
     size_t total = 0;
     PRUint8 block[PRNG_ENTROPY_BLOCK_SIZE];
     PRUint8 hash[SHA256_LENGTH];
     SHA256Context ctx;
     SECStatus rv = SECSuccess;
 
-    if (PR_CallOnce(&coRNGInitEntropy, prng_initEntropy) != PR_SUCCESS) {
+    if (prng_initEntropy () != PR_SUCCESS) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
 
     /* For FIPS 140-2 4.9.2 continuous random generator test,
      * iteratively fetch fixed sized blocks from the system and
      * compare consecutive blocks. */
     while (total < requestLength) {
@@ -553,20 +578,44 @@ prng_freeRNGContext(RNGContext *rng)
  * small, so it is imperative that RNG_RandomUpdate() be called with
  * additional seed data before the generator is used.  A good way to
  * provide the generator with additional entropy is to call
  * RNG_SystemInfoForRNG().  Note that C_Initialize() does exactly that.
  */
 SECStatus
 RNG_RNGInit(void)
 {
+    /* Don't have NSPR, so can't use the real PR_CallOnce. Implement a stripped
+     * down version. This is similar to freebl_RunLoaderOnce(). */
+    if (coRNGInit.initialized) {
+        return coRNGInit.status;
+    }
+    if (__sync_lock_test_and_set(&coRNGInit.inProgress, 1) != 0) {
+        /* Shouldn't have a lot of takers here, which is good
+         * since we don't have condition variables yet.
+         * 'initialized' only ever gets set (not cleared) so we don't
+         * need the traditional locks. */
+        while (!coRNGInit.initialized) {
+            sleep(1); /* don't have condition variables, just give up the CPU */
+        }
+        return coRNGInit.status;
+    }
+
     /* Allow only one call to initialize the context */
-    PR_CallOnce(&coRNGInit, rng_init);
+    coRNGInit.status = rng_init ();
+    __sync_synchronize ();
+    coRNGInit.initialized = 1;
+    if (coRNGInit.status != PR_SUCCESS)
+        return SECFailure;
+
     /* Make sure there is a context */
-    return (globalrng != NULL) ? SECSuccess : SECFailure;
+    coRNGInit.status = (globalrng != NULL) ? SECSuccess : SECFailure;
+    __sync_synchronize ();
+    coRNGInit.initialized = 1;
+    return coRNGInit.status;
 }
 
 /*
 ** Update the global random number generator with more seeding
 ** material.
 */
 SECStatus
 RNG_RandomUpdate(const void *data, size_t bytes)
@@ -831,17 +880,31 @@ PRNGTEST_Generate(PRUint8 *bytes, unsign
 {
     SECStatus rv;
     if (!testContext.isValid) {
         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
         return SECFailure;
     }
     /* replicate reseed test from prng_GenerateGlobalRandomBytes */
     if (testContext.reseed_counter[0] >= RESEED_VALUE) {
-        rv = prng_reseed(&testContext, NULL, 0, NULL, 0);
+        /* We need to supply the entropy so as to avoid use of global RNG */
+        static const PRUint8 reseed_entropy[] = {
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+        };
+        static const PRUint8 additional_input[] = {
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+        };
+        rv = prng_reseed(&testContext, reseed_entropy, sizeof reseed_entropy,
+                         additional_input, sizeof additional_input);
         if (rv != SECSuccess) {
             return rv;
         }
     }
     return prng_generateNewBytes(&testContext, bytes, bytes_len,
                                  additional, additional_len);
 }
 
