Skip to content

Commit 6f732ff

Browse files
committed
Merge bitcoin/bitcoin#28774: wallet: avoid returning a reference to vMasterKey after releasing the mutex that guards it
32a9f13 wallet: avoid returning a reference to vMasterKey after releasing the mutex that guards it (Vasil Dimov) Pull request description: `CWallet::GetEncryptionKey()` would return a reference to the internal `CWallet::vMasterKey`, guarded by `CWallet::cs_wallet`, which is unsafe. Returning a copy would be a shorter solution, but could have security implications of the master key remaining somewhere in the memory even after `CWallet::Lock()` (the current calls to `CWallet::GetEncryptionKey()` are safe, but that is not future proof). So, instead of `EncryptSecret(m_storage.GetEncryptionKey(), ...)` change the `GetEncryptionKey()` method to provide the encryption key to a given callback: `m_storage.WithEncryptionKey([](const CKeyingMaterial& k) { EncryptSecret(k, ...); })` This silences the following (clang 18): ``` wallet/wallet.cpp:3520:12: error: returning variable 'vMasterKey' by reference requires holding mutex 'cs_wallet' [-Werror,-Wthread-safety-reference-return] 3520 | return vMasterKey; | ^ ``` --- _Previously this PR modified both ArgsManager and wallet code. But the ArgsManager commit bitcoin/bitcoin@856c887 was merged in bitcoin/bitcoin#29040 so now this only affects wallet code. The previous PR description was:_ Avoid this unsafe pattern from `ArgsManager` and `CWallet`: ```cpp class A { Mutex mutex; Foo member GUARDED_BY(mutex); const Foo& Get() { LOCK(mutex); return member; } // callers of `Get()` will have access to `member` without owning the mutex. ``` ACKs for top commit: achow101: ACK 32a9f13 ryanofsky: Code review ACK 32a9f13. This seems like a potentially real race condition, and the fix here is pretty simple. furszy: ACK 32a9f13 Tree-SHA512: 133da84691642afc1a73cf14ad004a7266cb4be1a6a3ec634d131dca5dbcdef52522c1d5eb04f5b6c4e06e1fc3e6ac57315f8fe1e207b464ca025c2b4edefdc1
2 parents 7cb7759 + 32a9f13 commit 6f732ff

File tree

4 files changed

+20
-8
lines changed

4 files changed

+20
-8
lines changed

src/wallet/scriptpubkeyman.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,9 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pu
811811

812812
std::vector<unsigned char> vchCryptedSecret;
813813
CKeyingMaterial vchSecret{UCharCast(key.begin()), UCharCast(key.end())};
814-
if (!EncryptSecret(m_storage.GetEncryptionKey(), vchSecret, pubkey.GetHash(), vchCryptedSecret)) {
814+
if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
815+
return EncryptSecret(encryption_key, vchSecret, pubkey.GetHash(), vchCryptedSecret);
816+
})) {
815817
return false;
816818
}
817819

@@ -997,7 +999,9 @@ bool LegacyScriptPubKeyMan::GetKey(const CKeyID &address, CKey& keyOut) const
997999
{
9981000
const CPubKey &vchPubKey = (*mi).second.first;
9991001
const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second;
1000-
return DecryptKey(m_storage.GetEncryptionKey(), vchCryptedSecret, vchPubKey, keyOut);
1002+
return m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
1003+
return DecryptKey(encryption_key, vchCryptedSecret, vchPubKey, keyOut);
1004+
});
10011005
}
10021006
return false;
10031007
}
@@ -2128,7 +2132,9 @@ std::map<CKeyID, CKey> DescriptorScriptPubKeyMan::GetKeys() const
21282132
const CPubKey& pubkey = key_pair.second.first;
21292133
const std::vector<unsigned char>& crypted_secret = key_pair.second.second;
21302134
CKey key;
2131-
DecryptKey(m_storage.GetEncryptionKey(), crypted_secret, pubkey, key);
2135+
m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
2136+
return DecryptKey(encryption_key, crypted_secret, pubkey, key);
2137+
});
21322138
keys[pubkey.GetID()] = key;
21332139
}
21342140
return keys;
@@ -2262,7 +2268,9 @@ bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const
22622268

22632269
std::vector<unsigned char> crypted_secret;
22642270
CKeyingMaterial secret{UCharCast(key.begin()), UCharCast(key.end())};
2265-
if (!EncryptSecret(m_storage.GetEncryptionKey(), secret, pubkey.GetHash(), crypted_secret)) {
2271+
if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) {
2272+
return EncryptSecret(encryption_key, secret, pubkey.GetHash(), crypted_secret);
2273+
})) {
22662274
return false;
22672275
}
22682276

src/wallet/scriptpubkeyman.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include <boost/signals2/signal.hpp>
2424

25+
#include <functional>
2526
#include <optional>
2627
#include <unordered_map>
2728

@@ -46,7 +47,8 @@ class WalletStorage
4647
virtual void UnsetBlankWalletFlag(WalletBatch&) = 0;
4748
virtual bool CanSupportFeature(enum WalletFeature) const = 0;
4849
virtual void SetMinVersion(enum WalletFeature, WalletBatch* = nullptr) = 0;
49-
virtual const CKeyingMaterial& GetEncryptionKey() const = 0;
50+
//! Pass the encryption key to cb().
51+
virtual bool WithEncryptionKey(std::function<bool (const CKeyingMaterial&)> cb) const = 0;
5052
virtual bool HasEncryptionKeys() const = 0;
5153
virtual bool IsLocked() const = 0;
5254
};

src/wallet/wallet.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3513,9 +3513,10 @@ void CWallet::SetupLegacyScriptPubKeyMan()
35133513
AddScriptPubKeyMan(id, std::move(spk_manager));
35143514
}
35153515

3516-
const CKeyingMaterial& CWallet::GetEncryptionKey() const
3516+
bool CWallet::WithEncryptionKey(std::function<bool (const CKeyingMaterial&)> cb) const
35173517
{
3518-
return vMasterKey;
3518+
LOCK(cs_wallet);
3519+
return cb(vMasterKey);
35193520
}
35203521

35213522
bool CWallet::HasEncryptionKeys() const

src/wallet/wallet.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -962,7 +962,8 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
962962
//! Make a LegacyScriptPubKeyMan and set it for all types, internal, and external.
963963
void SetupLegacyScriptPubKeyMan();
964964

965-
const CKeyingMaterial& GetEncryptionKey() const override;
965+
bool WithEncryptionKey(std::function<bool (const CKeyingMaterial&)> cb) const override;
966+
966967
bool HasEncryptionKeys() const override;
967968

968969
/** Get last block processed height */

0 commit comments

Comments
 (0)