Merge #17670: Move events_hasher into RNGState()

8bda0960f9 Move events_hasher into RNGState() (Pieter Wuille)

Pull request description:

  This moves `events_hasher` and `events_mutex` into `RNGState()` in random.cpp. This guarantees (through the existing `GetRNGState()` function) that the mutex is always created before any events are added, even when that happens inside global initializers.

  Fixes the issue reported here: https://github.com/bitcoin/bitcoin/pull/17573#issuecomment-561828251, and includes the annotation from #17666).

ACKs for top commit:
  MarcoFalke:
    re-ACK 8bda0960f9 🥈
  sipsorcery:
    re-ACK 8bda0960f9.

Tree-SHA512: 78702d668764df19e9d61d87d82eca71cceca87d5351b740e13e732a1c18a3d53d7fbaaf63245266da597370bfebec9fa6a4749c15ec5a78dcfe6122c33553ed
This commit is contained in:
MarcoFalke 2019-12-05 15:55:22 -05:00
commit 5d0b7f9e3d
No known key found for this signature in database
GPG key ID: CE2B75697E69A548
2 changed files with 40 additions and 33 deletions

View file

@ -360,6 +360,9 @@ class RNGState {
uint64_t m_counter GUARDED_BY(m_mutex) = 0; uint64_t m_counter GUARDED_BY(m_mutex) = 0;
bool m_strongly_seeded GUARDED_BY(m_mutex) = false; bool m_strongly_seeded GUARDED_BY(m_mutex) = false;
Mutex m_events_mutex;
CSHA256 m_events_hasher GUARDED_BY(m_events_mutex);
public: public:
RNGState() noexcept RNGState() noexcept
{ {
@ -370,6 +373,35 @@ public:
{ {
} }
void AddEvent(uint32_t event_info) noexcept
{
LOCK(m_events_mutex);
m_events_hasher.Write((const unsigned char *)&event_info, sizeof(event_info));
// Get the low four bytes of the performance counter. This translates to roughly the
// subsecond part.
uint32_t perfcounter = (GetPerformanceCounter() & 0xffffffff);
m_events_hasher.Write((const unsigned char*)&perfcounter, sizeof(perfcounter));
}
/**
* Feed (the hash of) all events added through AddEvent() to hasher.
*/
void SeedEvents(CSHA512& hasher) noexcept
{
// We use only SHA256 for the events hashing to get the ASM speedups we have for SHA256,
// since we want it to be fast as network peers may be able to trigger it repeatedly.
LOCK(m_events_mutex);
unsigned char events_hash[32];
m_events_hasher.Finalize(events_hash);
hasher.Write(events_hash, 32);
// Re-initialize the hasher with the finalized state to use later.
m_events_hasher.Reset();
m_events_hasher.Write(events_hash, 32);
}
/** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher. /** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher.
* *
* If this function has never been called with strong_seed = true, false is returned. * If this function has never been called with strong_seed = true, false is returned.
@ -440,24 +472,7 @@ static void SeedFast(CSHA512& hasher) noexcept
SeedTimestamp(hasher); SeedTimestamp(hasher);
} }
// We use only SHA256 for the events hashing to get the ASM speedups we have for SHA256, static void SeedSlow(CSHA512& hasher, RNGState& rng) noexcept
// since we want it to be fast as network peers may be able to trigger it repeatedly.
static Mutex events_mutex;
static CSHA256 events_hasher;
static void SeedEvents(CSHA512& hasher)
{
LOCK(events_mutex);
unsigned char events_hash[32];
events_hasher.Finalize(events_hash);
hasher.Write(events_hash, 32);
// Re-initialize the hasher with the finalized state to use later.
events_hasher.Reset();
events_hasher.Write(events_hash, 32);
}
static void SeedSlow(CSHA512& hasher) noexcept
{ {
unsigned char buffer[32]; unsigned char buffer[32];
@ -469,7 +484,7 @@ static void SeedSlow(CSHA512& hasher) noexcept
hasher.Write(buffer, sizeof(buffer)); hasher.Write(buffer, sizeof(buffer));
// Add the events hasher into the mix // Add the events hasher into the mix
SeedEvents(hasher); rng.SeedEvents(hasher);
// High-precision timestamp. // High-precision timestamp.
// //
@ -497,7 +512,7 @@ static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept
SeedTimestamp(hasher); SeedTimestamp(hasher);
// Add the events hasher into the mix // Add the events hasher into the mix
SeedEvents(hasher); rng.SeedEvents(hasher);
// Dynamic environment data (performance monitoring, ...) // Dynamic environment data (performance monitoring, ...)
auto old_size = hasher.Size(); auto old_size = hasher.Size();
@ -514,7 +529,7 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
SeedHardwareSlow(hasher); SeedHardwareSlow(hasher);
// Everything that the 'slow' seeder includes. // Everything that the 'slow' seeder includes.
SeedSlow(hasher); SeedSlow(hasher, rng);
// Dynamic environment data (performance monitoring, ...) // Dynamic environment data (performance monitoring, ...)
auto old_size = hasher.Size(); auto old_size = hasher.Size();
@ -534,7 +549,7 @@ enum class RNGLevel {
PERIODIC, //!< Called by RandAddPeriodic() PERIODIC, //!< Called by RandAddPeriodic()
}; };
static void ProcRand(unsigned char* out, int num, RNGLevel level) static void ProcRand(unsigned char* out, int num, RNGLevel level) noexcept
{ {
// Make sure the RNG is initialized first (as all Seed* function possibly need hwrand to be available). // Make sure the RNG is initialized first (as all Seed* function possibly need hwrand to be available).
RNGState& rng = GetRNGState(); RNGState& rng = GetRNGState();
@ -547,7 +562,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
SeedFast(hasher); SeedFast(hasher);
break; break;
case RNGLevel::SLOW: case RNGLevel::SLOW:
SeedSlow(hasher); SeedSlow(hasher, rng);
break; break;
case RNGLevel::PERIODIC: case RNGLevel::PERIODIC:
SeedPeriodic(hasher, rng); SeedPeriodic(hasher, rng);
@ -566,15 +581,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
void GetRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::FAST); } void GetRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::FAST); }
void GetStrongRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::SLOW); } void GetStrongRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::SLOW); }
void RandAddPeriodic() noexcept { ProcRand(nullptr, 0, RNGLevel::PERIODIC); } void RandAddPeriodic() noexcept { ProcRand(nullptr, 0, RNGLevel::PERIODIC); }
void RandAddEvent(const uint32_t event_info) noexcept { GetRNGState().AddEvent(event_info); }
void RandAddEvent(const uint32_t event_info) {
LOCK(events_mutex);
events_hasher.Write((const unsigned char *)&event_info, sizeof(event_info));
// Get the low four bytes of the performance counter. This translates to roughly the
// subsecond part.
uint32_t perfcounter = (GetPerformanceCounter() & 0xffffffff);
events_hasher.Write((const unsigned char*)&perfcounter, sizeof(perfcounter));
}
bool g_mock_deterministic_tests{false}; bool g_mock_deterministic_tests{false};

View file

@ -95,7 +95,7 @@ void RandAddPeriodic() noexcept;
* *
* Thread-safe. * Thread-safe.
*/ */
void RandAddEvent(const uint32_t event_info); void RandAddEvent(const uint32_t event_info) noexcept;
/** /**
* Fast randomness source. This is seeded once with secure random data, but * Fast randomness source. This is seeded once with secure random data, but