Merge pull request #5152 from vijaydasmp/bp21_14

backport: Merge bitcoin#18814, 18781
This commit is contained in:
PastaPastaPasta 2023-12-05 12:48:51 -06:00 committed by pasta
parent 3e5a6ef649
commit 234d090045
No known key found for this signature in database
GPG Key ID: 52527BEDABE87984
6 changed files with 35 additions and 23 deletions

View File

@ -14,16 +14,14 @@
#include <wincrypt.h> #include <wincrypt.h>
#endif #endif
#include <logging.h> // for LogPrintf() #include <logging.h> // for LogPrintf()
#include <randomenv.h>
#include <support/allocators/secure.h>
#include <sync.h> // for Mutex #include <sync.h> // for Mutex
#include <util/time.h> // for GetTimeMicros() #include <util/time.h> // for GetTimeMicros()
#include <stdlib.h> #include <stdlib.h>
#include <thread> #include <thread>
#include <randomenv.h>
#include <support/allocators/secure.h>
#ifndef WIN32 #ifndef WIN32
#include <fcntl.h> #include <fcntl.h>
#endif #endif
@ -582,16 +580,6 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) noexcept
} }
} }
std::chrono::microseconds GetRandMicros(std::chrono::microseconds duration_max) noexcept
{
return std::chrono::microseconds{GetRand(duration_max.count())};
}
std::chrono::milliseconds GetRandMillis(std::chrono::milliseconds duration_max) noexcept
{
return std::chrono::milliseconds{GetRand(duration_max.count())};
}
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); }

View File

@ -69,9 +69,21 @@
* Thread-safe. * Thread-safe.
*/ */
void GetRandBytes(unsigned char* buf, int num) noexcept; void GetRandBytes(unsigned char* buf, int num) noexcept;
/** Generate a uniform random integer in the range [0..range). Precondition: range > 0 */
uint64_t GetRand(uint64_t nMax) noexcept; uint64_t GetRand(uint64_t nMax) noexcept;
std::chrono::microseconds GetRandMicros(std::chrono::microseconds duration_max) noexcept; /** Generate a uniform random duration in the range [0..max). Precondition: max.count() > 0 */
std::chrono::milliseconds GetRandMillis(std::chrono::milliseconds duration_max) noexcept; template <typename D>
D GetRandomDuration(typename std::common_type<D>::type max) noexcept
// Having the compiler infer the template argument from the function argument
// is dangerous, because the desired return value generally has a different
// type than the function argument. So std::common_type is used to force the
// call site to specify the type of the return value.
{
assert(max.count() > 0);
return D{GetRand(max.count())};
};
constexpr auto GetRandMicros = GetRandomDuration<std::chrono::microseconds>;
constexpr auto GetRandMillis = GetRandomDuration<std::chrono::milliseconds>;
int GetRandInt(int nMax) noexcept; int GetRandInt(int nMax) noexcept;
uint256 GetRandHash() noexcept; uint256 GetRandHash() noexcept;

View File

@ -28,7 +28,8 @@ static std::string rpcWarmupStatus GUARDED_BY(g_rpc_warmup_mutex) = "RPC server
/* Timer-creating functions */ /* Timer-creating functions */
static RPCTimerInterface* timerInterface = nullptr; static RPCTimerInterface* timerInterface = nullptr;
/* Map of name to timer. */ /* Map of name to timer. */
static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers; static Mutex g_deadline_timers_mutex;
static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers GUARDED_BY(g_deadline_timers_mutex);
static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler, std::multimap<std::string, std::vector<UniValue>> mapPlatformRestrictions); static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler, std::multimap<std::string, std::vector<UniValue>> mapPlatformRestrictions);
// Any commands submitted by this user will have their commands filtered based on the mapPlatformRestrictions // Any commands submitted by this user will have their commands filtered based on the mapPlatformRestrictions
@ -330,7 +331,7 @@ void InterruptRPC()
void StopRPC() void StopRPC()
{ {
LogPrint(BCLog::RPC, "Stopping RPC\n"); LogPrint(BCLog::RPC, "Stopping RPC\n");
deadlineTimers.clear(); WITH_LOCK(g_deadline_timers_mutex, deadlineTimers.clear());
DeleteAuthCookie(); DeleteAuthCookie();
g_rpcSignals.Stopped(); g_rpcSignals.Stopped();
} }
@ -609,6 +610,7 @@ void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nS
{ {
if (!timerInterface) if (!timerInterface)
throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
LOCK(g_deadline_timers_mutex);
deadlineTimers.erase(name); deadlineTimers.erase(name);
LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
deadlineTimers.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000))); deadlineTimers.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)));

View File

