wallet: Bound the records read from a BDB page to the page size #35992

pull shuv-amp wants to merge 1 commits into bitcoin:master from shuv-amp:bdbro-bound-page-records changing 3 files +125 −5
  1. shuv-amp commented at 10:41 AM on August 17, 2026: none

    The read-only BDB parser reads a btree page by walking its index array and reading the record each entry points at, keeping a copy of every one (RecordsPage::Unserialize and InternalPage::Unserialize in migrate.cpp). The only thing checked about an index is that it does not point before the running cursor. Nothing stops several entries from pointing at the same record.

    The cursor only advances by the two bytes of the index just read, so all of a page's entries can hold the same offset, and the parser reads that one record once per entry and keeps every copy. entries and the record length are both 16-bit fields, so a single ~64 KB page can be read into entries * len bytes. A 256 KB file takes a node from 54 MB to ~800 MB during migratewallet, and it succeeds rather than erroring. Distinct such pages add up, so a few-MB file OOMs the process.

    This is not caught by #34959 (btree levels and overflow lengths) or #35990 (revisited pages), because it happens within a single page, with no cycle or repeated page. It is the same hostile-wallet-file case #34959 set out to harden: migratewallet, loadwallet of a legacy .dat, and bitcoin-wallet.

    In a valid page the records do not overlap and so cannot be larger than the page. This tracks their total size while reading and rejects a page whose records do not fit, which bounds what one page can allocate to its own size. Nothing that parses today is affected. The two rejections are:

    Data records exceed page size       # leaf page
    Internal records exceed page size   # internal page
    

    Testing

    A regression test in db_tests.cpp hand-crafts such a page and checks the parser rejects it; it fails without this change. Core cannot write a BDB file, so the page bytes are laid out directly, the same reason #34959 was only fuzz-tested. The two new error strings are also added to the wallet_bdb_parser fuzz allow-list, and a page packed with many distinct records still parses, so the bound does not reject a legitimately full page.

  2. wallet, bdbro: Enforce that a page's records fit in the page
    A page's index entries each point at a record that is read and kept,
    but the offsets are not checked against each other. A page whose
    entries all point at the same record is therefore read into
    entries * len bytes of memory, so a single page can be expanded into
    far more than its own size. In a valid page the records do not overlap
    and cannot be larger than the page, so track their total size while
    reading and reject a page whose records do not fit.
    
    Add a regression test that crafts such a page and checks it is rejected.
    959a7a965f
  3. DrahtBot added the label Wallet on Aug 17, 2026
  4. DrahtBot commented at 10:41 AM on August 17, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/35992.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process. A summary of reviews will appear here.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

    LLM Linter (✨ experimental)

    Possible places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):

    • builder.WLE(3, 4, 1, 4) in src/wallet/test/db_tests.cpp
    • builder.WLE(3, 20, entries, 2) in src/wallet/test/db_tests.cpp
    • builder.W8(3, 24, 1) in src/wallet/test/db_tests.cpp
    • builder.W8(3, 25, 5) in src/wallet/test/db_tests.cpp
    • builder.WLE(3, 26 + 2 * i, record_off, 2) in src/wallet/test/db_tests.cpp
    • builder.WLE(3, record_off, record_len, 2) in src/wallet/test/db_tests.cpp
    • builder.W8(3, record_off + 2, 1) in src/wallet/test/db_tests.cpp

    <sup>2026-08-17 10:41:39</sup>

  5. shuv-amp marked this as ready for review on Aug 17, 2026
  6. shuv-amp commented at 10:46 AM on August 17, 2026: none

    Folding this into #35990

  7. shuv-amp closed this on Aug 17, 2026


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-08-21 04:51 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me