diff --git a/cpp/src/arrow/util/decimal.cc b/cpp/src/arrow/util/decimal.cc index d80164f45c0e..a9d2fcb02d94 100644 --- a/cpp/src/arrow/util/decimal.cc +++ b/cpp/src/arrow/util/decimal.cc @@ -881,9 +881,13 @@ 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 +949,13 @@ 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 e0aa0b2b85a4..7022c8117802 100644 --- a/cpp/src/arrow/util/decimal_test.cc +++ b/cpp/src/arrow/util/decimal_test.cc @@ -135,8 +135,8 @@ class DecimalFromStringTest : public ::testing::Test { void TestInvalidInput() { 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"}) { + "00a", "1e1a", "0.00123D/3", "1.23eA8", "1.23E+3A", "-1.23E--5", "1.2345E+++07", + "0E-2147483648", "1.0E-2147483647"}) { ARROW_SCOPED_TRACE("invalid_value = '", invalid_value, "'"); ASSERT_RAISES(Invalid, DecimalType::FromString(invalid_value)); }