wallet: fix crash on importdescriptors with a range ending at 2^31-1 #35989

pull shuv-amp wants to merge 3 commits into bitcoin:master from shuv-amp:wallet-descriptor-range-overflow changing 3 files +42 −2
  1. shuv-amp commented at 8:15 PM on August 16, 2026: none

    WalletDescriptor stores the descriptor range in int32_t fields, with range_end exclusive. ProcessDescriptorImport computes that end in int64_t and passes it to the constructor with no bound in between. ParseDescriptorRange accepts an inclusive endpoint of 2^31 - 1, so the exclusive end can be 2^31. That does not fit in int32_t and truncates to INT32_MIN, which leaves the descriptor with an inverted range. The node then aborts while filling the keypool:

    $ bitcoin-cli -named createwallet wallet_name=w disable_private_keys=true
    $ bitcoin-cli -rpcwallet=w importdescriptors '[{"desc":"wpkh([728986fc/84h/1h/0h]tpubDDLEJ5Q3Tu8hn4BBEn5bnd2cYecWFwPpse1o9rx8uds47cP2bfr62sZM9LaYuNYPGnWmSKmiPeXinV2YxjHC4kFf3UUShMziLazstZbGRXU/0/*)#0gv2ddmk","range":[2147483647,2147483647],"timestamp":"now"}]'
    error: Error while attempting to communicate with server 127.0.0.1:8332 (EOF)
    
    Assertion failed: (m_wallet_descriptor.range_end - 1 == m_max_cached_index),
    function TopUpWithDB, file scriptpubkeyman.cpp, line 1109.
    

    The same crash happens with no range argument at all when -keypool is set above INT32_MAX, because that branch takes the end from m_keypool_size, which is only bounded from below (wallet.cpp:3092). Guarding the range argument alone is not enough.

    By the time the assert fires the descriptor has already been written to disk. CreateFromImport calls TopUpWithDB directly and skips the transaction wrapper that TopUp has, so the record survives the abort. The wallet still loads afterwards, but listdescriptors reports the inverted range:

    "range": [2147483647, -2147482650]
    

    The first commit bounds the end after both branches, so every field that reaches the WalletDescriptor constructor is representable. I did not put the check in ParseDescriptorRange. #35872 made an endpoint of 2^31 - 1 valid for the scanning RPCs and added a test asserting it succeeds, so tightening the shared parser would regress that. This limit comes from how the wallet stores the range, not from descriptor ranges in general.

  2. wallet: reject descriptor import ranges that cannot be represented
    `WalletDescriptor` stores the range in `int32_t` with an exclusive end, but
    `ProcessDescriptorImport` computes that end in `int64_t` and passes it straight
    to the constructor. An inclusive endpoint of `INT32_MAX`, which
    `ParseDescriptorRange` accepts, truncates to `INT32_MIN` and leaves an inverted
    range that aborts the node in `TopUpWithDB`. The same happens with no range
    given when `-keypool` is larger than `INT32_MAX`.
    
    Bound the end after both branches. `ParseDescriptorRange` is left alone since an
    endpoint of `INT32_MAX` is valid for the scanning RPCs.
    5507b5f05b
  3. wallet: avoid integer overflow when topping up the descriptor keypool
    `TopUpWithDB` adds `target_size` to `next_index` in `int32_t`. `next_index` is
    user controlled for imported descriptors and `target_size` comes from
    `-keypool`, so the addition overflows once `next_index` is within `target_size`
    of `INT32_MAX`, which traps in `-ftrapv` builds and is undefined behavior
    otherwise.
    
    Use `CheckedAdd` and leave the range unchanged when there is no representable
    end.
    c3afe1844e
  4. test: add coverage for importdescriptors range end bounds
    Both cases abort the node without the previous commits: a range ending at
    `INT32_MAX`, and an import with no range under a `-keypool` larger than
    `INT32_MAX`. The first uses a watch-only descriptor so the import reaches the
    keypool top up instead of failing earlier on the private key check.
    16b31cae72
  5. DrahtBot added the label Wallet on Aug 16, 2026
  6. DrahtBot commented at 8:15 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/35989.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

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

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #34993 (wallet: NotifyCanGetAddressesChanged when advancing next_index by davidgumberg)
    • #34861 (wallet: Add importdescriptors interface by polespinasa)
    • #31668 (Added rescan option for import descriptors by saikiran57)

    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.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->


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