diff --git a/test/boost/big_decimal_test.cc b/test/boost/big_decimal_test.cc index 1f744a1edd..ac94ee3964 100644 --- a/test/boost/big_decimal_test.cc +++ b/test/boost/big_decimal_test.cc @@ -99,6 +99,23 @@ BOOST_AUTO_TEST_CASE(test_big_decimal_construct_from_string) { BOOST_REQUIRE_THROW(big_decimal("+-5"), marshal_exception); BOOST_REQUIRE_THROW(big_decimal("++5"), marshal_exception); BOOST_REQUIRE_THROW(big_decimal("--5"), marshal_exception); + + // Verify large exponent gets parsed correctly + // 1E-2147483647 : scale = 2147483647; OK + BOOST_REQUIRE_NO_THROW(big_decimal("1E-2147483647")); + // 1E2147483648 : scale = -2147483648; OK + BOOST_REQUIRE_NO_THROW(big_decimal("1E2147483648")); + // 0.01E2147483650 : scale = -2147483648; + // exponent is > int32::max() but the adjusted scale is still within int32 limits, so it is OK. + BOOST_REQUIRE_NO_THROW(big_decimal("0.01E2147483650")); + + // Any overflow to scale should throw marshal_exception. + // 1E-2147483648 : scale(2147483648) > int32::max() + BOOST_REQUIRE_THROW(big_decimal("1E-2147483648"), marshal_exception); + // 1E2147483649 : scale(-2147483649) < int32::min() + BOOST_REQUIRE_THROW(big_decimal("1E2147483649"), marshal_exception); + // 1.2E-2147483647 : scale(2147483648) > int32::max() + BOOST_REQUIRE_THROW(big_decimal("1.2E-2147483647"), marshal_exception); } BOOST_AUTO_TEST_CASE(test_big_decimal_div) { diff --git a/utils/big_decimal.cc b/utils/big_decimal.cc index b83c86d18d..698087a213 100644 --- a/utils/big_decimal.cc +++ b/utils/big_decimal.cc @@ -78,12 +78,18 @@ big_decimal::big_decimal(std::string_view text) if (negative) { _unscaled_value *= -1; } + // parse scale as int64_t, so that it can be adjusted with fraction size and then checked for overflow. + int64_t scale = 0; try { - _scale = exponent.empty() ? 0 : -boost::lexical_cast(exponent); + scale = exponent.empty() ? 0 : -boost::lexical_cast(exponent); } catch (...) { throw marshal_exception(seastar::format("big_decimal - failed to parse exponent: {}", exponent)); } - _scale += fraction.size(); + scale += fraction.size(); + if (scale < std::numeric_limits::min() || scale > std::numeric_limits::max()) { + throw marshal_exception(seastar::format("big_decimal - scale out of range: {}", scale)); + } + _scale = static_cast(scale); } boost::multiprecision::cpp_rational big_decimal::as_rational() const {