Skip to content

Commit 6060a3e

Browse files
authored
Merge pull request #7145 from alexs-sh/seq-fix-min-exponent
seq: add a lower bound for exponents
2 parents dca0ec0 + b3c0633 commit 6060a3e

File tree

2 files changed

+36
-9
lines changed

2 files changed

+36
-9
lines changed

src/uu/seq/src/numberparse.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,18 @@ fn parse_exponent_no_decimal(s: &str, j: usize) -> Result<PreciseNumber, ParseNu
103103
// displayed in decimal notation. For example, "1e-2" will be
104104
// displayed as "0.01", but "1e2" will be displayed as "100",
105105
// without a decimal point.
106-
let x: BigDecimal = {
107-
let parsed_decimal = s
108-
.parse::<BigDecimal>()
109-
.map_err(|_| ParseNumberError::Float)?;
110-
if parsed_decimal == BigDecimal::zero() {
111-
BigDecimal::zero()
112-
} else {
113-
parsed_decimal
114-
}
106+
107+
// In ['BigDecimal'], a positive scale represents a negative power of 10.
108+
// This means the exponent value from the number must be inverted. However,
109+
// since the |i64::MIN| > |i64::MAX| (i.e. |−2^63| > |2^63−1|) inverting a
110+
// valid negative value could result in an overflow. To prevent this, we
111+
// limit the minimal value with i64::MIN + 1.
112+
let exponent = exponent.max(i64::MIN + 1);
113+
let base: BigInt = s[..j].parse().map_err(|_| ParseNumberError::Float)?;
114+
let x = if base.is_zero() {
115+
BigDecimal::zero()
116+
} else {
117+
BigDecimal::from_bigint(base, -exponent)
115118
};
116119

117120
let num_integral_digits = if is_minus_zero_float(s, &x) {
@@ -599,4 +602,18 @@ mod tests {
599602
assert_eq!(num_fractional_digits("-0e-1"), 1);
600603
assert_eq!(num_fractional_digits("-0.0e-1"), 2);
601604
}
605+
606+
#[test]
607+
fn test_parse_min_exponents() {
608+
// Make sure exponents <= i64::MIN do not cause errors
609+
assert!("1e-9223372036854775807".parse::<PreciseNumber>().is_ok());
610+
assert!("1e-9223372036854775808".parse::<PreciseNumber>().is_ok());
611+
}
612+
613+
#[test]
614+
fn test_parse_max_exponents() {
615+
// Make sure exponents >= i64::MAX cause errors
616+
assert!("1e9223372036854775807".parse::<PreciseNumber>().is_err());
617+
assert!("1e9223372036854775808".parse::<PreciseNumber>().is_err());
618+
}
602619
}

tests/by-util/test_seq.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,16 @@ fn test_parse_float_gnu_coreutils() {
878878
.stdout_only("0.000000\n0.000001\n0.000002\n0.000003\n");
879879
}
880880

881+
#[test]
882+
fn test_parse_out_of_bounds_exponents() {
883+
// The value 1e-9223372036854775808 is used in GNU Coreutils and BigDecimal tests to verify
884+
// overflows and undefined behavior. Let's test the value too.
885+
new_ucmd!()
886+
.args(&["1e-9223372036854775808"])
887+
.succeeds()
888+
.stdout_only("");
889+
}
890+
881891
#[ignore]
882892
#[test]
883893
fn test_parse_valid_hexadecimal_float_format_issues() {

0 commit comments

Comments
 (0)