Skip to content

Commit 7b8e157

Browse files
committed
rpc: Fix rpcRunLater race in walletpassphrase
1 parent d2db252 commit 7b8e157

File tree

1 file changed

+39
-31
lines changed

1 file changed

+39
-31
lines changed

src/wallet/rpcwallet.cpp

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1918,44 +1918,52 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
19181918
},
19191919
}.Check(request);
19201920

1921-
auto locked_chain = pwallet->chain().lock();
1922-
LOCK(pwallet->cs_wallet);
1923-
1924-
if (!pwallet->IsCrypted()) {
1925-
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1926-
}
1921+
int64_t nSleepTime;
1922+
{
1923+
auto locked_chain = pwallet->chain().lock();
1924+
LOCK(pwallet->cs_wallet);
19271925

1928-
// Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
1929-
SecureString strWalletPass;
1930-
strWalletPass.reserve(100);
1931-
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1932-
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
1933-
strWalletPass = request.params[0].get_str().c_str();
1926+
if (!pwallet->IsCrypted()) {
1927+
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
1928+
}
19341929

1935-
// Get the timeout
1936-
int64_t nSleepTime = request.params[1].get_int64();
1937-
// Timeout cannot be negative, otherwise it will relock immediately
1938-
if (nSleepTime < 0) {
1939-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
1940-
}
1941-
// Clamp timeout
1942-
constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
1943-
if (nSleepTime > MAX_SLEEP_TIME) {
1944-
nSleepTime = MAX_SLEEP_TIME;
1945-
}
1930+
// Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
1931+
SecureString strWalletPass;
1932+
strWalletPass.reserve(100);
1933+
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
1934+
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
1935+
strWalletPass = request.params[0].get_str().c_str();
1936+
1937+
// Get the timeout
1938+
nSleepTime = request.params[1].get_int64();
1939+
// Timeout cannot be negative, otherwise it will relock immediately
1940+
if (nSleepTime < 0) {
1941+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
1942+
}
1943+
// Clamp timeout
1944+
constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
1945+
if (nSleepTime > MAX_SLEEP_TIME) {
1946+
nSleepTime = MAX_SLEEP_TIME;
1947+
}
19461948

1947-
if (strWalletPass.empty()) {
1948-
throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
1949-
}
1949+
if (strWalletPass.empty()) {
1950+
throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
1951+
}
19501952

1951-
if (!pwallet->Unlock(strWalletPass)) {
1952-
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1953-
}
1953+
if (!pwallet->Unlock(strWalletPass)) {
1954+
throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
1955+
}
19541956

1955-
pwallet->TopUpKeyPool();
1957+
pwallet->TopUpKeyPool();
19561958

1957-
pwallet->nRelockTime = GetTime() + nSleepTime;
1959+
pwallet->nRelockTime = GetTime() + nSleepTime;
1960+
}
19581961

1962+
// rpcRunLater must be called without cs_wallet held otherwise a deadlock
1963+
// can occur. The deadlock would happen when RPCRunLater removes the
1964+
// previous timer (and waits for the callback to finish if already running)
1965+
// and the callback locks cs_wallet.
1966+
AssertLockNotHeld(wallet->cs_wallet);
19591967
// Keep a weak pointer to the wallet so that it is possible to unload the
19601968
// wallet before the following callback is called. If a valid shared pointer
19611969
// is acquired in the callback then the wallet is still loaded.

0 commit comments

Comments
 (0)