wallet: harden the read-only BDB parser against crafted files #35990

pull shuv-amp wants to merge 3 commits into bitcoin:master from shuv-amp:bdbro-reject-revisited-pages changing 3 files +226 −7
  1. shuv-amp commented at 9:15 PM on August 16, 2026: none

    The read-only BDB parser (BerkeleyRODatabase::Open() in migrate.cpp) is fed an attacker-supplied file whenever a user runs migratewallet, loads a legacy .dat wallet, or runs bitcoin-wallet. #34959 hardened it against circular references, but three crafted-file cases still get through and turn a small file into unbounded CPU or memory.

    Revisited btree page. The level check rejects a page that is its own ancestor, but a page reachable from more than one parent stays level consistent, passes the check, and is parsed once per path to it. An ~8 KB file of shared subtrees does not finish.

    Overflow chain of empty pages. #34959 bounds an overflow chain by the stated data length, but pages that carry no data never advance it, so two empty overflow pages pointing at each other loop forever.

    Page records that do not fit. A page's index entries can all point at the same record, which is then read and kept once per entry. entries and the record length are both 16-bit, 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 distinct such pages add up to an OOM.

    None of these corrupt memory; they are CPU/memory exhaustion from a crafted or corrupted wallet file, the same threat model #34959 addressed.

    The first two are fixed by tracking visited pages in each traversal (the approach from #34946 / #35150, closed as superseded by #34959, which turns out not to cover these level-consistent and empty-page cases). The third is fixed by bounding a page's total record size to the page. A valid BDB database references each page once and its records fit within the page, so none of this rejects a file that parses today.

    Each fix is a separate commit with a regression test in db_tests.cpp that hand-crafts the relevant file (Core cannot write a BDB database, so the bytes are laid out directly) and checks the parser rejects it. The three new error strings are also added to the wallet_bdb_parser fuzz allow-list.

    Supersedes #35992, which proposed the record-size bound on its own.

  2. DrahtBot added the label Wallet on Aug 16, 2026
  3. DrahtBot commented at 9:16 PM on August 16, 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/35990.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

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

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. shuv-amp marked this as ready for review on Aug 16, 2026
  5. shuv-amp marked this as a draft on Aug 17, 2026
  6. shuv-amp force-pushed on Aug 17, 2026
  7. shuv-amp renamed this:
    wallet: Reject revisited pages in the read-only BDB parser
    wallet: harden the read-only BDB parser against crafted files
    on Aug 17, 2026
  8. shuv-amp marked this as ready for review on Aug 17, 2026
  9. wallet, bdbro: Reject revisited btree pages
    Validating btree page levels stops a page from being its own ancestor,
    but a page can still be reachable from more than one parent while
    keeping a consistent level. Such a page, and everything below it, is
    parsed again for every path that reaches it, so a small file can
    describe an exponential amount of work. A valid btree references each
    page once, so keep track of the pages already visited and reject any
    that is seen again.
    381bd446ac
  10. wallet, bdbro: Reject revisited overflow pages
    Enforcing overflow data lengths stops a chain whose pages carry data,
    but overflow pages that carry no data never advance the accumulated
    length, so a chain that cycles through empty overflow pages loops
    forever. Keep track of the pages visited while following a chain and
    reject one that is seen again.
    ba7e0100c1
  11. 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 read 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.
    acbde35bb2
  12. shuv-amp force-pushed 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