From e1813ea7afd0ed55fc33786114bff6debda4958f Mon Sep 17 00:00:00 2001 From: Nikolaus Schuetz Date: Thu, 13 Aug 2026 23:10:59 -0400 Subject: [PATCH 1/2] Fix integer overflow in --parameter-scan near i32::MAX The RangeStep iterator advanced with an unconditional state += step, which overflows on the final element when the range ends at i32::MAX (panic in debug, silent wraparound to i32::MIN in release, generating ~4 billion commands). Track completion with a finished flag and compute the step with end - state, which cannot overflow since state <= end. Closes #920 --- src/parameter/range_step.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/parameter/range_step.rs b/src/parameter/range_step.rs index bffbeb12e..8dfd2cc65 100644 --- a/src/parameter/range_step.rs +++ b/src/parameter/range_step.rs @@ -35,6 +35,7 @@ pub struct RangeStep { state: T, end: T, step: T, + finished: bool, } impl RangeStep { @@ -53,6 +54,7 @@ impl RangeStep { state: start, end, step, + finished: false, }), _ => Err(ParameterScanError::TooLarge), } @@ -63,11 +65,16 @@ impl Iterator for RangeStep { type Item = T; fn next(&mut self) -> Option { - if self.state > self.end { + if self.finished || self.state > self.end { return None; } let return_val = self.state; - self.state += self.step; + // `end - state` never overflows here (state <= end), unlike `state + step`. + if self.end - self.state < self.step { + self.finished = true; + } else { + self.state += self.step; + } Some(return_val) } @@ -120,6 +127,12 @@ mod tests { assert_eq!(param_range[10], Decimal::from(1)); } + #[test] + fn does_not_overflow_near_type_max() { + let param_range: Vec = RangeStep::new(i32::MAX - 1, i32::MAX, 1).unwrap().collect(); + assert_eq!(param_range, vec![i32::MAX - 1, i32::MAX]); + } + #[test] fn test_range_step_validate() { let result = RangeStep::new(0, 10, 3); From 32b76b3e5c59ddb6331798502a7f1e0bea1ab3a0 Mon Sep 17 00:00:00 2001 From: Nikolaus Schuetz Date: Wed, 19 Aug 2026 13:42:16 -0500 Subject: [PATCH 2/2] range_step: fix size_hint after exhaustion and correct overflow-safety comment --- src/parameter/range_step.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/parameter/range_step.rs b/src/parameter/range_step.rs index 8dfd2cc65..51a168abc 100644 --- a/src/parameter/range_step.rs +++ b/src/parameter/range_step.rs @@ -69,7 +69,8 @@ impl Iterator for RangeStep { return None; } let return_val = self.state; - // `end - state` never overflows here (state <= end), unlike `state + step`. + // new() capped the range at MAX_PARAMETERS steps, so `end - state` stays + // small and cannot overflow here, unlike a speculative `state + step`. if self.end - self.state < self.step { self.finished = true; } else { @@ -80,6 +81,9 @@ impl Iterator for RangeStep { } fn size_hint(&self) -> (usize, Option) { + if self.finished { + return (0, Some(0)); + } range_step_size_hint(self.state, self.end, self.step) } } @@ -133,6 +137,16 @@ mod tests { assert_eq!(param_range, vec![i32::MAX - 1, i32::MAX]); } + #[test] + fn size_hint_is_zero_once_exhausted() { + let mut it = RangeStep::new(i32::MAX - 1, i32::MAX, 1).unwrap(); + assert_eq!(it.size_hint(), (2, Some(2))); + assert_eq!(it.next(), Some(i32::MAX - 1)); + assert_eq!(it.next(), Some(i32::MAX)); + assert_eq!(it.next(), None); + assert_eq!(it.size_hint(), (0, Some(0))); + } + #[test] fn test_range_step_validate() { let result = RangeStep::new(0, 10, 3);