Integrate util/system's CInit into RNGState
This guarantees that OpenSSL is initialized properly whenever randomness is used, even when that randomness is invoked from global constructors. Note that this patch uses Mutex directly, rather than CCriticalSection. This is because the lock-detection code is not necessarily initialized during global constructors.
This commit is contained in:
parent
2ccc3d3aa3
commit
16e40a8b56
2 changed files with 43 additions and 51 deletions
|
@ -47,6 +47,7 @@
|
|||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/conf.h>
|
||||
|
||||
[[noreturn]] static void RandFailure()
|
||||
{
|
||||
|
@ -294,15 +295,46 @@ void GetRandBytes(unsigned char* buf, int num)
|
|||
}
|
||||
}
|
||||
|
||||
void LockingCallbackOpenSSL(int mode, int i, const char* file, int line);
|
||||
|
||||
namespace {
|
||||
|
||||
struct RNGState {
|
||||
Mutex m_mutex;
|
||||
unsigned char m_state[32] GUARDED_BY(m_mutex) = {0};
|
||||
uint64_t m_counter GUARDED_BY(m_mutex) = 0;
|
||||
std::unique_ptr<Mutex[]> m_mutex_openssl;
|
||||
|
||||
RNGState()
|
||||
{
|
||||
InitHardwareRand();
|
||||
|
||||
// Init OpenSSL library multithreading support
|
||||
m_mutex_openssl.reset(new Mutex[CRYPTO_num_locks()]);
|
||||
CRYPTO_set_locking_callback(LockingCallbackOpenSSL);
|
||||
|
||||
// OpenSSL can optionally load a config file which lists optional loadable modules and engines.
|
||||
// We don't use them so we don't require the config. However some of our libs may call functions
|
||||
// which attempt to load the config file, possibly resulting in an exit() or crash if it is missing
|
||||
// or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be
|
||||
// that the config appears to have been loaded and there are no modules/engines available.
|
||||
OPENSSL_no_config();
|
||||
|
||||
#ifdef WIN32
|
||||
// Seed OpenSSL PRNG with current contents of the screen
|
||||
RAND_screen();
|
||||
#endif
|
||||
|
||||
// Seed OpenSSL PRNG with performance counter
|
||||
RandAddSeed();
|
||||
}
|
||||
|
||||
~RNGState()
|
||||
{
|
||||
// Securely erase the memory used by the OpenSSL PRNG
|
||||
RAND_cleanup();
|
||||
// Shutdown OpenSSL library multithreading support
|
||||
CRYPTO_set_locking_callback(nullptr);
|
||||
}
|
||||
|
||||
/** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher. */
|
||||
|
@ -343,6 +375,17 @@ RNGState& GetRNGState()
|
|||
}
|
||||
}
|
||||
|
||||
void LockingCallbackOpenSSL(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS
|
||||
{
|
||||
RNGState& rng = GetRNGState();
|
||||
|
||||
if (mode & CRYPTO_LOCK) {
|
||||
rng.m_mutex_openssl[i].lock();
|
||||
} else {
|
||||
rng.m_mutex_openssl[i].unlock();
|
||||
}
|
||||
}
|
||||
|
||||
static void AddDataToRng(void* data, size_t len, RNGState& rng);
|
||||
|
||||
void RandAddSeedSleep()
|
||||
|
|
|
@ -73,9 +73,6 @@
|
|||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/conf.h>
|
||||
#include <thread>
|
||||
|
||||
// Application startup time (used for uptime calculation)
|
||||
|
@ -86,54 +83,6 @@ const char * const BITCOIN_PID_FILENAME = "bitcoind.pid";
|
|||
|
||||
ArgsManager gArgs;
|
||||
|
||||
/** Init OpenSSL library multithreading support */
|
||||
static std::unique_ptr<CCriticalSection[]> ppmutexOpenSSL;
|
||||
void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS
|
||||
{
|
||||
if (mode & CRYPTO_LOCK) {
|
||||
ENTER_CRITICAL_SECTION(ppmutexOpenSSL[i]);
|
||||
} else {
|
||||
LEAVE_CRITICAL_SECTION(ppmutexOpenSSL[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton for wrapping OpenSSL setup/teardown.
|
||||
class CInit
|
||||
{
|
||||
public:
|
||||
CInit()
|
||||
{
|
||||
// Init OpenSSL library multithreading support
|
||||
ppmutexOpenSSL.reset(new CCriticalSection[CRYPTO_num_locks()]);
|
||||
CRYPTO_set_locking_callback(locking_callback);
|
||||
|
||||
// OpenSSL can optionally load a config file which lists optional loadable modules and engines.
|
||||
// We don't use them so we don't require the config. However some of our libs may call functions
|
||||
// which attempt to load the config file, possibly resulting in an exit() or crash if it is missing
|
||||
// or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be
|
||||
// that the config appears to have been loaded and there are no modules/engines available.
|
||||
OPENSSL_no_config();
|
||||
|
||||
#ifdef WIN32
|
||||
// Seed OpenSSL PRNG with current contents of the screen
|
||||
RAND_screen();
|
||||
#endif
|
||||
|
||||
// Seed OpenSSL PRNG with performance counter
|
||||
RandAddSeed();
|
||||
}
|
||||
~CInit()
|
||||
{
|
||||
// Securely erase the memory used by the PRNG
|
||||
RAND_cleanup();
|
||||
// Shutdown OpenSSL library multithreading support
|
||||
CRYPTO_set_locking_callback(nullptr);
|
||||
// Clear the set of locks now to maintain symmetry with the constructor.
|
||||
ppmutexOpenSSL.reset();
|
||||
}
|
||||
}
|
||||
instance_of_cinit;
|
||||
|
||||
/** A map that contains all the currently held directory locks. After
|
||||
* successful locking, these will be held here until the global destructor
|
||||
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
|
||||
|
|
Loading…
Reference in a new issue