MessagePack reader: reject an array/map 32 length equal to SIZE_MAX

On a platform with a 32-bit std::size_t, an array 32 or map 32 length of
0xFFFFFFFF equals unknown_size(), so start_array()/start_object() were
told the size was unknown and skipped the max_size() check. Route both
through the CBOR length check, renamed get_container_size(), which
already rejects that value.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-11-02 18:25:06 +01:00
1 parent db73a6044f
commit da6d21ae24
4 files changed
+39 -15

No files matched your search

@@ -1024,7 +1024,7 @@ class binary_reader
{
std::uint64_t len{};
std::size_t size{};
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_cbor_array(size);
return get_cbor_argument(len) && get_container_size(len, size, "array") && enter_cbor_array(size);
}
case 0x9F: // array (indefinite length)
@@ -1064,7 +1064,7 @@ class binary_reader
{
std::uint64_t len{};
std::size_t size{};
return get_cbor_argument(len) && get_cbor_container_size(len, size, "map") && enter_object(size);
return get_cbor_argument(len) && get_container_size(len, size, "map") && enter_object(size);
}
case 0xBF: // map (indefinite length)
@@ -1700,7 +1700,7 @@ class binary_reader
}
/*!
@brief narrow a definite CBOR array/map length to std::size_t
@brief narrow a definite CBOR or MessagePack array/map length to std::size_t
A definite length is rejected if it does not fit in std::size_t or if it
equals detail::unknown_size(), which is reserved to mark an indefinite-
@@ -1713,7 +1713,7 @@ class binary_reader
@param[in] context "array" or "map", for the error message
@return whether the length is usable
*/
bool get_cbor_container_size(const std::uint64_t len, std::size_t& result, const char* context)
bool get_container_size(const std::uint64_t len, std::size_t& result, const char* context)
{
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
{
@@ -2305,7 +2305,8 @@ class binary_reader
case 0xDD: // array 32
{
std::uint32_t len{};
return get_number(len) && enter_msgpack_array(conditional_static_cast<std::size_t>(len));
std::size_t size{};
return get_number(len) && get_container_size(len, size, "array") && enter_msgpack_array(size);
}
case 0xDE: // map 16
@@ -2317,7 +2318,8 @@ class binary_reader
case 0xDF: // map 32
{
std::uint32_t len{};
return get_number(len) && enter_object(conditional_static_cast<std::size_t>(len));
std::size_t size{};
return get_number(len) && get_container_size(len, size, "map") && enter_object(size);
}
// negative fixint
@@ -3128,7 +3130,7 @@ class binary_reader
A count equal to npos is rejected: npos marks a container whose count is
not known, so a count of that value would be read as "no count" (compare
get_cbor_container_size()). No container can hold that many elements.
get_container_size()). No container can hold that many elements.
@param[in] count the count read from the input
@param[out] result set to @a count unless it is rejected
+9 -7
View File
@@ -19031,7 +19031,7 @@ class binary_reader
{
std::uint64_t len{};
std::size_t size{};
return get_cbor_argument(len) && get_cbor_container_size(len, size, "array") && enter_cbor_array(size);
return get_cbor_argument(len) && get_container_size(len, size, "array") && enter_cbor_array(size);
}
case 0x9F: // array (indefinite length)
@@ -19071,7 +19071,7 @@ class binary_reader
{
std::uint64_t len{};
std::size_t size{};
return get_cbor_argument(len) && get_cbor_container_size(len, size, "map") && enter_object(size);
return get_cbor_argument(len) && get_container_size(len, size, "map") && enter_object(size);
}
case 0xBF: // map (indefinite length)
@@ -19707,7 +19707,7 @@ class binary_reader
}
/*!
@brief narrow a definite CBOR array/map length to std::size_t
@brief narrow a definite CBOR or MessagePack array/map length to std::size_t
A definite length is rejected if it does not fit in std::size_t or if it
equals detail::unknown_size(), which is reserved to mark an indefinite-
@@ -19720,7 +19720,7 @@ class binary_reader
@param[in] context "array" or "map", for the error message
@return whether the length is usable
*/
bool get_cbor_container_size(const std::uint64_t len, std::size_t& result, const char* context)
bool get_container_size(const std::uint64_t len, std::size_t& result, const char* context)
{
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
{
@@ -20312,7 +20312,8 @@ class binary_reader
case 0xDD: // array 32
{
std::uint32_t len{};
return get_number(len) && enter_msgpack_array(conditional_static_cast<std::size_t>(len));
std::size_t size{};
return get_number(len) && get_container_size(len, size, "array") && enter_msgpack_array(size);
}
case 0xDE: // map 16
@@ -20324,7 +20325,8 @@ class binary_reader
case 0xDF: // map 32
{
std::uint32_t len{};
return get_number(len) && enter_object(conditional_static_cast<std::size_t>(len));
std::size_t size{};
return get_number(len) && get_container_size(len, size, "map") && enter_object(size);
}
// negative fixint
@@ -21135,7 +21137,7 @@ class binary_reader
A count equal to npos is rejected: npos marks a container whose count is
not known, so a count of that value would be read as "no count" (compare
get_cbor_container_size()). No container can hold that many elements.
get_container_size()). No container can hold that many elements.
@param[in] count the count read from the input
@param[out] result set to @a count unless it is rejected
+20
View File
@@ -97,3 +97,23 @@ TEST_CASE("CBOR")
}
}
}
TEST_CASE("MessagePack")
{
SECTION("parse errors")
{
SECTION("array/map size equal to the unknown-size sentinel")
{
// on 32-bit platforms an array 32/map 32 length of 0xFFFFFFFF aliases unknown_size()
std::vector<uint8_t> const varr = {0xDD, 0xFF, 0xFF, 0xFF, 0xFF};
std::vector<uint8_t> const vmap = {0xDF, 0xFF, 0xFF, 0xFF, 0xFF};
json _;
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(varr), "[json.exception.out_of_range.408] syntax error while parsing MessagePack size: excessive array size", json::out_of_range&);
CHECK(json::from_msgpack(varr, true, false).is_discarded());
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vmap), "[json.exception.out_of_range.408] syntax error while parsing MessagePack size: excessive map size", json::out_of_range&);
CHECK(json::from_msgpack(vmap, true, false).is_discarded());
}
}
}
+1 -1
View File
@@ -1005,7 +1005,7 @@ TEST_CASE("regression test - excessive binary container size honors allow_except
// allow_exceptions=true (the default) must still throw exactly as before.
// The exact message text is not checked here: on platforms where
// std::size_t is 32-bit, the CBOR reader's own length-narrowing check
// (get_cbor_container_size(), unrelated to this fix) intercepts a
// (get_container_size(), unrelated to this fix) intercepts a
// declared length of 2^63 before it ever reaches the check this test
// targets, with different (but equally valid, and already correct)
// wording -- see unit-cbor.cpp for coverage of that message.