From 05fde14e3afe6f7156ebb6df6cd0e3ae12635b89 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 17 Dec 2018 16:48:21 -0800 Subject: [PATCH] Automatically initialize RNG on first use. --- src/bench/bench_bitcoin.cpp | 2 -- src/random.cpp | 68 +++++++++++++++++++++++++++---------- src/random.h | 7 +++- src/test/test_bitcoin.cpp | 1 - 4 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index 32faba86b4..b804a84478 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -67,7 +66,6 @@ int main(int argc, char** argv) const fs::path bench_datadir{SetDataDir()}; SHA256AutoDetect(); - RandomInit(); ECC_Start(); SetupEnvironment(); diff --git a/src/random.cpp b/src/random.cpp index 7dacc477d4..caeb87ff05 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -74,7 +74,6 @@ static inline int64_t GetPerformanceCounter() #endif } - #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) static std::atomic hwrand_initialized{false}; static bool rdrand_supported = false; @@ -83,13 +82,24 @@ static void RDRandInit() { uint32_t eax, ebx, ecx, edx; if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { - LogPrintf("Using RdRand as an additional entropy source\n"); rdrand_supported = true; } hwrand_initialized.store(true); } + +static void RDRandReport() +{ + assert(hwrand_initialized.load(std::memory_order_relaxed)); + if (rdrand_supported) { + // This must be done in a separate function, as HWRandInit() may be indirectly called + // from global constructors, before logging is initialized. + LogPrintf("Using RdRand as an additional entropy source\n"); + } +} + #else static void RDRandInit() {} +static void RDRandReport() {} #endif static bool GetHWRand(unsigned char* ent32) { @@ -279,6 +289,26 @@ void GetRandBytes(unsigned char* buf, int num) } } +namespace { +struct RNGState { + Mutex m_mutex; + unsigned char m_state[32] = {0}; + uint64_t m_counter = 0; + + explicit RNGState() { + RDRandInit(); + } +}; + +RNGState& GetRNGState() +{ + // This C++11 idiom relies on the guarantee that static variable are initialized + // on first call, even when multiple parallel calls are permitted. + static std::unique_ptr g_rng{new RNGState()}; + return *g_rng; +} +} + static void AddDataToRng(void* data, size_t len); void RandAddSeedSleep() @@ -295,29 +325,28 @@ void RandAddSeedSleep() memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2)); } - -static Mutex cs_rng_state; -static unsigned char rng_state[32] = {0}; -static uint64_t rng_counter = 0; - static void AddDataToRng(void* data, size_t len) { + RNGState& rng = GetRNGState(); + CSHA512 hasher; hasher.Write((const unsigned char*)&len, sizeof(len)); hasher.Write((const unsigned char*)data, len); unsigned char buf[64]; { - WAIT_LOCK(cs_rng_state, lock); - hasher.Write(rng_state, sizeof(rng_state)); - hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter)); - ++rng_counter; + WAIT_LOCK(rng.m_mutex, lock); + hasher.Write(rng.m_state, sizeof(rng.m_state)); + hasher.Write((const unsigned char*)&rng.m_counter, sizeof(rng.m_counter)); + ++rng.m_counter; hasher.Finalize(buf); - memcpy(rng_state, buf + 32, 32); + memcpy(rng.m_state, buf + 32, 32); } memory_cleanse(buf, 64); } void GetStrongRandBytes(unsigned char* out, int num) { + RNGState& rng = GetRNGState(); + assert(num <= 32); CSHA512 hasher; unsigned char buf[64]; @@ -338,12 +367,12 @@ void GetStrongRandBytes(unsigned char* out, int num) // Combine with and update state { - WAIT_LOCK(cs_rng_state, lock); - hasher.Write(rng_state, sizeof(rng_state)); - hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter)); - ++rng_counter; + WAIT_LOCK(rng.m_mutex, lock); + hasher.Write(rng.m_state, sizeof(rng.m_state)); + hasher.Write((const unsigned char*)&rng.m_counter, sizeof(rng.m_counter)); + ++rng.m_counter; hasher.Finalize(buf); - memcpy(rng_state, buf + 32, 32); + memcpy(rng.m_state, buf + 32, 32); } // Produce output @@ -480,5 +509,8 @@ FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexce void RandomInit() { - RDRandInit(); + // Invoke RNG code to trigger initialization (if not already performed) + GetRNGState(); + + RDRandReport(); } diff --git a/src/random.h b/src/random.h index 00e90abbc5..221545a8ef 100644 --- a/src/random.h +++ b/src/random.h @@ -178,7 +178,12 @@ void GetOSRand(unsigned char *ent32); */ bool Random_SanityCheck(); -/** Initialize the RNG. */ +/** + * Initialize global RNG state and log any CPU features that are used. + * + * Calling this function is optional. RNG state will be initialized when first + * needed if it is not called. + */ void RandomInit(); #endif // BITCOIN_RANDOM_H diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 858bb512fc..23b2076041 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -49,7 +49,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) : m_path_root(fs::temp_directory_path() / "test_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30)))) { SHA256AutoDetect(); - RandomInit(); ECC_Start(); SetupEnvironment(); SetupNetworking();