From c670f92ce8077eaee7678329a75c4965c30cdb70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Furga=C5=82a?= <83299832+00200200@users.noreply.github.com> Date: Tue, 29 Sep 2026 17:59:49 +0200 Subject: [PATCH] Reject oversized --parameter-scan ranges without overflowing i32. range_step_size_hint evaluated (end - start + 1) / step in i32, so a full-width scan panicked in debug (and wrapped in release) before the MAX_PARAMETERS cap could reject it. Compute that count in i64 instead. --- src/parameter/range_step.rs | 33 ++++++++++++++++++++++++++++++++- tests/integration_tests.rs | 13 +++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/parameter/range_step.rs b/src/parameter/range_step.rs index 88ed76597..d9ba20701 100644 --- a/src/parameter/range_step.rs +++ b/src/parameter/range_step.rs @@ -1,4 +1,4 @@ -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; use std::ops::{Add, AddAssign, Div, Sub}; use crate::error::ParameterScanError; @@ -86,6 +86,18 @@ fn range_step_size_hint(start: T, end: T, step: T) -> (usize, Option return (usize::MAX, None); } + // Integer scans are stored as i32. Computing `(end - start + 1) / step` + // in i32 overflows for a full-width span (`i32::MIN..=i32::MAX`) and for + // ranges where `end - start` itself fits but `+ 1` does not (`0..=i32::MAX`). + // Those ranges are far above MAX_PARAMETERS; evaluate them in i64 so we + // can reject them instead of panicking in debug / wrapping in release. + if let (Number::Int(s), Number::Int(e), Number::Int(st)) = + (start.into(), end.into(), step.into()) + { + let steps = (i64::from(e) - i64::from(s) + 1) / i64::from(st); + return usize::try_from(steps).map_or((usize::MAX, None), |u| (u, Some(u))); + } + let steps = (end - start + T::from(1)) / step; steps .into() @@ -181,4 +193,23 @@ mod tests { "Parameter range is too large" ); } + + #[test] + fn full_i32_span_is_too_large_not_overflow() { + let result = RangeStep::new(i32::MIN, i32::MAX, 1); + assert_eq!( + format!("{}", result.unwrap_err()), + "Parameter range is too large" + ); + } + + #[test] + fn i32_add_overflow_span_is_too_large_not_overflow() { + // `(end - start + 1)` overflows i32 even though `end - start` does not. + let result = RangeStep::new(0, i32::MAX, 1); + assert_eq!( + format!("{}", result.unwrap_err()), + "Parameter range is too large" + ); + } } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index e6832b2aa..e6e2089e4 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1020,6 +1020,19 @@ fn speed_comparison_sort_order() { "); } +#[test] +fn parameter_scan_full_i32_range_is_rejected() { + hyperfine() + .arg("--parameter-scan") + .arg("x") + .arg("-2147483648") + .arg("2147483647") + .arg("true") + .assert() + .failure() + .stderr(predicate::str::contains("Parameter range is too large")); +} + #[cfg(windows)] #[test] fn windows_quote_args() {