From: Elias Rohrer Date: Wed, 8 Apr 2026 09:42:39 +0000 (+0200) Subject: fix(chain): prevent integer overflow in `SpkIterator::new_with_range` X-Git-Url: http://internal-gitweb-vhost/?a=commitdiff_plain;h=b340e96f0221f6c3088fe5daf2b87c56b3bf4010;p=bdk fix(chain): prevent integer overflow in `SpkIterator::new_with_range` The start and end bound calculations used unchecked addition which overflows when given `u32::MAX`, causing the iterator to silently produce wrong results (e.g., an empty iterator for `0..=u32::MAX` in release mode). Use `saturating_add` to handle the boundary correctly. Co-Authored-By: HAL 9000 Signed-off-by: Elias Rohrer --- diff --git a/crates/chain/src/spk_iter.rs b/crates/chain/src/spk_iter.rs index 2353807a..32112119 100644 --- a/crates/chain/src/spk_iter.rs +++ b/crates/chain/src/spk_iter.rs @@ -67,12 +67,12 @@ where { let start = match range.start_bound() { Bound::Included(start) => *start, - Bound::Excluded(start) => *start + 1, + Bound::Excluded(start) => start.saturating_add(1), Bound::Unbounded => u32::MIN, }; let mut end = match range.end_bound() { - Bound::Included(end) => *end + 1, + Bound::Included(end) => end.saturating_add(1), Bound::Excluded(end) => *end, Bound::Unbounded => u32::MAX, }; @@ -136,6 +136,8 @@ where #[cfg(test)] #[cfg_attr(coverage_nightly, coverage(off))] mod test { + use core::ops::Bound; + use crate::{ bitcoin::secp256k1::Secp256k1, indexer::keychain_txout::KeychainTxOutIndex, @@ -264,6 +266,31 @@ mod test { None ); } + + #[test] + fn test_spkiterator_no_overflow_on_u32_max_range() { + let (_, external_desc, _) = init_txout_index(); + + // Inclusive range up to u32::MAX should not overflow and should produce items + // (clamped to BIP32_MAX_INDEX). + let mut iter = SpkIterator::new_with_range(&external_desc, 0..=u32::MAX); + assert!( + iter.next().is_some(), + "0..=u32::MAX range should produce items, not overflow to empty" + ); + + // Exclusive start at u32::MAX should not overflow and should produce an empty iterator + // (start saturates to u32::MAX which is beyond BIP32_MAX_INDEX). + let mut iter = SpkIterator::new_with_range( + &external_desc, + (Bound::Excluded(u32::MAX), Bound::Included(u32::MAX)), + ); + assert_eq!( + iter.next(), + None, + "range starting after u32::MAX should be empty" + ); + } } #[test]