From b3c0633b9560f909fa93a80a8275e06f9ee2c0b9 Mon Sep 17 00:00:00 2001 From: Alexander Shirokov Date: Wed, 15 Jan 2025 20:54:52 +0100 Subject: [PATCH] seq:add bounds for exponents Add bounds for exponents to avoid overflow issues for inputs like 'seq 1e-9223372036854775808' --- src/uu/seq/src/numberparse.rs | 35 ++++++++++++++++++++++++++--------- tests/by-util/test_seq.rs | 10 ++++++++++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/uu/seq/src/numberparse.rs b/src/uu/seq/src/numberparse.rs index 478622515cc..d00db16fa13 100644 --- a/src/uu/seq/src/numberparse.rs +++ b/src/uu/seq/src/numberparse.rs @@ -103,15 +103,18 @@ fn parse_exponent_no_decimal(s: &str, j: usize) -> Result() - .map_err(|_| ParseNumberError::Float)?; - if parsed_decimal == BigDecimal::zero() { - BigDecimal::zero() - } else { - parsed_decimal - } + + // In ['BigDecimal'], a positive scale represents a negative power of 10. + // This means the exponent value from the number must be inverted. However, + // since the |i64::MIN| > |i64::MAX| (i.e. |−2^63| > |2^63−1|) inverting a + // valid negative value could result in an overflow. To prevent this, we + // limit the minimal value with i64::MIN + 1. + let exponent = exponent.max(i64::MIN + 1); + let base: BigInt = s[..j].parse().map_err(|_| ParseNumberError::Float)?; + let x = if base.is_zero() { + BigDecimal::zero() + } else { + BigDecimal::from_bigint(base, -exponent) }; let num_integral_digits = if is_minus_zero_float(s, &x) { @@ -599,4 +602,18 @@ mod tests { assert_eq!(num_fractional_digits("-0e-1"), 1); assert_eq!(num_fractional_digits("-0.0e-1"), 2); } + + #[test] + fn test_parse_min_exponents() { + // Make sure exponents <= i64::MIN do not cause errors + assert!("1e-9223372036854775807".parse::().is_ok()); + assert!("1e-9223372036854775808".parse::().is_ok()); + } + + #[test] + fn test_parse_max_exponents() { + // Make sure exponents >= i64::MAX cause errors + assert!("1e9223372036854775807".parse::().is_err()); + assert!("1e9223372036854775808".parse::().is_err()); + } } diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 78fcb0068c7..d8ab71842a4 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -878,6 +878,16 @@ fn test_parse_float_gnu_coreutils() { .stdout_only("0.000000\n0.000001\n0.000002\n0.000003\n"); } +#[test] +fn test_parse_out_of_bounds_exponents() { + // The value 1e-9223372036854775808 is used in GNU Coreutils and BigDecimal tests to verify + // overflows and undefined behavior. Let's test the value too. + new_ucmd!() + .args(&["1e-9223372036854775808"]) + .succeeds() + .stdout_only(""); +} + #[ignore] #[test] fn test_parse_valid_hexadecimal_float_format_issues() {