diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index aa2f0cbf4c..11eac0a29c 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -43,6 +43,34 @@ inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n) n = nullptr; } +template +ArithmeticTypeTarget static_cast_check_range(const BasicJsonType& j) +{ + const ArithmeticTypeSource val = *j.template get_ptr(); + if constexpr (sizeof(ArithmeticTypeTarget) < sizeof(ArithmeticTypeSource) || std::is_floating_point::value) + { + const ArithmeticTypeSource min = + std::is_signed::value ? std::numeric_limits::lowest() : 0; + const ArithmeticTypeSource max = std::numeric_limits::max(); + bool valIsInf = false; + if constexpr (std::is_floating_point::value) + { + valIsInf = std::isinf(val); + } + if ((val < min || val > max) && !valIsInf) + { + JSON_THROW( + out_of_range::create( + 406, + "value " + std::to_string(val) + " is out of target integer range [" + + std::to_string(std::numeric_limits::lowest()) + ", " + + std::to_string(std::numeric_limits::max()) + "]", &j)); + } + } + return static_cast(val); +} + + // overloads for basic_json template parameters template < typename BasicJsonType, typename ArithmeticType, enable_if_t < std::is_arithmetic::value&& @@ -54,17 +82,17 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) { case value_t::number_unsigned: { - val = static_cast(*j.template get_ptr()); + val = static_cast_check_range(j); break; } case value_t::number_integer: { - val = static_cast(*j.template get_ptr()); + val = static_cast_check_range(j); break; } case value_t::number_float: { - val = static_cast(*j.template get_ptr()); + val = static_cast_check_range(j); break; } @@ -343,17 +371,17 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val) { case value_t::number_unsigned: { - val = static_cast(*j.template get_ptr()); + val = static_cast_check_range(j); break; } case value_t::number_integer: { - val = static_cast(*j.template get_ptr()); + val = static_cast_check_range(j); break; } case value_t::number_float: { - val = static_cast(*j.template get_ptr()); + val = static_cast_check_range(j); break; } case value_t::boolean: diff --git a/tests/src/unit-class_parser.cpp b/tests/src/unit-class_parser.cpp index e2a8bac0f7..114295f192 100644 --- a/tests/src/unit-class_parser.cpp +++ b/tests/src/unit-class_parser.cpp @@ -585,6 +585,15 @@ TEST_CASE("parser class") CHECK_THROWS_WITH_AS(parser_helper("1.18973e+4932").empty(), "[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'", json::out_of_range&); } + SECTION("out-of-range-conversion") + { + // overflows during parsing with casting yield an exception + CHECK_THROWS_WITH_AS(parser_helper("123456").get(), "[json.exception.out_of_range.406] value 123456 is out of target integer range [-32768, 32767]", json::out_of_range&); + CHECK_THROWS_WITH_AS(parser_helper("65536").get(), "[json.exception.out_of_range.406] value 65536 is out of target integer range [-32768, 32767]", json::out_of_range&); + CHECK_THROWS_WITH_AS(parser_helper("-32769").get(), "[json.exception.out_of_range.406] value -32769 is out of target integer range [-32768, 32767]", json::out_of_range&); + CHECK_THROWS_WITH_AS(parser_helper("18446744073709559808").get(), "[json.exception.out_of_range.406] value 18446744073709559808.000000 is out of target integer range [0, 18446744073709551615]", json::out_of_range&); + } + SECTION("invalid numbers") { // numbers must not begin with "+"