-
Notifications
You must be signed in to change notification settings - Fork 38.7k
wallet: avoid returning a reference to vMasterKey after releasing the mutex that guards it #28774
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
wallet: avoid returning a reference to vMasterKey after releasing the mutex that guards it #28774
Conversation
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code CoverageFor detailed information about the code coverage, see the test coverage report. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. ConflictsNo conflicts as of last run. |
clang-tidy-17 -p=/ci_container_base/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu -quiet -load=/tidy-build/libbitcoin-tidy.so /ci_container_base/ci/scratch/build/bitcoin-x86_64-pc-linux-gnu/src/common/args.cpp
common/args.cpp:281:1: error: return type 'const fs::path' is 'const'-qualified at the top level, which may reduce code readability without improving const correctness [readability-const-return-type,-warnings-as-errors]
281 | const fs::path ArgsManager::GetBlocksDirPath() const
| ^~~~~ |
e134010 to
93cd655
Compare
|
|
|
I don't think a mutex is required for read-only access to a const blob of memory? |
|
@maflcko, a mutex is required by readers to avoid writers modifying the blob while the read is happening, resulting in a torn read. The underlying variable is not readonly (ie declared as |
|
No write will happen to the datadir cache after the first write, and no reader will have the reference before the first write, no? I am trying to say that the current code is fine. Otherwise, it would be good to add steps to reproduce UB. No objection to changing the code, but if this is worth it to change, it should be done for all places in the whole codebase, not just some places. |
93cd655 to
d4ef700
Compare
That is not obvious to me. Line 293 in 9b68c9b
Maybe the current code is fine. I did not dive to the bottom of it to say with certainty whether it is fine or not because what matters is that it is certainly unsafe. Unsafe wrt future changes in the same way as if we remove some
These are all reported (by clang 18). I see |
|
There is the argument to not bother silencing compiler warnings from unreleased/development versions of compilers. I somewhat agree with that if it is being done for the sake of silencing a warning. In this case however, the problem is real and the warning is just the messenger. I think this deserves to be fixed even if the warning did not exist. |
jonatack
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK d4ef7004050c7ee8ac75ae895511334b62890f1b
Thanks, I've taken the commit and put it in #29040, because it fits into the other fs::path changes. Let me know if I should drop it and you prefer to keep it here. The fs::path should be fine to do, even if it is not needed. (Creating a copy here should be harmless and not cause performance issues). I haven't looked at the wallet commit. |
It is ok. I don't have a preference whether it gets merged via #29040 of via this PR. Just that it makes it to |
d4ef700 to
7a8e531
Compare
|
|
|
@achow101, this is now solely in the wallet. Maybe you want to take a look? |
src/wallet/scriptpubkeyman.h
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These methods doesn't seems to fit inside the WalletStorage interface. What do you think about creating a new interface for encrypting/decrypting data, then provide it to each spkm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, those methods were not very natural for the WalletStorage interface which contained a method to retrieve the encryption key. Adding a new interface seems a bit too complicated for this. Instead, I changed it to give the key to a callback function instead of returning the key to the caller.
… mutex that guards it
`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;
| ^
```
7a8e531 to
32a9f13
Compare
|
|
ryanofsky
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code review ACK 32a9f13. This seems like a potentially real race condition, and the fix here is pretty simple.
I do think the PR title and description are confusing and probably causing this PR to get overlooked.
Would suggest replacing PR title and description with the title and description from the 32a9f13 commit message which are much clearer. To make the PR comment history legible, you could include the original description below the new description maybe separated with a horizontal line and a comment like "Previously this PR modified both ArgsManager and wallet code. But the ArgsManager commit 856c887 was merged in #29040 so now this only affects wallet code. The previous PR description was: ..."
furszy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 32a9f13
| LOCK(cs_wallet); | ||
| return cb(vMasterKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
Could inline it
return WITH_LOCK(cs_wallet, return cb(vMasterKey));Also, could pass cb as a const ref.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do if I retouch.
Done. Thanks for the suggestion! |
|
ACK 32a9f13 |
…y 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@856c887 was merged in 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
…itcoin#28774 - lock annotations 1db9e6a Merge bitcoin#19979: Replace LockAssertion with AssertLockHeld, remove LockAssertion (MarcoFalke) d0a4198 Merge bitcoin#21598: refactor: Remove negative lock annotations from globals (MarcoFalke) 55114a6 Merge bitcoin#19668: Do not hide compile-time thread safety warnings (MarcoFalke) 898282d Merge bitcoin#28774: wallet: avoid returning a reference to vMasterKey after releasing the mutex that guards it (Ava Chow) 6d45284 fix: add missing lock annotation for ContextualCheckBlock and related TODO (Konstantin Akimov) 846ebab fix: fixes for orphange's locks and cs_main (Konstantin Akimov) 002580c fix: add ignoring safety-annotation for llmq/signing_shares due to template lambdas (Konstantin Akimov) 502d6ae fix: add multiple missing annotation about locks for dash specific code (Konstantin Akimov) a219a8b partial Merge bitcoin#29040: refactor: Remove pre-C++20 code, fs::path cleanup (Konstantin Akimov) 042e8a4 fix: drop unneded lock assert in net.cpp for m_nodes_mutex (Konstantin Akimov) Pull request description: ## What was done? These 4 backports improve noticeable implementation of thread-safety analysis by moving many possible warnings to compilation level. There's a lot of fixes for annotations after that! ## How Has This Been Tested? Build with clang: ``` CC=clang CXX=clang++ ./configure --prefix=$(pwd)/depends/x86_64-pc-linux-gnu --enable-suppress-external-warnings --enable-debug --enable-stacktraces --enable-werror --enable-crash-hooks --enable-maintainer-mode ``` ## Breaking Changes n/a ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone ACKs for top commit: UdjinM6: utACK 1db9e6a PastaPastaPasta: utACK 1db9e6a Tree-SHA512: 42a73e423f311689babc366b58a92f024556cf63af93357cfa0715c471efc1491d02ed9e7ba19b23efcdf423ab5bd051a24da2ad64c32e4b6db132a05f6669c1
… mutex that guards it
Summary:
```
`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;
| ^
```
Backport of [[bitcoin/bitcoin#28774 | core#28774]].
Test Plan:
With clang 18:
ninja all check-all
Reviewers: #bitcoin_abc, PiRK
Reviewed By: #bitcoin_abc, PiRK
Differential Revision: https://reviews.bitcoinabc.org/D17013
… mutex that guards it
Summary:
```
`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;
| ^
```
Backport of [[bitcoin/bitcoin#28774 | core#28774]].
Test Plan:
With clang 18:
ninja all check-all
Reviewers: #bitcoin_abc, PiRK
Reviewed By: #bitcoin_abc, PiRK
Differential Revision: https://reviews.bitcoinabc.org/D17013
CWallet::GetEncryptionKey()would return a reference to the internalCWallet::vMasterKey, guarded byCWallet::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 toCWallet::GetEncryptionKey()are safe, but that is not future proof).So, instead of
EncryptSecret(m_storage.GetEncryptionKey(), ...)change the
GetEncryptionKey()method to provide the encryptionkey to a given callback:
m_storage.WithEncryptionKey([](const CKeyingMaterial& k) { EncryptSecret(k, ...); })This silences the following (clang 18):
Previously this PR modified both ArgsManager and wallet code. But the ArgsManager commit 856c887 was merged in #29040 so now this only affects wallet code. The previous PR description was:
Avoid this unsafe pattern from
ArgsManagerandCWallet: