wallet, rpc: fix importdescriptors crash on int32_t range end overflow #36238

pull kriss39 wants to merge 2 commits into bitcoin:master from kriss39:fix-importdescriptors-range-end-overflow changing 3 files +18 −2
  1. kriss39 commented at 4:18 PM on September 13, 2026: contributor

    importdescriptors aborts the node when the inclusive range end is 2147483647:

    bitcoin-cli -regtest -rpcwallet=w importdescriptors \
      '[{"desc":"wpkh(tpub.../0/*)#checksum","range":[2147483647,2147483647],"timestamp":"now"}]'
    
    Assertion failed: (m_wallet_descriptor.GetEnd() - 1 == m_max_cached_index),
    function TopUpWithDB, file scriptpubkeyman.cpp, line 1165.
    

    WalletDescriptor keeps the exclusive range end in an int32_t, but ProcessDescriptorImport() computes it as int64_t and lets the narrowing happen implicitly when it constructs the descriptor. ParseDescriptorRange() only rejects an inclusive end of 2^31 and above, so INT32_MAX gets through and becomes an exclusive end of 2^31, which wraps to INT32_MIN.

    TopUpWithDB() then does:

    int32_t new_range_end = std::max(m_wallet_descriptor.GetNext() + (int32_t)target_size, m_wallet_descriptor.GetEnd());
    

    That addition is int32_t too, so it overflows on its own once the next index gets within target_size of INT32_MAX. Either way new_range_end ends up negative, the loop below derives nothing, and the invariant on the next line doesn't hold.

    The wrapped value is written to the wallet database before the abort, so the descriptor stays broken after a restart:

    "range": [2147483647, -2147482650], "next_index": 2147483647
    

    The first commit saturates the addition. The second rejects a range end that isn't representable as an int32_t, reusing the existing "End of range is too high" error.

    deriveaddresses and scantxoutset aren't affected, since they walk the range as int64_t and never store it. I checked that both still accept [2147483647, 2147483647].

    The new case in wallet_importdescriptors.py uses a descriptor the wallet can actually expand. My first attempt reused the sh(wpkh(xpriv/...)) descriptor the surrounding checks use, but in a watch-only wallet that one fails earlier with -4 and never reaches the code that crashes.

    Without the fix the test dies with RemoteDisconnected and node1's stderr has the assertion above. With it, it passes. I also ran wallet_descriptor, wallet_keypool, wallet_keypool_topup, wallet_basic, rpc_deriveaddresses and rpc_scantxoutset.

  2. wallet: avoid int32_t overflow computing descriptor range end
    TopUpWithDB() computed the new range end as
    
        GetNext() + (int32_t)target_size
    
    which is signed int32_t arithmetic. When the descriptor's next index is
    within target_size of INT32_MAX this overflows, which is undefined
    behaviour and in practice wraps to a negative range end.
    
    Saturate the addition instead, so a next index near INT32_MAX clamps to
    INT32_MAX rather than wrapping.
    32e5ea9f5e
  3. rpc: reject importdescriptors range end above int32_t max
    WalletDescriptor stores the exclusive range end as an int32_t, but
    ProcessDescriptorImport() computed it as int64_t and let the narrowing
    happen implicitly in the constructor.
    
    ParseDescriptorRange() only rejects an inclusive end of 2^31 or above,
    so an inclusive end of INT32_MAX was accepted and produced an exclusive
    end of 2^31, which wrapped to INT32_MIN. TopUpWithDB() then derived no
    keys and tripped its
    
        assert(m_wallet_descriptor.GetEnd() - 1 == m_max_cached_index)
    
    invariant, aborting the node. The wrapped range end was also written to
    the wallet database, leaving the descriptor unusable after restart.
    
    Reject a range end that is not representable as an int32_t, reusing the
    existing "End of range is too high" error.
    
    The inclusive end stays valid for deriveaddresses and scantxoutset,
    which iterate the range as int64_t and never store it.
    766924cd44
  4. DrahtBot commented at 4:18 PM on September 13, 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/36238.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Concept NACK polespinasa

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #36236 (wallet, rpc: add verify_balance option to importdescriptors by musaHaruna)
    • #35989 (wallet: fix crash on importdescriptors with a range ending at 2^31-1 by shuv-amp)
    • #35377 (wallet: Allow importing of descriptors without private keys when the wallet has the private keys by achow101)
    • #34861 (wallet: Add importdescriptors interface by polespinasa)

    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-->

  5. in src/wallet/rpc/backup.cpp:1 in 766924cd44


    vicjuma commented at 7:34 AM on September 14, 2026:

    The crash indeed happens before adding this check, though

    Before <img width="2134" height="1218" alt="Image" src="https://github.com/user-attachments/assets/afd72087-d598-421d-8485-e1950fd317aa" />

    After

    <img width="2144" height="902" alt="Image" src="https://github.com/user-attachments/assets/5b87fe92-019f-4396-98b8-2ad6d6dae8b4" />

  6. vicjuma commented at 7:40 AM on September 14, 2026: contributor

    But why would one decide to derive and watch the address at this specific index :-)? Being a reachable crash, however, it's worth the fix. Balancing between practicability and correctness, I will lean towards correctness. Editing my A-C-K cause this conflicts with #35989

  7. polespinasa commented at 10:52 AM on September 14, 2026: member

    NACK there is already a PR for this https://github.com/bitcoin/bitcoin/pull/35989

  8. sedited closed this on Sep 14, 2026

  9. kriss39 commented at 11:15 AM on September 14, 2026: contributor

    Ah, I missed #35989 when I opened this, sorry for the noise. Agreed this one should be closed.

    One thing that might be useful for whoever reviews #35989: the crash only reproduces with a descriptor the wallet can actually expand. My first version of the test reused the sh(wpkh(xpriv/...)) descriptor from the nearby checks, and in a watch-only wallet that fails earlier with -4 before ever reaching TopUpWithDB, so the test passed even without the fix. Might be worth double checking the test there hits the same path.

    I'll go take a look at #35989 instead.


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-09-15 19:51 UTC

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