Skip to content

Commit a33901c

Browse files
committed
Merge #18814: rpc: Relock wallet only if most recent callback
9f59dde rpc: Relock wallet only if most recent callback (João Barbosa) a2e6db5 rpc: Add mutex to guard deadlineTimers (João Barbosa) Pull request description: This PR fixes an early relocking race condition from #18811 where old relock callback runs after new wallet unlock code and nRelockTime update but before rpcRunLater call, causing early relock and incorrect nRelockTime time Issue introduced in #18487. Fixes #18811. ACKs for top commit: MarcoFalke: ACK 9f59dde ryanofsky: Code review ACK 9f59dde. No changes since last review except squashing commits. jonatack: ACK 9f59dde Tree-SHA512: 2f7fc03e5ab6037337f2d82dfad432495cc337c77d07c968ee2355105db6292f24543c03456f5402e0e759577a4327758f9372f7ea29de6d56dc3695fda9b379
2 parents 246e878 + 9f59dde commit a33901c

File tree

3 files changed

+14
-4
lines changed

3 files changed

+14
-4
lines changed

src/rpc/server.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server starte
2525
/* Timer-creating functions */
2626
static RPCTimerInterface* timerInterface = nullptr;
2727
/* Map of name to timer. */
28-
static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers;
28+
static Mutex g_deadline_timers_mutex;
29+
static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers GUARDED_BY(g_deadline_timers_mutex);
2930
static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler);
3031

3132
struct RPCCommandExecutionInfo
@@ -298,7 +299,7 @@ void InterruptRPC()
298299
void StopRPC()
299300
{
300301
LogPrint(BCLog::RPC, "Stopping RPC\n");
301-
deadlineTimers.clear();
302+
WITH_LOCK(g_deadline_timers_mutex, deadlineTimers.clear());
302303
DeleteAuthCookie();
303304
g_rpcSignals.Stopped();
304305
}
@@ -486,6 +487,7 @@ void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nS
486487
{
487488
if (!timerInterface)
488489
throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
490+
LOCK(g_deadline_timers_mutex);
489491
deadlineTimers.erase(name);
490492
LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
491493
deadlineTimers.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)));

src/wallet/rpcwallet.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1892,6 +1892,9 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
18921892
}.Check(request);
18931893

18941894
int64_t nSleepTime;
1895+
int64_t relock_time;
1896+
// Prevent concurrent calls to walletpassphrase with the same wallet.
1897+
LOCK(pwallet->m_unlock_mutex);
18951898
{
18961899
LOCK(pwallet->cs_wallet);
18971900

@@ -1929,6 +1932,7 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
19291932
pwallet->TopUpKeyPool();
19301933

19311934
pwallet->nRelockTime = GetTime() + nSleepTime;
1935+
relock_time = pwallet->nRelockTime;
19321936
}
19331937

19341938
// rpcRunLater must be called without cs_wallet held otherwise a deadlock
@@ -1940,9 +1944,11 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
19401944
// wallet before the following callback is called. If a valid shared pointer
19411945
// is acquired in the callback then the wallet is still loaded.
19421946
std::weak_ptr<CWallet> weak_wallet = wallet;
1943-
pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet] {
1947+
pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] {
19441948
if (auto shared_wallet = weak_wallet.lock()) {
19451949
LOCK(shared_wallet->cs_wallet);
1950+
// Skip if this is not the most recent rpcRunLater callback.
1951+
if (shared_wallet->nRelockTime != relock_time) return;
19461952
shared_wallet->Lock();
19471953
shared_wallet->nRelockTime = 0;
19481954
}

src/wallet/wallet.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -871,8 +871,10 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
871871
std::vector<std::string> GetDestValues(const std::string& prefix) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
872872

873873
//! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock().
874-
int64_t nRelockTime = 0;
874+
int64_t nRelockTime GUARDED_BY(cs_wallet){0};
875875

876+
// Used to prevent concurrent calls to walletpassphrase RPC.
877+
Mutex m_unlock_mutex;
876878
bool Unlock(const SecureString& strWalletPassphrase, bool accept_no_keys = false);
877879
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
878880
bool EncryptWallet(const SecureString& strWalletPassphrase);

0 commit comments

Comments
 (0)