diff --git a/include/intx/intx.hpp b/include/intx/intx.hpp index 20967468..90e74909 100644 --- a/include/intx/intx.hpp +++ b/include/intx/intx.hpp @@ -840,9 +840,11 @@ constexpr Int from_string(const char* str) throw_(str); const auto d = from_dec_digit(c); - x = x * Int{10} + d; - if (x < d) + // Check for overflow before multiplying, since checking after could be fooled by + // wraparound. + if (x > (std::numeric_limits::max() - d) / Int{10}) throw_(str); + x = x * Int{10} + d; } return x; } diff --git a/test/unittests/test_intx.cpp b/test/unittests/test_intx.cpp index 02f9f8f4..880d1c2a 100644 --- a/test/unittests/test_intx.cpp +++ b/test/unittests/test_intx.cpp @@ -348,6 +348,15 @@ TYPED_TEST(uint_test, string_conversions) } } +TYPED_TEST(uint_test, from_string_decimal_overflow) +{ + // A decimal string with as many digits as TypeParam::max() but consisting of all 9s + // is always greater than max() and must be rejected, not silently wrapped around. + const auto digits = to_string(std::numeric_limits::max()).size(); + const auto s = std::string(digits, '9'); + EXPECT_THROW_MESSAGE(from_string(s), std::out_of_range, s.c_str()); +} + TYPED_TEST(uint_test, to_string_base) { auto x = TypeParam{1024};