#35990 wallet: harden the read-only BDB parser against crafted files
| Reviewability | Reviews | Agreement | Size | ||||
|---|---|---|---|---|---|---|---|
| #35990 wallet: harden the read-only BDB parser against crafted files | Ready | 1 | Strong | 35 + 230 tests | |||
|
|
|
|
| |||
Reviewability: ReadyStalePaused Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs. Size: lines added or modified outside tests, then in tests; greener = smaller. | |||||||
Goal
Hardens the read-only Berkeley DB parser used by wallet migration against crafted or corrupted databases. It tracks visited B-tree and overflow pages to reject multi-parent DAG traversal and infinite loops across empty overflow pages, and verifies that the sum of record lengths on a page does not exceed the page size. It also updates the BDB fuzz target to expect these new errors along with previously unhandled overflow errors.
Problem: Malformed or malicious BDB files passed to `migratewallet` or `bitcoin-wallet` can trigger infinite loops, exponential traversal time, or huge memory inflation. Specifically, a page referenced by multiple parents causes redundant subtree traversal, empty overflow pages pointing to each other bypass length-based loop detection, and multiple index entries pointing to the same record duplicate memory allocation up to hundreds of megabytes.
Evidence of importance:
- PR description: 'On master an ~8 KB file built this way doesn't finish.'
- PR description: 'A single 64 KB page can turn into ~500 MB this way. On master a 256 KB file took migratewallet from 54 MB to ~800 MB.'
- Commit 436ba919bd notes that page level checks stop direct ancestor cycles but small files can still describe exponential work through DAG structures.
Scope: Adds a byte-level mock BDB file generator (`BDBFileBuilder`) in `db_tests.cpp` to construct raw malformed and full B-tree pages for test cases without relying on Berkeley DB.
Risk of a mistake: medium (wallet, wallet_storage). A mistake in BDB page traversal or record validation could cause valid legacy wallets to fail migration.
Changed since the previous assessment: Added commit 2dfce13f9f updating `wallet_bdb_parser.cpp` to expect the new error strings as well as two previously missing overflow errors from #34959 that caused the fuzzer to abort.
Reviewability
Ready
Nothing known that would invalidate a review now.
Waiting on (from the thread read):
- nothing
Agreement
Strong
substantive support, no open objection (fametrano)
Thread notes: Initial record. fametrano provided a Tested ACK with detailed testing across unit tests and migration. / Initial record. fametrano tested on macOS arm64 and confirmed the hang reproduces on master and is fixed on the branch; no objections raised.
Objections, suggestions, and questions: none recorded.
Support:
- fametrano (contributor) (Tested ACK, 2026-10-15) [tested: unit tests, wallet_migration.py]: the new rejection tests fail on master (the self-referencing overflow page hangs) and pass here, as do the unit tests, `wallet_migration.py` and migrating a v28.2 legacy wallet with internal and overflow pages
Participants:
- fametrano (contributor, support, 1 statements 2026-10-15 to 2026-10-15): Tested ACK verifying rejection tests fail on master and pass here, along with wallet migration
Files
230 lines under test/bench/ci.
- src/wallet/test/db_tests.cpp +224/-0
- src/wallet/migrate.cpp +35/-5
- src/wallet/test/fuzz/wallet_bdb_parser.cpp +6/-0
Card
The short text the ranking pass compares across PRs.
This PR hardens the read-only BDB parser used by `migratewallet` and `bitcoin-wallet` against crafted files that bypass earlier checks. It prevents exponential traversal from multi-parent B-tree pages and infinite loops from cyclic empty overflow pages by tracking visited pages, and stops memory blowup by rejecting pages whose total record sizes exceed page bounds. The author demonstrates that an ~8 KB file can hang indefinitely on master and a 256 KB file can inflate memory usage to ~800 MB. The change includes unit tests with hand-crafted raw BDB binary layouts and updates the fuzz target to handle the new error strings.
Data
thread read through 2026-10-15 (1 statements), head 2dfce13f9f
labels: Wallet
Categories
Wallet
P2 · DoS protection (ranking pass: Moved up: hardening the BDB parser is on the migration path every legacy user must cross, grouping it with the other migration fixes.)
Prevents infinite loops and severe memory bloat when parsing crafted or corrupt Berkeley DB files during wallet migration. As noted in the description, 'an ~8 KB file built this way doesn't finish' on master, and a 256 KB file inflated memory usage to ~800 MB through duplicated DAG references and looping overflow pages.
Membership: Touches src/wallet/migrate.cpp and carries the Wallet label.
Factors: security/stability 2, bug 2, performance 1, user value 1, leverage 0