diff --git a/include/nlohmann/detail/exceptions.hpp b/include/nlohmann/detail/exceptions.hpp index 0ab4243de..b73d7b1f9 100644 --- a/include/nlohmann/detail/exceptions.hpp +++ b/include/nlohmann/detail/exceptions.hpp @@ -263,6 +263,7 @@ json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON only supports integers numbers up to 9223372036854775807. | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | @liveexample{The following code shows how an `out_of_range` exception can be caught.,out_of_range} diff --git a/include/nlohmann/detail/input/binary_reader.hpp b/include/nlohmann/detail/input/binary_reader.hpp index ca7bcabaf..d4ca38f5f 100644 --- a/include/nlohmann/detail/input/binary_reader.hpp +++ b/include/nlohmann/detail/input/binary_reader.hpp @@ -1246,14 +1246,22 @@ class binary_reader if (size_and_type.first != string_t::npos) { + if (JSON_UNLIKELY(size_and_type.first > result.max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive array size: " + std::to_string(size_and_type.first))); + } + if (size_and_type.second != 0) { if (size_and_type.second != 'N') + { std::generate_n(std::back_inserter(*result.m_value.array), size_and_type.first, [this, size_and_type]() - { - return get_ubjson_value(size_and_type.second); - }); + { + return get_ubjson_value(size_and_type.second); + }); + } } else { @@ -1283,6 +1291,12 @@ class binary_reader if (size_and_type.first != string_t::npos) { + if (JSON_UNLIKELY(size_and_type.first > result.max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive object size: " + std::to_string(size_and_type.first))); + } + if (size_and_type.second != 0) { std::generate_n(std::inserter(*result.m_value.object, diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index ad4712bdb..50ccbc413 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -756,6 +756,7 @@ json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON only supports integers numbers up to 9223372036854775807. | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | @liveexample{The following code shows how an `out_of_range` exception can be caught.,out_of_range} @@ -6066,14 +6067,22 @@ class binary_reader if (size_and_type.first != string_t::npos) { + if (JSON_UNLIKELY(size_and_type.first > result.max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive array size: " + std::to_string(size_and_type.first))); + } + if (size_and_type.second != 0) { if (size_and_type.second != 'N') + { std::generate_n(std::back_inserter(*result.m_value.array), size_and_type.first, [this, size_and_type]() - { - return get_ubjson_value(size_and_type.second); - }); + { + return get_ubjson_value(size_and_type.second); + }); + } } else { @@ -6103,6 +6112,12 @@ class binary_reader if (size_and_type.first != string_t::npos) { + if (JSON_UNLIKELY(size_and_type.first > result.max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive object size: " + std::to_string(size_and_type.first))); + } + if (size_and_type.second != 0) { std::generate_n(std::inserter(*result.m_value.object, diff --git a/test/src/fuzzer-parse_ubjson.cpp b/test/src/fuzzer-parse_ubjson.cpp index 1ad07677f..fa33901ac 100644 --- a/test/src/fuzzer-parse_ubjson.cpp +++ b/test/src/fuzzer-parse_ubjson.cpp @@ -58,6 +58,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { // type errors can occur during parsing, too } + catch (const json::out_of_range&) + { + // out of range errors may happen if provided sizes are excessive + } // return 0 - non-zero return values are reserved for future use return 0; diff --git a/test/src/unit-regression.cpp b/test/src/unit-regression.cpp index dd8840d54..a3064ae67 100644 --- a/test/src/unit-regression.cpp +++ b/test/src/unit-regression.cpp @@ -1423,4 +1423,17 @@ TEST_CASE("regression tests") json j = json::from_cbor(v_cbor); CHECK(j == "abcd123"); } + + SECTION("issue #962 - Timeout (OSS-Fuzz 6034)") + { + std::vector v_ubjson = {0x5b, 0x24, 0x5a, 0x23, 0x4c, 0x78, 0x28, 0x00, 0x68, 0x28, 0x69, 0x69, 0x17}; + CHECK_THROWS_AS(json::from_ubjson(v_ubjson), json::out_of_range&); + CHECK_THROWS_WITH(json::from_ubjson(v_ubjson), + "[json.exception.out_of_range.408] excessive array size: 8658170730974374167"); + + v_ubjson[0] = '{'; + CHECK_THROWS_AS(json::from_ubjson(v_ubjson), json::out_of_range&); + CHECK_THROWS_WITH(json::from_ubjson(v_ubjson), + "[json.exception.out_of_range.408] excessive object size: 8658170730974374167"); + } }