diff --git a/cpp/src/arrow/util/decimal.cc b/cpp/src/arrow/util/decimal.cc index d80164f45c0..4cdcd961eb9 100644 --- a/cpp/src/arrow/util/decimal.cc +++ b/cpp/src/arrow/util/decimal.cc @@ -881,9 +881,15 @@ Status DecimalFromString(const char* type_name, std::string_view s, Decimal* out int32_t parsed_scale = 0; if (dec.has_exponent) { - auto adjusted_exponent = dec.exponent; - parsed_scale = - -adjusted_exponent + static_cast(dec.fractional_digits.size()); + // parsed_scale = -exponent + fractional_digits, computed with overflow + // detection: an exponent of INT32_MIN ("0E-2147483648") makes the negation, + // and a near-INT32_MIN exponent the addition, signed-overflow UB otherwise. + if (internal::SubtractWithOverflow( + static_cast(dec.fractional_digits.size()), dec.exponent, + &parsed_scale)) { + return Status::Invalid("The string '", s, "' cannot be represented as ", + type_name); + } } else { parsed_scale = static_cast(dec.fractional_digits.size()); } @@ -945,9 +951,15 @@ Status SimpleDecimalFromString(const char* type_name, std::string_view s, int32_t parsed_scale = 0; if (dec.has_exponent) { - auto adjusted_exponent = dec.exponent; - parsed_scale = - -adjusted_exponent + static_cast(dec.fractional_digits.size()); + // parsed_scale = -exponent + fractional_digits, computed with overflow + // detection: an exponent of INT32_MIN ("0E-2147483648") makes the negation, + // and a near-INT32_MIN exponent the addition, signed-overflow UB otherwise. + if (internal::SubtractWithOverflow( + static_cast(dec.fractional_digits.size()), dec.exponent, + &parsed_scale)) { + return Status::Invalid("The string '", s, "' cannot be represented as ", + type_name); + } } else { parsed_scale = static_cast(dec.fractional_digits.size()); } diff --git a/cpp/src/arrow/util/decimal_test.cc b/cpp/src/arrow/util/decimal_test.cc index e0aa0b2b85a..397b0a5e6eb 100644 --- a/cpp/src/arrow/util/decimal_test.cc +++ b/cpp/src/arrow/util/decimal_test.cc @@ -136,7 +136,7 @@ class DecimalFromStringTest : public ::testing::Test { for (const std::string invalid_value : {"-", "0.0.0", "0-13-32", "a", "-23092.235-", "-+23092.235", "+-23092.235", "00a", "1e1a", "0.00123D/3", "1.23eA8", "1.23E+3A", "-1.23E--5", - "1.2345E+++07"}) { + "1.2345E+++07", "0E-2147483648", "1.0E-2147483647"}) { ARROW_SCOPED_TRACE("invalid_value = '", invalid_value, "'"); ASSERT_RAISES(Invalid, DecimalType::FromString(invalid_value)); }