@ -27,6 +27,8 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
for (int i = 10; i > 0; --i) { for (int i = 10; i > 0; --i) {
BOOST_CHECK_EQUAL(GetRand(std::numeric_limits<uint64_t>::max()), uint64_t{10393729187455219830U}); BOOST_CHECK_EQUAL(GetRand(std::numeric_limits<uint64_t>::max()), uint64_t{10393729187455219830U});
BOOST_CHECK_EQUAL(GetRandInt(std::numeric_limits<int>::max()), int{769702006}); BOOST_CHECK_EQUAL(GetRandInt(std::numeric_limits<int>::max()), int{769702006});
BOOST_CHECK_EQUAL(GetRandMicros(std::chrono::hours{1}).count(), 2917185654);
BOOST_CHECK_EQUAL(GetRandMillis(std::chrono::hours{1}).count(), 2144374);
} }
{ {
constexpr SteadySeconds time_point{1s}; constexpr SteadySeconds time_point{1s};
@ -66,6 +68,8 @@ BOOST_AUTO_TEST_CASE(fastrandom_tests)
for (int i = 10; i > 0; --i) { for (int i = 10; i > 0; --i) {
BOOST_CHECK(GetRand(std::numeric_limits<uint64_t>::max()) != uint64_t{10393729187455219830U}); BOOST_CHECK(GetRand(std::numeric_limits<uint64_t>::max()) != uint64_t{10393729187455219830U});
BOOST_CHECK(GetRandInt(std::numeric_limits<int>::max()) != int{769702006}); BOOST_CHECK(GetRandInt(std::numeric_limits<int>::max()) != int{769702006});
BOOST_CHECK(GetRandMicros(std::chrono::hours{1}) != std::chrono::microseconds{2917185654});
BOOST_CHECK(GetRandMillis(std::chrono::hours{1}) != std::chrono::milliseconds{2144374});
} }
{ {
@ -117,7 +121,6 @@ BOOST_AUTO_TEST_CASE(stdrandom_test)
BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end()); BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
} }
} }
} }
/** Test that Shuffle reaches every permutation with equal probability. */ /** Test that Shuffle reaches every permutation with equal probability. */

View File

@ -1927,6 +1927,9 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
CWallet* const pwallet = wallet.get(); CWallet* const pwallet = wallet.get();
int64_t nSleepTime; int64_t nSleepTime;
int64_t relock_time;
// Prevent concurrent calls to walletpassphrase with the same wallet.
LOCK(pwallet->m_unlock_mutex);
{ {
LOCK(pwallet->cs_wallet); LOCK(pwallet->cs_wallet);
@ -1975,7 +1978,7 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
pwallet->TopUpKeyPool(); pwallet->TopUpKeyPool();
pwallet->nRelockTime = GetTime() + nSleepTime; pwallet->nRelockTime = GetTime() + nSleepTime;
relock_time = pwallet->nRelockTime;
} }
// rpcRunLater must be called without cs_wallet held otherwise a deadlock // rpcRunLater must be called without cs_wallet held otherwise a deadlock
// can occur. The deadlock would happen when RPCRunLater removes the // can occur. The deadlock would happen when RPCRunLater removes the
@ -1986,9 +1989,11 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
// wallet before the following callback is called. If a valid shared pointer // wallet before the following callback is called. If a valid shared pointer
// is acquired in the callback then the wallet is still loaded. // is acquired in the callback then the wallet is still loaded.
std::weak_ptr<CWallet> weak_wallet = wallet; std::weak_ptr<CWallet> weak_wallet = wallet;
pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet] { pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] {
if (auto shared_wallet = weak_wallet.lock()) { if (auto shared_wallet = weak_wallet.lock()) {
LOCK(shared_wallet->cs_wallet); LOCK(shared_wallet->cs_wallet);
// Skip if this is not the most recent rpcRunLater callback.
if (shared_wallet->nRelockTime != relock_time) return;
shared_wallet->Lock(); shared_wallet->Lock();
shared_wallet->nRelockTime = 0; shared_wallet->nRelockTime = 0;
} }

View File

@ -965,8 +965,10 @@ public:
std::vector<std::string> GetDestValues(const std::string& prefix) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); std::vector<std::string> GetDestValues(const std::string& prefix) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock(). //! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock().
int64_t nRelockTime = 0; int64_t nRelockTime GUARDED_BY(cs_wallet){0};
// Used to prevent concurrent calls to walletpassphrase RPC.
Mutex m_unlock_mutex;
bool Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnly = false, bool accept_no_keys = false); bool Unlock(const SecureString& strWalletPassphrase, bool fForMixingOnly = false, bool accept_no_keys = false);
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase);