From d479ef4f70d5fb3b6de58975d61dffd03ed66465 Mon Sep 17 00:00:00 2001 From: metsw24-max Date: Wed, 27 May 2026 22:20:18 +0530 Subject: [PATCH] GH-50057: [C++] Avoid signed overflow in Decimal FromString exponent --- cpp/src/arrow/util/decimal.cc | 24 ++++++++++++++++++------ cpp/src/arrow/util/decimal_test.cc | 2 +- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/cpp/src/arrow/util/decimal.cc b/cpp/src/arrow/util/decimal.cc index d80164f45c0e..4cdcd961eb9f 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 e0aa0b2b85a4..397b0a5e6ebc 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)); }