Skip to content

Commit

Permalink
Integrate util/system's CInit into RNGState
Browse files Browse the repository at this point in the history
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.

Cherry-picked from: 16e40a8
  • Loading branch information
xanimo committed Apr 3, 2024
1 parent b2a9f0a commit 5034503
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 54 deletions.
51 changes: 48 additions & 3 deletions src/random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include <wincrypt.h>
#endif
#include "util.h" // for LogPrint()
#include "sync.h" // for LOCK
#include "sync.h" // for WAIT_LOCK
#include "utiltime.h" // for GetTime()

#include <stdlib.h>
Expand Down Expand Up @@ -44,8 +44,11 @@
#include <cpuid.h>
#endif

#include <boost/thread/mutex.hpp>

#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/conf.h>

static void RandFailure()
{
Expand Down Expand Up @@ -289,15 +292,46 @@ void GetRandBytes(unsigned char* buf, int num)
}
}

void LockingCallbackOpenSSL(int mode, int i, const char* file, int line);

namespace {

struct RNGState {
CCriticalSection m_mutex;
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. */
Expand All @@ -307,7 +341,7 @@ struct RNGState {
unsigned char buf[64];
static_assert(sizeof(buf) == CSHA512::OUTPUT_SIZE, "Buffer needs to have hasher's output size");
{
LOCK(m_mutex);
WAIT_LOCK(m_mutex);
// Write the current state of the RNG into the hasher
hasher.Write(m_state, 32);
// Write a new counter number into the state
Expand Down Expand Up @@ -338,6 +372,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()
Expand Down
51 changes: 0 additions & 51 deletions src/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,57 +104,6 @@ bool fLogIPs = DEFAULT_LOGIPS;
std::atomic<bool> fReopenDebugLog(false);
CTranslationInterface translationInterface;

/** Init OpenSSL library multithreading support */
static 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]);
}
}

// Init
class CInit
{
public:
CInit()
{
// Init OpenSSL library multithreading support
ppmutexOpenSSL = (CCriticalSection**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(CCriticalSection*));
for (int i = 0; i < CRYPTO_num_locks(); i++)
ppmutexOpenSSL[i] = new CCriticalSection();
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(NULL);
for (int i = 0; i < CRYPTO_num_locks(); i++)
delete ppmutexOpenSSL[i];
OPENSSL_free(ppmutexOpenSSL);
}
}
instance_of_cinit;

/**
* LogPrintf() has been broken a couple of times now
* by well-meaning people adding mutexes in the most straightforward way.
Expand Down

0 comments on commit 5034503

Please sign in to comment.