-
Notifications
You must be signed in to change notification settings - Fork 37.7k
wallet: fix crash on loading descriptor wallet containing legacy key type entries #26462
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: fix crash on loading descriptor wallet containing legacy key type entries #26462
Conversation
f938a1d
to
42d61d2
Compare
Meh. I think it's reasonable to expect failures if the user is manually modifying their wallet file by doing direct database operations themselves. Since it is completely unexpected to have legacy and descriptor wallet things mixed, I don't think we should just silently ignore such issues and instead return an error. |
Right, throwing an error seems more reasonable. Obviously it wouldn't be the user itself modifying an existing wallet, but e.g. an external malicious actor crafting and handing out these mixed wallets, hoping that users would open them to cause node crashes. The PR in its current state is just the easiest "avoid crash"-remedy. Will look into your proposed |
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ReviewsSee the guideline for information on the review process.
ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
Concept ACK in terms of not crashing, and instead abort cleanly with a message. Even when this is an edge case; if people/attacker insert data into the wallet db, then the user might have worst problems then a simple crash... Still, for those users experimenting with our wallet db; any crash could end up corrupting other dbs and indexes that the node has, which would require a fresh resync or reindex etc.. So, better if we don't crash and just abort cleanly. Saying that, I would rather focus on getting #24914 merged first. The current load process is a mess, and continue adding stuff on top would just make it messier. |
Concept ACK on throwing an error.
Bugfixes need to be backported, so this should ideally be merged first. |
42d61d2
to
3c6ff49
Compare
…or wallets In the wallet key-value-loading routine, most legacy type entries require a LegacyScriptPubKeyMan instance after successful deserialization. On a descriptor wallet, creating that (via method `GetOrCreateLegacyScriptPubKeyMan`) fails and then leads to a null-pointer dereference crash. Fix this by throwing an error if if the wallet flags indicate that we have a descriptor wallet and there is a legacy entry found.
3c6ff49
to
3198e42
Compare
Force-pushed with a new fix that throws an error now, as suggested by reviewers (the old branch with the inferior ignore approach can still be found here). Happy to improve the error message if there is any suggestions. |
True, still I'm not sure that adding a specific |
ACK 3198e42 |
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 3198e42
Tested with the steps in the PR description:
$ ./src/bitcoin-cli -regtest createwallet crashme
$ ./src/bitcoin-cli -regtest unloadwallet crashme
$ sqlite3 ~/.bitcoin/regtest/wallets/crashme/wallet.dat
sqlite> INSERT INTO main VALUES(x'07637363726970740000000000000000000000000000000000000000', x'00');
$ ./src/bitcoin-cli -regtest loadwallet crashme
error code: -4
error message:
Wallet loading failed. Unexpected legacy entry in descriptor wallet found. Loading wallet /home/aureleoules/.bitcoin/regtest/wallets/crashme/wallet.dat
The wallet might have been tampered with or created with malicious intent.
strErr = strprintf("Error: Unexpected legacy entry found in descriptor wallet %s. ", pwallet->GetName()); | ||
strErr += "The wallet might have been tampered with or created with malicious intent."; | ||
pwallet->WalletLogPrintf("%s\n", strErr); |
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.
I don't think there is a need to store the error in strErr?
strErr = strprintf("Error: Unexpected legacy entry found in descriptor wallet %s. ", pwallet->GetName()); | |
strErr += "The wallet might have been tampered with or created with malicious intent."; | |
pwallet->WalletLogPrintf("%s\n", strErr); | |
pwallet->WalletLogPrintf("Error: Unexpected legacy entry found in descriptor wallet %s. The wallet might have been tampered with or created with malicious intent.\n", pwallet->GetName()); |
…ontaining legacy key type entries 3198e42 test: check that loading descriptor wallet with legacy entries throws error (Sebastian Falbesoner) 349ed2a wallet: throw error if legacy entries are present on loading descriptor wallets (Sebastian Falbesoner) Pull request description: Loading a descriptor wallet currently leads to a segfault if a legacy key type entry is present that can be deserialized successfully and needs SPKman-interaction. To reproduce with a "cscript" entry (see second commit for details): ``` $ ./src/bitcoin-cli createwallet crashme $ ./src/bitcoin-cli unloadwallet crashme $ sqlite3 ~/.bitcoin/wallets/crashme/wallet.dat SQLite version 3.38.2 2022-03-26 13:51:10 Enter ".help" for usage hints. sqlite> INSERT INTO main VALUES(x'07637363726970740000000000000000000000000000000000000000', x'00'); $ ./src/bitcoin-cli loadwallet crashme --- bitcoind output: --- 2022-11-06T13:51:01Z Using SQLite Version 3.38.2 2022-11-06T13:51:01Z Using wallet /home/honey/.bitcoin/wallets/crashme 2022-11-06T13:51:01Z init message: Loading wallet… 2022-11-06T13:51:01Z [crashme] Wallet file version = 10500, last client version = 249900 Segmentation fault (core dumped) ``` Background: In the wallet key-value-loading routine, most legacy type entries require a `LegacyScriptPubKeyMan` instance after successful deserialization. On a descriptor wallet, creating that (via method `GetOrCreateLegacyScriptPubKeyMan`) fails and then leads to a null-pointer dereference crash. E.g. for CSCRIPT: https://github.com/bitcoin/bitcoin/blob/50422b770a40f5fa964201d1e99fd6b5dc1653ca/src/wallet/walletdb.cpp#L589-L594 ~~This PR fixes this by simply ignoring legacy entries if the wallet flags indicate that we have a descriptor wallet. The second commits adds a regression test to the descriptor wallet's functional test (fortunately Python includes sqlite3 support in the standard library).~~ ~~Probably it would be even better to throw a warning to the user if unexpected legacy entries are found in descriptor wallets, but I think as a first mitigation everything is obvisouly better than crashing. As far as I'm aware, descriptor wallets created/migrated by Bitcoin Core should never end up in a state containing legacy type entries though.~~ This PR fixes this by throwing an error if legacy entries are found in descriptor wallets on loading. ACKs for top commit: achow101: ACK 3198e42 aureleoules: ACK 3198e42 Tree-SHA512: ee43da3f61248e0fde55d9a705869202cb83df678ebf4816f0e77263f0beac0d7bae9490465d1753159efb093ee37182931d76b2e2b6e8c6f8761285700ace1c
The test is broken on Windows:
Fixed in #28204. |
703b758 qa: Close SQLite connection properly (Hennadii Stepanov) Pull request description: This PR is a follow-up for bitcoin/bitcoin#26462 that introduced a bug on Windows: ``` >test\functional\wallet_descriptor.py ... PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: ... ``` From `sqlite3` Python module [docs](https://docs.python.org/3/library/sqlite3.html#how-to-use-the-connection-context-manager): > `Connection` object used as context manager only commits or rollbacks transactions, so the connection object should be closed manually. ACKs for top commit: MarcoFalke: lgtm ACK 703b758 theStack: utACK 703b758 Tree-SHA512: 35b1403507be06d1fc04e7e07ff56af5bcfe5013024671f0c1d9f3c41aacc4c777bcc6376ce82d720394e27450415d50ff5d5834ed388ec3f21503f86f1a42a5
703b758 qa: Close SQLite connection properly (Hennadii Stepanov) Pull request description: This PR is a follow-up for bitcoin#26462 that introduced a bug on Windows: ``` >test\functional\wallet_descriptor.py ... PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: ... ``` From `sqlite3` Python module [docs](https://docs.python.org/3/library/sqlite3.html#how-to-use-the-connection-context-manager): > `Connection` object used as context manager only commits or rollbacks transactions, so the connection object should be closed manually. ACKs for top commit: MarcoFalke: lgtm ACK 703b758 theStack: utACK 703b758 Tree-SHA512: 35b1403507be06d1fc04e7e07ff56af5bcfe5013024671f0c1d9f3c41aacc4c777bcc6376ce82d720394e27450415d50ff5d5834ed388ec3f21503f86f1a42a5
Loading a descriptor wallet currently leads to a segfault if a legacy key type entry is present that can be deserialized successfully and needs SPKman-interaction. To reproduce with a "cscript" entry (see second commit for details):
Background: In the wallet key-value-loading routine, most legacy type entries require a
LegacyScriptPubKeyMan
instance after successful deserialization. On a descriptor wallet, creating that (via methodGetOrCreateLegacyScriptPubKeyMan
) fails and then leads to a null-pointer dereference crash. E.g. for CSCRIPT:bitcoin/src/wallet/walletdb.cpp
Lines 589 to 594 in 50422b7
This PR fixes this by simply ignoring legacy entries if the wallet flags indicate that we have a descriptor wallet. The second commits adds a regression test to the descriptor wallet's functional test (fortunately Python includes sqlite3 support in the standard library).Probably it would be even better to throw a warning to the user if unexpected legacy entries are found in descriptor wallets, but I think as a first mitigation everything is obvisouly better than crashing. As far as I'm aware, descriptor wallets created/migrated by Bitcoin Core should never end up in a state containing legacy type entries though.This PR fixes this by throwing an error if legacy entries are found in descriptor wallets on loading.