mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-21 22:42:04 +01:00
Merge #17270: Feed environment data into RNG initializers
d1c02775aa
Report amount of data gathered from environment (Pieter Wuille)64e1e022ce
Use thread-safe atomic in perfmon seeder (Pieter Wuille)d61f2bb076
Run background seeding periodically instead of unpredictably (Pieter Wuille)483b94292e
Add information gathered through getauxval() (Pieter Wuille)11793ea22e
Feed CPUID data into RNG (Pieter Wuille)a81c494b4c
Use sysctl for seeding on MacOS/BSD (Pieter Wuille)2554c1b81b
Gather additional entropy from the environment (Pieter Wuille)c2a262a78c
Seed randomness with process id / thread id / various clocks (Pieter Wuille)723c796667
[MOVEONLY] Move cpuid code from random & sha256 to compat/cpuid (Pieter Wuille)cea3902015
[MOVEONLY] Move perfmon data gathering to new randomenv module (Pieter Wuille)b51bae1a5a
doc: minor corrections in random.cpp (fanquake) Pull request description: This introduces a new `randomenv` module that queries varies non-cryptographic (and non-RNG) sources of entropy available on the system; things like user IDs, system configuration, time, statistics, CPUID data. The idea is that these provide a fallback in scenarios where system entropy is somehow broken (note that if system entropy *fails* we will abort regardless; this is only meant to function as a last resort against undetected failure). It includes some data sources OpenSSL currently uses, and more. The separation between random and randomenv is a bit arbitrary, but I felt that all this "non-essential" functionality deserved to be separated from the core random module. ACKs for top commit: TheBlueMatt: utACKd1c02775aa
. Certainly no longer measuring the time elapsed between a 1ms sleep (which got removed in the latest change) is a fair tradeoff for adding about 2 million other actually-higher-entropy bits :). laanwj: ACKd1c02775aa
Tree-SHA512: d290a8db6538a164348118ee02079e4f4c8551749ea78fa44b2aad57f5df2ccbc2a12dc7d80d8f3e916d68cdd8e204faf9e1bcbec15f9054eba6b22f17c66ae3
This commit is contained in:
commit
0bb37e437e
11 changed files with 611 additions and 108 deletions
14
configure.ac
14
configure.ac
|
@ -790,7 +790,7 @@ if test x$TARGET_OS = xdarwin; then
|
|||
AX_CHECK_LINK_FLAG([[-Wl,-dead_strip]], [LDFLAGS="$LDFLAGS -Wl,-dead_strip"])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h])
|
||||
AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h sys/sysctl.h vm/vm_param.h sys/vmmeter.h sys/resources.h])
|
||||
|
||||
# FD_ZERO may be dependent on a declaration of memcpy, e.g. in SmartOS
|
||||
# check that it fails to build without memcpy, then that it builds with
|
||||
|
@ -950,6 +950,18 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>
|
|||
[ AC_MSG_RESULT(no)]
|
||||
)
|
||||
|
||||
AC_MSG_CHECKING(for sysctl)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
|
||||
#include <sys/sysctl.h>]],
|
||||
[[ static const int name[2] = {CTL_KERN, KERN_VERSION};
|
||||
#ifdef __linux__
|
||||
#error "Don't use sysctl on Linux, it's deprecated even when it works"
|
||||
#endif
|
||||
sysctl(name, 2, nullptr, nullptr, nullptr, 0); ]])],
|
||||
[ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSCTL, 1,[Define this symbol if the BSD sysctl() is available]) ],
|
||||
[ AC_MSG_RESULT(no)]
|
||||
)
|
||||
|
||||
AC_MSG_CHECKING(for sysctl KERN_ARND)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
|
||||
#include <sys/sysctl.h>]],
|
||||
|
|
|
@ -119,6 +119,7 @@ BITCOIN_CORE_H = \
|
|||
compat.h \
|
||||
compat/assumptions.h \
|
||||
compat/byteswap.h \
|
||||
compat/cpuid.h \
|
||||
compat/endian.h \
|
||||
compat/sanity.h \
|
||||
compressor.h \
|
||||
|
@ -175,6 +176,7 @@ BITCOIN_CORE_H = \
|
|||
protocol.h \
|
||||
psbt.h \
|
||||
random.h \
|
||||
randomenv.h \
|
||||
reverse_iterator.h \
|
||||
reverselock.h \
|
||||
rpc/blockchain.h \
|
||||
|
@ -503,6 +505,7 @@ libbitcoin_util_a_SOURCES = \
|
|||
interfaces/handler.cpp \
|
||||
logging.cpp \
|
||||
random.cpp \
|
||||
randomenv.cpp \
|
||||
rpc/request.cpp \
|
||||
support/cleanse.cpp \
|
||||
sync.cpp \
|
||||
|
|
24
src/compat/cpuid.h
Normal file
24
src/compat/cpuid.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) 2017-2019 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_COMPAT_CPUID_H
|
||||
#define BITCOIN_COMPAT_CPUID_H
|
||||
|
||||
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
|
||||
#define HAVE_GETCPUID
|
||||
|
||||
#include <cpuid.h>
|
||||
|
||||
// We can't use cpuid.h's __get_cpuid as it does not support subleafs.
|
||||
void static inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
__cpuid_count(leaf, subleaf, a, b, c, d);
|
||||
#else
|
||||
__asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf));
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
|
||||
#endif // BITCOIN_COMPAT_CPUID_H
|
|
@ -8,9 +8,10 @@
|
|||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <compat/cpuid.h>
|
||||
|
||||
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
|
||||
#if defined(USE_ASM)
|
||||
#include <cpuid.h>
|
||||
namespace sha256_sse4
|
||||
{
|
||||
void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks);
|
||||
|
@ -546,18 +547,7 @@ bool SelfTest() {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__))
|
||||
// We can't use cpuid.h's __get_cpuid as it does not support subleafs.
|
||||
void inline cpuid(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
__cpuid_count(leaf, subleaf, a, b, c, d);
|
||||
#else
|
||||
__asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf));
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Check whether the OS has enabled AVX registers. */
|
||||
bool AVXEnabled()
|
||||
{
|
||||
|
@ -572,7 +562,7 @@ bool AVXEnabled()
|
|||
std::string SHA256AutoDetect()
|
||||
{
|
||||
std::string ret = "standard";
|
||||
#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__))
|
||||
#if defined(USE_ASM) && defined(HAVE_GETCPUID)
|
||||
bool have_sse4 = false;
|
||||
bool have_xsave = false;
|
||||
bool have_avx = false;
|
||||
|
@ -589,7 +579,7 @@ std::string SHA256AutoDetect()
|
|||
(void)enabled_avx;
|
||||
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
cpuid(1, 0, eax, ebx, ecx, edx);
|
||||
GetCPUID(1, 0, eax, ebx, ecx, edx);
|
||||
have_sse4 = (ecx >> 19) & 1;
|
||||
have_xsave = (ecx >> 27) & 1;
|
||||
have_avx = (ecx >> 28) & 1;
|
||||
|
@ -597,7 +587,7 @@ std::string SHA256AutoDetect()
|
|||
enabled_avx = AVXEnabled();
|
||||
}
|
||||
if (have_sse4) {
|
||||
cpuid(7, 0, eax, ebx, ecx, edx);
|
||||
GetCPUID(7, 0, eax, ebx, ecx, edx);
|
||||
have_avx2 = (ebx >> 5) & 1;
|
||||
have_shani = (ebx >> 29) & 1;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ public:
|
|||
CSHA512& Write(const unsigned char* data, size_t len);
|
||||
void Finalize(unsigned char hash[OUTPUT_SIZE]);
|
||||
CSHA512& Reset();
|
||||
uint64_t Size() const { return bytes; }
|
||||
};
|
||||
|
||||
#endif // BITCOIN_CRYPTO_SHA512_H
|
||||
|
|
|
@ -1258,6 +1258,11 @@ bool AppInitMain(NodeContext& node)
|
|||
CScheduler::Function serviceLoop = std::bind(&CScheduler::serviceQueue, &scheduler);
|
||||
threadGroup.create_thread(std::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));
|
||||
|
||||
// Gather some entropy once per minute.
|
||||
scheduler.scheduleEvery([]{
|
||||
RandAddPeriodic();
|
||||
}, 60000);
|
||||
|
||||
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
|
||||
GetMainSignals().RegisterWithMempoolSignals(mempool);
|
||||
|
||||
|
|
120
src/random.cpp
120
src/random.cpp
|
@ -5,19 +5,22 @@
|
|||
|
||||
#include <random.h>
|
||||
|
||||
#include <compat/cpuid.h>
|
||||
#include <crypto/sha512.h>
|
||||
#include <support/cleanse.h>
|
||||
#ifdef WIN32
|
||||
#include <compat.h> // for Windows API
|
||||
#include <wincrypt.h>
|
||||
#endif
|
||||
#include <logging.h> // for LogPrint()
|
||||
#include <sync.h> // for WAIT_LOCK
|
||||
#include <logging.h> // for LogPrintf()
|
||||
#include <sync.h> // for Mutex
|
||||
#include <util/time.h> // for GetTime()
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <thread>
|
||||
|
||||
#include <randomenv.h>
|
||||
|
||||
#include <support/allocators/secure.h>
|
||||
|
||||
#ifndef WIN32
|
||||
|
@ -40,11 +43,6 @@
|
|||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
|
||||
#include <cpuid.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/conf.h>
|
||||
|
@ -75,7 +73,7 @@ static inline int64_t GetPerformanceCounter() noexcept
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__)
|
||||
#ifdef HAVE_GETCPUID
|
||||
static bool g_rdrand_supported = false;
|
||||
static bool g_rdseed_supported = false;
|
||||
static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000;
|
||||
|
@ -86,15 +84,6 @@ static_assert(CPUID_F1_ECX_RDRAND == bit_RDRND, "Unexpected value for bit_RDRND"
|
|||
#ifdef bit_RDSEED
|
||||
static_assert(CPUID_F7_EBX_RDSEED == bit_RDSEED, "Unexpected value for bit_RDSEED");
|
||||
#endif
|
||||
static void inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d)
|
||||
{
|
||||
// We can't use __get_cpuid as it doesn't support subleafs.
|
||||
#ifdef __GNUC__
|
||||
__cpuid_count(leaf, subleaf, a, b, c, d);
|
||||
#else
|
||||
__asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void InitHardwareRand()
|
||||
{
|
||||
|
@ -263,44 +252,6 @@ static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA51
|
|||
memory_cleanse(buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
static void RandAddSeedPerfmon(CSHA512& hasher)
|
||||
{
|
||||
#ifdef WIN32
|
||||
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
|
||||
// Seed with the entire set of perfmon data
|
||||
|
||||
// This can take up to 2 seconds, so only do it every 10 minutes
|
||||
static int64_t nLastPerfmon;
|
||||
if (GetTime() < nLastPerfmon + 10 * 60)
|
||||
return;
|
||||
nLastPerfmon = GetTime();
|
||||
|
||||
std::vector<unsigned char> vData(250000, 0);
|
||||
long ret = 0;
|
||||
unsigned long nSize = 0;
|
||||
const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data
|
||||
while (true) {
|
||||
nSize = vData.size();
|
||||
ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize);
|
||||
if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
|
||||
break;
|
||||
vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
|
||||
}
|
||||
RegCloseKey(HKEY_PERFORMANCE_DATA);
|
||||
if (ret == ERROR_SUCCESS) {
|
||||
hasher.Write(vData.data(), nSize);
|
||||
memory_cleanse(vData.data(), nSize);
|
||||
} else {
|
||||
// Performance data is only a best-effort attempt at improving the
|
||||
// situation when the OS randomness (and other sources) aren't
|
||||
// adequate. As a result, failure to read it is isn't considered critical,
|
||||
// so we don't call RandFailure().
|
||||
// TODO: Add logging when the logger is made functional before global
|
||||
// constructors have been invoked.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
/** Fallback: get 32 bytes of system entropy from /dev/urandom. The most
|
||||
* compatible way to get cryptographic randomness on UNIX-ish platforms.
|
||||
|
@ -556,22 +507,16 @@ static void SeedSlow(CSHA512& hasher) noexcept
|
|||
}
|
||||
|
||||
/** Extract entropy from rng, strengthen it, and feed it into hasher. */
|
||||
static void SeedStrengthen(CSHA512& hasher, RNGState& rng) noexcept
|
||||
static void SeedStrengthen(CSHA512& hasher, RNGState& rng, int microseconds) noexcept
|
||||
{
|
||||
static std::atomic<int64_t> last_strengthen{0};
|
||||
int64_t last_time = last_strengthen.load();
|
||||
int64_t current_time = GetTimeMicros();
|
||||
if (current_time > last_time + 60000000) { // Only run once a minute
|
||||
// Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
|
||||
unsigned char strengthen_seed[32];
|
||||
rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
|
||||
// Strengthen it for 10ms (100ms on first run), and feed it into hasher.
|
||||
Strengthen(strengthen_seed, last_time == 0 ? 100000 : 10000, hasher);
|
||||
last_strengthen = current_time;
|
||||
}
|
||||
// Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher.
|
||||
unsigned char strengthen_seed[32];
|
||||
rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false);
|
||||
// Strengthen the seed, and feed it into hasher.
|
||||
Strengthen(strengthen_seed, microseconds, hasher);
|
||||
}
|
||||
|
||||
static void SeedSleep(CSHA512& hasher, RNGState& rng)
|
||||
static void SeedPeriodic(CSHA512& hasher, RNGState& rng)
|
||||
{
|
||||
// Everything that the 'fast' seeder includes
|
||||
SeedFast(hasher);
|
||||
|
@ -579,17 +524,13 @@ static void SeedSleep(CSHA512& hasher, RNGState& rng)
|
|||
// High-precision timestamp
|
||||
SeedTimestamp(hasher);
|
||||
|
||||
// Sleep for 1ms
|
||||
MilliSleep(1);
|
||||
// Dynamic environment data (performance monitoring, ...)
|
||||
auto old_size = hasher.Size();
|
||||
RandAddDynamicEnv(hasher);
|
||||
LogPrintf("Feeding %i bytes of dynamic environment data into RNG\n", hasher.Size() - old_size);
|
||||
|
||||
// High-precision timestamp after sleeping (as we commit to both the time before and after, this measures the delay)
|
||||
SeedTimestamp(hasher);
|
||||
|
||||
// Windows performance monitor data (once every 10 minutes)
|
||||
RandAddSeedPerfmon(hasher);
|
||||
|
||||
// Strengthen every minute
|
||||
SeedStrengthen(hasher, rng);
|
||||
// Strengthen for 10 ms
|
||||
SeedStrengthen(hasher, rng, 10000);
|
||||
}
|
||||
|
||||
static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
|
||||
|
@ -600,17 +541,22 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
|
|||
// Everything that the 'slow' seeder includes.
|
||||
SeedSlow(hasher);
|
||||
|
||||
// Windows performance monitor data.
|
||||
RandAddSeedPerfmon(hasher);
|
||||
// Dynamic environment data (performance monitoring, ...)
|
||||
auto old_size = hasher.Size();
|
||||
RandAddDynamicEnv(hasher);
|
||||
|
||||
// Strengthen
|
||||
SeedStrengthen(hasher, rng);
|
||||
// Static environment data
|
||||
RandAddStaticEnv(hasher);
|
||||
LogPrintf("Feeding %i bytes of environment data into RNG\n", hasher.Size() - old_size);
|
||||
|
||||
// Strengthen for 100 ms
|
||||
SeedStrengthen(hasher, rng, 100000);
|
||||
}
|
||||
|
||||
enum class RNGLevel {
|
||||
FAST, //!< Automatically called by GetRandBytes
|
||||
SLOW, //!< Automatically called by GetStrongRandBytes
|
||||
SLEEP, //!< Called by RandAddSeedSleep()
|
||||
PERIODIC, //!< Called by RandAddPeriodic()
|
||||
};
|
||||
|
||||
static void ProcRand(unsigned char* out, int num, RNGLevel level)
|
||||
|
@ -628,8 +574,8 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
|
|||
case RNGLevel::SLOW:
|
||||
SeedSlow(hasher);
|
||||
break;
|
||||
case RNGLevel::SLEEP:
|
||||
SeedSleep(hasher, rng);
|
||||
case RNGLevel::PERIODIC:
|
||||
SeedPeriodic(hasher, rng);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -652,7 +598,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 GetStrongRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::SLOW); }
|
||||
void RandAddSeedSleep() { ProcRand(nullptr, 0, RNGLevel::SLEEP); }
|
||||
void RandAddPeriodic() { ProcRand(nullptr, 0, RNGLevel::PERIODIC); }
|
||||
|
||||
bool g_mock_deterministic_tests{false};
|
||||
|
||||
|
@ -716,7 +662,7 @@ bool Random_SanityCheck()
|
|||
uint64_t start = GetPerformanceCounter();
|
||||
|
||||
/* This does not measure the quality of randomness, but it does test that
|
||||
* OSRandom() overwrites all 32 bytes of the output given a maximum
|
||||
* GetOSRand() overwrites all 32 bytes of the output given a maximum
|
||||
* number of tries.
|
||||
*/
|
||||
static const ssize_t MAX_TRIES = 1024;
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
* sources used in the 'slow' seeder are included, but also:
|
||||
* - 256 bits from the hardware RNG (rdseed or rdrand) when available.
|
||||
* - (On Windows) Performance monitoring data from the OS.
|
||||
* - (On Windows) Through OpenSSL, the screen contents.
|
||||
* - Strengthen the entropy for 100 ms using repeated SHA512.
|
||||
*
|
||||
* When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and
|
||||
|
@ -85,11 +84,11 @@ uint256 GetRandHash() noexcept;
|
|||
void GetStrongRandBytes(unsigned char* buf, int num) noexcept;
|
||||
|
||||
/**
|
||||
* Sleep for 1ms, gather entropy from various sources, and feed them to the PRNG state.
|
||||
* Gather entropy from various expensive sources, and feed them to the PRNG state.
|
||||
*
|
||||
* Thread-safe.
|
||||
*/
|
||||
void RandAddSeedSleep();
|
||||
void RandAddPeriodic();
|
||||
|
||||
/**
|
||||
* Fast randomness source. This is seeded once with secure random data, but
|
||||
|
|
508
src/randomenv.cpp
Normal file
508
src/randomenv.cpp
Normal file
|
@ -0,0 +1,508 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2019 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include <config/bitcoin-config.h>
|
||||
#endif
|
||||
|
||||
#include <randomenv.h>
|
||||
|
||||
#include <clientversion.h>
|
||||
#include <compat/cpuid.h>
|
||||
#include <crypto/sha512.h>
|
||||
#include <support/cleanse.h>
|
||||
#include <util/time.h> // for GetTime()
|
||||
#ifdef WIN32
|
||||
#include <compat.h> // for Windows API
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <sys/types.h> // must go before a number of other headers
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef __MACH__
|
||||
#include <mach/clock.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#endif
|
||||
#if HAVE_DECL_GETIFADDRS
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
#if HAVE_SYSCTL
|
||||
#include <sys/sysctl.h>
|
||||
#if HAVE_VM_VM_PARAM_H
|
||||
#include <vm/vm_param.h>
|
||||
#endif
|
||||
#if HAVE_SYS_RESOURCES_H
|
||||
#include <sys/resources.h>
|
||||
#endif
|
||||
#if HAVE_SYS_VMMETER_H
|
||||
#include <sys/vmmeter.h>
|
||||
#endif
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <sys/auxv.h>
|
||||
#endif
|
||||
|
||||
//! Necessary on some platforms
|
||||
extern char** environ;
|
||||
|
||||
namespace {
|
||||
|
||||
void RandAddSeedPerfmon(CSHA512& hasher)
|
||||
{
|
||||
#ifdef WIN32
|
||||
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
|
||||
// Seed with the entire set of perfmon data
|
||||
|
||||
// This can take up to 2 seconds, so only do it every 10 minutes
|
||||
static std::atomic<std::chrono::seconds> last_perfmon{std::chrono::seconds{0}};
|
||||
auto last_time = last_perfmon.load();
|
||||
auto current_time = GetTime<std::chrono::seconds>();
|
||||
if (current_time < last_time + std::chrono::minutes{10}) return;
|
||||
last_perfmon = current_time;
|
||||
|
||||
std::vector<unsigned char> vData(250000, 0);
|
||||
long ret = 0;
|
||||
unsigned long nSize = 0;
|
||||
const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data
|
||||
while (true) {
|
||||
nSize = vData.size();
|
||||
ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize);
|
||||
if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize)
|
||||
break;
|
||||
vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially
|
||||
}
|
||||
RegCloseKey(HKEY_PERFORMANCE_DATA);
|
||||
if (ret == ERROR_SUCCESS) {
|
||||
hasher.Write(vData.data(), nSize);
|
||||
memory_cleanse(vData.data(), nSize);
|
||||
} else {
|
||||
// Performance data is only a best-effort attempt at improving the
|
||||
// situation when the OS randomness (and other sources) aren't
|
||||
// adequate. As a result, failure to read it is isn't considered critical,
|
||||
// so we don't call RandFailure().
|
||||
// TODO: Add logging when the logger is made functional before global
|
||||
// constructors have been invoked.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Helper to easily feed data into a CSHA512.
|
||||
*
|
||||
* Note that this does not serialize the passed object (like stream.h's << operators do).
|
||||
* Its raw memory representation is used directly.
|
||||
*/
|
||||
template<typename T>
|
||||
CSHA512& operator<<(CSHA512& hasher, const T& data) {
|
||||
static_assert(!std::is_same<typename std::decay<T>::type, char*>::value, "Calling operator<<(CSHA512, char*) is probably not what you want");
|
||||
static_assert(!std::is_same<typename std::decay<T>::type, unsigned char*>::value, "Calling operator<<(CSHA512, unsigned char*) is probably not what you want");
|
||||
static_assert(!std::is_same<typename std::decay<T>::type, const char*>::value, "Calling operator<<(CSHA512, const char*) is probably not what you want");
|
||||
static_assert(!std::is_same<typename std::decay<T>::type, const unsigned char*>::value, "Calling operator<<(CSHA512, const unsigned char*) is probably not what you want");
|
||||
hasher.Write((const unsigned char*)&data, sizeof(data));
|
||||
return hasher;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
void AddSockaddr(CSHA512& hasher, const struct sockaddr *addr)
|
||||
{
|
||||
if (addr == nullptr) return;
|
||||
switch (addr->sa_family) {
|
||||
case AF_INET:
|
||||
hasher.Write((const unsigned char*)addr, sizeof(sockaddr_in));
|
||||
break;
|
||||
case AF_INET6:
|
||||
hasher.Write((const unsigned char*)addr, sizeof(sockaddr_in6));
|
||||
break;
|
||||
default:
|
||||
hasher.Write((const unsigned char*)&addr->sa_family, sizeof(addr->sa_family));
|
||||
}
|
||||
}
|
||||
|
||||
void AddFile(CSHA512& hasher, const char *path)
|
||||
{
|
||||
struct stat sb = {};
|
||||
int f = open(path, O_RDONLY);
|
||||
size_t total = 0;
|
||||
if (f != -1) {
|
||||
unsigned char fbuf[4096];
|
||||
int n;
|
||||
hasher.Write((const unsigned char*)&f, sizeof(f));
|
||||
if (fstat(f, &sb) == 0) hasher << sb;
|
||||
do {
|
||||
n = read(f, fbuf, sizeof(fbuf));
|
||||
if (n > 0) hasher.Write(fbuf, n);
|
||||
total += n;
|
||||
/* not bothering with EINTR handling. */
|
||||
} while (n == sizeof(fbuf) && total < 1048576); // Read only the first 1 Mbyte
|
||||
close(f);
|
||||
}
|
||||
}
|
||||
|
||||
void AddPath(CSHA512& hasher, const char *path)
|
||||
{
|
||||
struct stat sb = {};
|
||||
if (stat(path, &sb) == 0) {
|
||||
hasher.Write((const unsigned char*)path, strlen(path) + 1);
|
||||
hasher << sb;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_SYSCTL
|
||||
template<int... S>
|
||||
void AddSysctl(CSHA512& hasher)
|
||||
{
|
||||
int CTL[sizeof...(S)] = {S...};
|
||||
unsigned char buffer[65536];
|
||||
size_t siz = 65536;
|
||||
int ret = sysctl(CTL, sizeof...(S), buffer, &siz, nullptr, 0);
|
||||
if (ret == 0 || (ret == -1 && errno == ENOMEM)) {
|
||||
hasher << sizeof(CTL);
|
||||
hasher.Write((const unsigned char*)CTL, sizeof(CTL));
|
||||
if (siz > sizeof(buffer)) siz = sizeof(buffer);
|
||||
hasher << siz;
|
||||
hasher.Write(buffer, siz);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETCPUID
|
||||
void inline AddCPUID(CSHA512& hasher, uint32_t leaf, uint32_t subleaf, uint32_t& ax, uint32_t& bx, uint32_t& cx, uint32_t& dx)
|
||||
{
|
||||
GetCPUID(leaf, subleaf, ax, bx, cx, dx);
|
||||
hasher << leaf << subleaf << ax << bx << cx << dx;
|
||||
}
|
||||
|
||||
void AddAllCPUID(CSHA512& hasher)
|
||||
{
|
||||
uint32_t ax, bx, cx, dx;
|
||||
// Iterate over all standard leaves
|
||||
AddCPUID(hasher, 0, 0, ax, bx, cx, dx); // Returns max leaf in ax
|
||||
uint32_t max = ax;
|
||||
for (uint32_t leaf = 1; leaf <= max; ++leaf) {
|
||||
for (uint32_t subleaf = 0;; ++subleaf) {
|
||||
AddCPUID(hasher, leaf, subleaf, ax, bx, cx, dx);
|
||||
// Iterate over subleaves for leaf 4, 11, 13
|
||||
if (leaf != 4 && leaf != 11 && leaf != 13) break;
|
||||
if ((leaf == 4 || leaf == 13) && ax == 0) break;
|
||||
if (leaf == 11 && (cx & 0xFF00) == 0) break;
|
||||
}
|
||||
}
|
||||
// Iterate over all extended leaves
|
||||
AddCPUID(hasher, 0x80000000, 0, ax, bx, cx, dx); // Returns max extended leaf in ax
|
||||
uint32_t ext_max = ax;
|
||||
for (uint32_t leaf = 0x80000001; leaf <= ext_max; ++leaf) {
|
||||
AddCPUID(hasher, leaf, 0, ax, bx, cx, dx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
void RandAddDynamicEnv(CSHA512& hasher)
|
||||
{
|
||||
RandAddSeedPerfmon(hasher);
|
||||
|
||||
// Various clocks
|
||||
#ifdef WIN32
|
||||
FILETIME ftime;
|
||||
GetSystemTimeAsFileTime(&ftime);
|
||||
hasher << ftime;
|
||||
#else
|
||||
# ifndef __MACH__
|
||||
// On non-MacOS systems, use various clock_gettime() calls.
|
||||
struct timespec ts = {};
|
||||
# ifdef CLOCK_MONOTONIC
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
hasher << ts;
|
||||
# endif
|
||||
# ifdef CLOCK_REALTIME
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
hasher << ts;
|
||||
# endif
|
||||
# ifdef CLOCK_BOOTTIME
|
||||
clock_gettime(CLOCK_BOOTTIME, &ts);
|
||||
hasher << ts;
|
||||
# endif
|
||||
# else
|
||||
// On MacOS use mach_absolute_time (number of CPU ticks since boot) as a replacement for CLOCK_MONOTONIC,
|
||||
// and clock_get_time for CALENDAR_CLOCK as a replacement for CLOCK_REALTIME.
|
||||
hasher << mach_absolute_time();
|
||||
// From https://gist.github.com/jbenet/1087739
|
||||
clock_serv_t cclock;
|
||||
mach_timespec_t mts = {};
|
||||
if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock) == KERN_SUCCESS && clock_get_time(cclock, &mts) == KERN_SUCCESS) {
|
||||
hasher << mts;
|
||||
mach_port_deallocate(mach_task_self(), cclock);
|
||||
}
|
||||
# endif
|
||||
// gettimeofday is available on all UNIX systems, but only has microsecond precision.
|
||||
struct timeval tv = {};
|
||||
gettimeofday(&tv, nullptr);
|
||||
hasher << tv;
|
||||
#endif
|
||||
// Probably redundant, but also use all the clocks C++11 provides:
|
||||
hasher << std::chrono::system_clock::now().time_since_epoch().count();
|
||||
hasher << std::chrono::steady_clock::now().time_since_epoch().count();
|
||||
hasher << std::chrono::high_resolution_clock::now().time_since_epoch().count();
|
||||
|
||||
#ifndef WIN32
|
||||
// Current resource usage.
|
||||
struct rusage usage = {};
|
||||
if (getrusage(RUSAGE_SELF, &usage) == 0) hasher << usage;
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
AddFile(hasher, "/proc/diskstats");
|
||||
AddFile(hasher, "/proc/vmstat");
|
||||
AddFile(hasher, "/proc/schedstat");
|
||||
AddFile(hasher, "/proc/zoneinfo");
|
||||
AddFile(hasher, "/proc/meminfo");
|
||||
AddFile(hasher, "/proc/softirqs");
|
||||
AddFile(hasher, "/proc/stat");
|
||||
AddFile(hasher, "/proc/self/schedstat");
|
||||
AddFile(hasher, "/proc/self/status");
|
||||
#endif
|
||||
|
||||
#if HAVE_SYSCTL
|
||||
# ifdef CTL_KERN
|
||||
# if defined(KERN_PROC) && defined(KERN_PROC_ALL)
|
||||
AddSysctl<CTL_KERN, KERN_PROC, KERN_PROC_ALL>(hasher);
|
||||
# endif
|
||||
# endif
|
||||
# ifdef CTL_HW
|
||||
# ifdef HW_DISKSTATS
|
||||
AddSysctl<CTL_HW, HW_DISKSTATS>(hasher);
|
||||
# endif
|
||||
# endif
|
||||
# ifdef CTL_VM
|
||||
# ifdef VM_LOADAVG
|
||||
AddSysctl<CTL_VM, VM_LOADAVG>(hasher);
|
||||
# endif
|
||||
# ifdef VM_TOTAL
|
||||
AddSysctl<CTL_VM, VM_TOTAL>(hasher);
|
||||
# endif
|
||||
# ifdef VM_METER
|
||||
AddSysctl<CTL_VM, VM_METER>(hasher);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Stack and heap location
|
||||
void* addr = malloc(4097);
|
||||
hasher << &addr << addr;
|
||||
free(addr);
|
||||
}
|
||||
|
||||
void RandAddStaticEnv(CSHA512& hasher)
|
||||
{
|
||||
// Some compile-time static properties
|
||||
hasher << (CHAR_MIN < 0) << sizeof(void*) << sizeof(long) << sizeof(int);
|
||||
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
|
||||
hasher << __GNUC__ << __GNUC_MINOR__ << __GNUC_PATCHLEVEL__;
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
hasher << _MSC_VER;
|
||||
#endif
|
||||
hasher << __cplusplus;
|
||||
#ifdef _XOPEN_VERSION
|
||||
hasher << _XOPEN_VERSION;
|
||||
#endif
|
||||
#ifdef __VERSION__
|
||||
const char* COMPILER_VERSION = __VERSION__;
|
||||
hasher.Write((const unsigned char*)COMPILER_VERSION, strlen(COMPILER_VERSION) + 1);
|
||||
#endif
|
||||
|
||||
// Bitcoin client version
|
||||
hasher << CLIENT_VERSION;
|
||||
|
||||
#ifdef __linux__
|
||||
// Information available through getauxval()
|
||||
# ifdef AT_HWCAP
|
||||
hasher << getauxval(AT_HWCAP);
|
||||
# endif
|
||||
# ifdef AT_HWCAP2
|
||||
hasher << getauxval(AT_HWCAP2);
|
||||
# endif
|
||||
# ifdef AT_RANDOM
|
||||
const unsigned char* random_aux = (const unsigned char*)getauxval(AT_RANDOM);
|
||||
if (random_aux) hasher.Write(random_aux, 16);
|
||||
# endif
|
||||
# ifdef AT_PLATFORM
|
||||
const char* platform_str = (const char*)getauxval(AT_PLATFORM);
|
||||
if (platform_str) hasher.Write((const unsigned char*)platform_str, strlen(platform_str) + 1);
|
||||
# endif
|
||||
# ifdef AT_EXECFN
|
||||
const char* exec_str = (const char*)getauxval(AT_EXECFN);
|
||||
if (exec_str) hasher.Write((const unsigned char*)exec_str, strlen(exec_str) + 1);
|
||||
# endif
|
||||
#endif // __linux__
|
||||
|
||||
#ifdef HAVE_GETCPUID
|
||||
AddAllCPUID(hasher);
|
||||
#endif
|
||||
|
||||
// Memory locations
|
||||
hasher << &hasher << &RandAddStaticEnv << &malloc << &errno << &environ;
|
||||
|
||||
// Hostname
|
||||
char hname[256];
|
||||
if (gethostname(hname, 256) == 0) {
|
||||
hasher.Write((const unsigned char*)hname, strnlen(hname, 256));
|
||||
}
|
||||
|
||||
#if HAVE_DECL_GETIFADDRS
|
||||
// Network interfaces
|
||||
struct ifaddrs *ifad = NULL;
|
||||
getifaddrs(&ifad);
|
||||
struct ifaddrs *ifit = ifad;
|
||||
while (ifit != NULL) {
|
||||
hasher.Write((const unsigned char*)&ifit, sizeof(ifit));
|
||||
hasher.Write((const unsigned char*)ifit->ifa_name, strlen(ifit->ifa_name) + 1);
|
||||
hasher.Write((const unsigned char*)&ifit->ifa_flags, sizeof(ifit->ifa_flags));
|
||||
AddSockaddr(hasher, ifit->ifa_addr);
|
||||
AddSockaddr(hasher, ifit->ifa_netmask);
|
||||
AddSockaddr(hasher, ifit->ifa_dstaddr);
|
||||
ifit = ifit->ifa_next;
|
||||
}
|
||||
freeifaddrs(ifad);
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
// UNIX kernel information
|
||||
struct utsname name;
|
||||
if (uname(&name) != -1) {
|
||||
hasher.Write((const unsigned char*)&name.sysname, strlen(name.sysname) + 1);
|
||||
hasher.Write((const unsigned char*)&name.nodename, strlen(name.nodename) + 1);
|
||||
hasher.Write((const unsigned char*)&name.release, strlen(name.release) + 1);
|
||||
hasher.Write((const unsigned char*)&name.version, strlen(name.version) + 1);
|
||||
hasher.Write((const unsigned char*)&name.machine, strlen(name.machine) + 1);
|
||||
}
|
||||
|
||||
/* Path and filesystem provided data */
|
||||
AddPath(hasher, "/");
|
||||
AddPath(hasher, ".");
|
||||
AddPath(hasher, "/tmp");
|
||||
AddPath(hasher, "/home");
|
||||
AddPath(hasher, "/proc");
|
||||
#ifdef __linux__
|
||||
AddFile(hasher, "/proc/cmdline");
|
||||
AddFile(hasher, "/proc/cpuinfo");
|
||||
AddFile(hasher, "/proc/version");
|
||||
#endif
|
||||
AddFile(hasher, "/etc/passwd");
|
||||
AddFile(hasher, "/etc/group");
|
||||
AddFile(hasher, "/etc/hosts");
|
||||
AddFile(hasher, "/etc/resolv.conf");
|
||||
AddFile(hasher, "/etc/timezone");
|
||||
AddFile(hasher, "/etc/localtime");
|
||||
#endif
|
||||
|
||||
// For MacOS/BSDs, gather data through sysctl instead of /proc. Not all of these
|
||||
// will exist on every system.
|
||||
#if HAVE_SYSCTL
|
||||
# ifdef CTL_HW
|
||||
# ifdef HW_MACHINE
|
||||
AddSysctl<CTL_HW, HW_MACHINE>(hasher);
|
||||
# endif
|
||||
# ifdef HW_MODEL
|
||||
AddSysctl<CTL_HW, HW_MODEL>(hasher);
|
||||
# endif
|
||||
# ifdef HW_NCPU
|
||||
AddSysctl<CTL_HW, HW_NCPU>(hasher);
|
||||
# endif
|
||||
# ifdef HW_PHYSMEM
|
||||
AddSysctl<CTL_HW, HW_PHYSMEM>(hasher);
|
||||
# endif
|
||||
# ifdef HW_USERMEM
|
||||
AddSysctl<CTL_HW, HW_USERMEM>(hasher);
|
||||
# endif
|
||||
# ifdef HW_MACHINE_ARCH
|
||||
AddSysctl<CTL_HW, HW_MACHINE_ARCH>(hasher);
|
||||
# endif
|
||||
# ifdef HW_REALMEM
|
||||
AddSysctl<CTL_HW, HW_REALMEM>(hasher);
|
||||
# endif
|
||||
# ifdef HW_CPU_FREQ
|
||||
AddSysctl<CTL_HW, HW_CPU_FREQ>(hasher);
|
||||
# endif
|
||||
# ifdef HW_BUS_FREQ
|
||||
AddSysctl<CTL_HW, HW_BUS_FREQ>(hasher);
|
||||
# endif
|
||||
# ifdef HW_CACHELINE
|
||||
AddSysctl<CTL_HW, HW_CACHELINE>(hasher);
|
||||
# endif
|
||||
# endif
|
||||
# ifdef CTL_KERN
|
||||
# ifdef KERN_BOOTFILE
|
||||
AddSysctl<CTL_KERN, KERN_BOOTFILE>(hasher);
|
||||
# endif
|
||||
# ifdef KERN_BOOTTIME
|
||||
AddSysctl<CTL_KERN, KERN_BOOTTIME>(hasher);
|
||||
# endif
|
||||
# ifdef KERN_CLOCKRATE
|
||||
AddSysctl<CTL_KERN, KERN_CLOCKRATE>(hasher);
|
||||
# endif
|
||||
# ifdef KERN_HOSTID
|
||||
AddSysctl<CTL_KERN, KERN_HOSTID>(hasher);
|
||||
# endif
|
||||
# ifdef KERN_HOSTUUID
|
||||
AddSysctl<CTL_KERN, KERN_HOSTUUID>(hasher);
|
||||
# endif
|
||||
# ifdef KERN_HOSTNAME
|
||||
AddSysctl<CTL_KERN, KERN_HOSTNAME>(hasher);
|
||||
# endif
|
||||
# ifdef KERN_OSRELDATE
|
||||
AddSysctl<CTL_KERN, KERN_OSRELDATE>(hasher);
|
||||
# endif
|
||||
# ifdef KERN_OSRELEASE
|
||||
AddSysctl<CTL_KERN, KERN_OSRELEASE>(hasher);
|
||||
# endif
|
||||
# ifdef KERN_OSREV
|
||||
AddSysctl<CTL_KERN, KERN_OSREV>(hasher);
|
||||
# endif
|
||||
# ifdef KERN_OSTYPE
|
||||
AddSysctl<CTL_KERN, KERN_OSTYPE>(hasher);
|
||||
# endif
|
||||
# ifdef KERN_POSIX1
|
||||
AddSysctl<CTL_KERN, KERN_OSREV>(hasher);
|
||||
# endif
|
||||
# ifdef KERN_VERSION
|
||||
AddSysctl<CTL_KERN, KERN_VERSION>(hasher);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Env variables
|
||||
if (environ) {
|
||||
for (size_t i = 0; environ[i]; ++i) {
|
||||
hasher.Write((const unsigned char*)environ[i], strlen(environ[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Process, thread, user, session, group, ... ids.
|
||||
#ifdef WIN32
|
||||
hasher << GetCurrentProcessId() << GetCurrentThreadId();
|
||||
#else
|
||||
hasher << getpid() << getppid() << getsid(0) << getpgid(0) << getuid() << geteuid() << getgid() << getegid();
|
||||
#endif
|
||||
hasher << std::this_thread::get_id();
|
||||
}
|
17
src/randomenv.h
Normal file
17
src/randomenv.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2019 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_RANDOMENV_H
|
||||
#define BITCOIN_RANDOMENV_H
|
||||
|
||||
#include <crypto/sha512.h>
|
||||
|
||||
/** Gather non-cryptographic environment data that changes over time. */
|
||||
void RandAddDynamicEnv(CSHA512& hasher);
|
||||
|
||||
/** Gather non-cryptographic environment data that does not change over time. */
|
||||
void RandAddStaticEnv(CSHA512& hasher);
|
||||
|
||||
#endif
|
|
@ -41,8 +41,6 @@ void CScheduler::serviceQueue()
|
|||
try {
|
||||
if (!shouldStop() && taskQueue.empty()) {
|
||||
reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock);
|
||||
// Use this chance to get more entropy
|
||||
RandAddSeedSleep();
|
||||
}
|
||||
while (!shouldStop() && taskQueue.empty()) {
|
||||
// Wait until there is something to do.
|
||||
|
|
Loading…
Add table
Reference in a new issue