Skip to content

Commit 05fde14

Browse files
committed
Automatically initialize RNG on first use.
1 parent 2d1cc50 commit 05fde14

File tree

4 files changed

+56
-22
lines changed

4 files changed

+56
-22
lines changed

src/bench/bench_bitcoin.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
#include <crypto/sha256.h>
88
#include <key.h>
9-
#include <random.h>
109
#include <util/system.h>
1110
#include <util/strencodings.h>
1211
#include <validation.h>
@@ -67,7 +66,6 @@ int main(int argc, char** argv)
6766
const fs::path bench_datadir{SetDataDir()};
6867

6968
SHA256AutoDetect();
70-
RandomInit();
7169
ECC_Start();
7270
SetupEnvironment();
7371

src/random.cpp

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ static inline int64_t GetPerformanceCounter()
7474
#endif
7575
}
7676

77-
7877
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
7978
static std::atomic<bool> hwrand_initialized{false};
8079
static bool rdrand_supported = false;
@@ -83,13 +82,24 @@ static void RDRandInit()
8382
{
8483
uint32_t eax, ebx, ecx, edx;
8584
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) {
86-
LogPrintf("Using RdRand as an additional entropy source\n");
8785
rdrand_supported = true;
8886
}
8987
hwrand_initialized.store(true);
9088
}
89+
90+
static void RDRandReport()
91+
{
92+
assert(hwrand_initialized.load(std::memory_order_relaxed));
93+
if (rdrand_supported) {
94+
// This must be done in a separate function, as HWRandInit() may be indirectly called
95+
// from global constructors, before logging is initialized.
96+
LogPrintf("Using RdRand as an additional entropy source\n");
97+
}
98+
}
99+
91100
#else
92101
static void RDRandInit() {}
102+
static void RDRandReport() {}
93103
#endif
94104

95105
static bool GetHWRand(unsigned char* ent32) {
@@ -279,6 +289,26 @@ void GetRandBytes(unsigned char* buf, int num)
279289
}
280290
}
281291

292+
namespace {
293+
struct RNGState {
294+
Mutex m_mutex;
295+
unsigned char m_state[32] = {0};
296+
uint64_t m_counter = 0;
297+
298+
explicit RNGState() {
299+
RDRandInit();
300+
}
301+
};
302+
303+
RNGState& GetRNGState()
304+
{
305+
// This C++11 idiom relies on the guarantee that static variable are initialized
306+
// on first call, even when multiple parallel calls are permitted.
307+
static std::unique_ptr<RNGState> g_rng{new RNGState()};
308+
return *g_rng;
309+
}
310+
}
311+
282312
static void AddDataToRng(void* data, size_t len);
283313

284314
void RandAddSeedSleep()
@@ -295,29 +325,28 @@ void RandAddSeedSleep()
295325
memory_cleanse(&nPerfCounter2, sizeof(nPerfCounter2));
296326
}
297327

298-
299-
static Mutex cs_rng_state;
300-
static unsigned char rng_state[32] = {0};
301-
static uint64_t rng_counter = 0;
302-
303328
static void AddDataToRng(void* data, size_t len) {
329+
RNGState& rng = GetRNGState();
330+
304331
CSHA512 hasher;
305332
hasher.Write((const unsigned char*)&len, sizeof(len));
306333
hasher.Write((const unsigned char*)data, len);
307334
unsigned char buf[64];
308335
{
309-
WAIT_LOCK(cs_rng_state, lock);
310-
hasher.Write(rng_state, sizeof(rng_state));
311-
hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
312-
++rng_counter;
336+
WAIT_LOCK(rng.m_mutex, lock);
337+
hasher.Write(rng.m_state, sizeof(rng.m_state));
338+
hasher.Write((const unsigned char*)&rng.m_counter, sizeof(rng.m_counter));
339+
++rng.m_counter;
313340
hasher.Finalize(buf);
314-
memcpy(rng_state, buf + 32, 32);
341+
memcpy(rng.m_state, buf + 32, 32);
315342
}
316343
memory_cleanse(buf, 64);
317344
}
318345

319346
void GetStrongRandBytes(unsigned char* out, int num)
320347
{
348+
RNGState& rng = GetRNGState();
349+
321350
assert(num <= 32);
322351
CSHA512 hasher;
323352
unsigned char buf[64];
@@ -338,12 +367,12 @@ void GetStrongRandBytes(unsigned char* out, int num)
338367

339368
// Combine with and update state
340369
{
341-
WAIT_LOCK(cs_rng_state, lock);
342-
hasher.Write(rng_state, sizeof(rng_state));
343-
hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
344-
++rng_counter;
370+
WAIT_LOCK(rng.m_mutex, lock);
371+
hasher.Write(rng.m_state, sizeof(rng.m_state));
372+
hasher.Write((const unsigned char*)&rng.m_counter, sizeof(rng.m_counter));
373+
++rng.m_counter;
345374
hasher.Finalize(buf);
346-
memcpy(rng_state, buf + 32, 32);
375+
memcpy(rng.m_state, buf + 32, 32);
347376
}
348377

349378
// Produce output
@@ -480,5 +509,8 @@ FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexce
480509

481510
void RandomInit()
482511
{
483-
RDRandInit();
512+
// Invoke RNG code to trigger initialization (if not already performed)
513+
GetRNGState();
514+
515+
RDRandReport();
484516
}

src/random.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,12 @@ void GetOSRand(unsigned char *ent32);
178178
*/
179179
bool Random_SanityCheck();
180180

181-
/** Initialize the RNG. */
181+
/**
182+
* Initialize global RNG state and log any CPU features that are used.
183+
*
184+
* Calling this function is optional. RNG state will be initialized when first
185+
* needed if it is not called.
186+
*/
182187
void RandomInit();
183188

184189
#endif // BITCOIN_RANDOM_H

src/test/test_bitcoin.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
4949
: m_path_root(fs::temp_directory_path() / "test_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
5050
{
5151
SHA256AutoDetect();
52-
RandomInit();
5352
ECC_Start();
5453
SetupEnvironment();
5554
SetupNetworking();

0 commit comments

Comments
 (0)