mirror of
https://github.com/nlohmann/json.git
synced 2026-09-07 16:57:59 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3970ecccd1 | ||
|
|
00e5d21041 |
@@ -996,23 +996,21 @@ class binary_reader
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a CBOR string
|
||||
@brief reads a definite-length CBOR string
|
||||
|
||||
This function first reads starting bytes to determine the expected
|
||||
string length and then copies this number of bytes into a string.
|
||||
Additionally, CBOR's strings with indefinite lengths are supported.
|
||||
Reads everything @ref get_cbor_string accepts except the indefinite-length
|
||||
form, which that function handles itself. The bytes are appended to @a
|
||||
result, so consecutive chunks of an indefinite-length string can be read
|
||||
into the same string.
|
||||
|
||||
@param[out] result created string
|
||||
@param[out] result string the bytes are appended to
|
||||
|
||||
@return whether string creation completed
|
||||
*/
|
||||
bool get_cbor_string(string_t& result)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@pre @a current is not EOF
|
||||
*/
|
||||
bool get_cbor_string_chunk(string_t& result)
|
||||
{
|
||||
switch (current)
|
||||
{
|
||||
// UTF-8 string (0x00..0x17 bytes follow)
|
||||
@@ -1068,20 +1066,6 @@ class binary_reader
|
||||
return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
|
||||
}
|
||||
|
||||
case 0x7F: // UTF-8 string (indefinite length)
|
||||
{
|
||||
while (get() != 0xFF)
|
||||
{
|
||||
string_t chunk;
|
||||
if (!get_cbor_string(chunk))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.append(chunk);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
@@ -1092,23 +1076,82 @@ class binary_reader
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a CBOR byte array
|
||||
@brief reads a CBOR string
|
||||
|
||||
This function first reads starting bytes to determine the expected
|
||||
byte array length and then copies this number of bytes into the byte array.
|
||||
Additionally, CBOR's byte arrays with indefinite lengths are supported.
|
||||
string length and then copies this number of bytes into a string.
|
||||
Additionally, CBOR's strings with indefinite lengths are supported.
|
||||
|
||||
@param[out] result created byte array
|
||||
@param[out] result created string
|
||||
|
||||
@return whether string creation completed
|
||||
*/
|
||||
bool get_cbor_string(string_t& result)
|
||||
{
|
||||
// number of indefinite-length strings that have been opened and not
|
||||
// closed yet. RFC 8949, Section 3.2.3 does not permit nesting them,
|
||||
// but this reader has always accepted it, so the open levels are
|
||||
// counted instead of recursed through, which overflowed the stack for
|
||||
// an input of repeated 0x7F bytes (see #5104). Every chunk is appended
|
||||
// to the same result, so no per-level state is needed.
|
||||
std::size_t open = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current == 0x7F) // UTF-8 string (indefinite length)
|
||||
{
|
||||
++open;
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
// a break marker closes the innermost indefinite-length string;
|
||||
// outside of one it is not a string and falls through to the error
|
||||
if (open != 0 && current == 0xFF)
|
||||
{
|
||||
if (--open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
get();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a definite-length CBOR byte array
|
||||
|
||||
Reads everything @ref get_cbor_binary accepts except the indefinite-length
|
||||
form, which that function handles itself. The bytes are appended to @a
|
||||
result, so consecutive chunks of an indefinite-length byte array can be
|
||||
read into the same byte array.
|
||||
|
||||
@param[out] result byte array the bytes are appended to
|
||||
|
||||
@return whether byte array creation completed
|
||||
*/
|
||||
bool get_cbor_binary(binary_t& result)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@pre @a current is not EOF
|
||||
*/
|
||||
bool get_cbor_binary_chunk(binary_t& result)
|
||||
{
|
||||
switch (current)
|
||||
{
|
||||
// Binary data (0x00..0x17 bytes follow)
|
||||
@@ -1168,20 +1211,6 @@ class binary_reader
|
||||
get_binary(input_format_t::cbor, len, result);
|
||||
}
|
||||
|
||||
case 0x5F: // Binary data (indefinite length)
|
||||
{
|
||||
while (get() != 0xFF)
|
||||
{
|
||||
binary_t chunk;
|
||||
if (!get_cbor_binary(chunk))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.insert(result.end(), chunk.begin(), chunk.end());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
@@ -1191,6 +1220,63 @@ class binary_reader
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a CBOR byte array
|
||||
|
||||
This function first reads starting bytes to determine the expected
|
||||
byte array length and then copies this number of bytes into the byte array.
|
||||
Additionally, CBOR's byte arrays with indefinite lengths are supported.
|
||||
|
||||
@param[out] result created byte array
|
||||
|
||||
@return whether byte array creation completed
|
||||
*/
|
||||
bool get_cbor_binary(binary_t& result)
|
||||
{
|
||||
// the open indefinite-length byte arrays are counted rather than
|
||||
// recursed through, for the reason given in @ref get_cbor_string
|
||||
std::size_t open = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current == 0x5F) // Binary data (indefinite length)
|
||||
{
|
||||
++open;
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
// a break marker closes the innermost indefinite-length byte
|
||||
// array; outside of one it falls through to the error below
|
||||
if (open != 0 && current == 0xFF)
|
||||
{
|
||||
if (--open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_binary_chunk(result)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
get();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief narrow a definite CBOR array/map length to std::size_t
|
||||
|
||||
@@ -2391,7 +2477,12 @@ class binary_reader
|
||||
{
|
||||
result.first = npos; // size
|
||||
result.second = 0; // type
|
||||
bool is_ndarray = false;
|
||||
// seed the flag with the caller's context: inside an ndarray dimension
|
||||
// vector another ndarray is not allowed, and get_ubjson_size_value()
|
||||
// rejects it up front instead of reading it and reporting afterwards.
|
||||
// Seeding it with `false` made every '#' of a "[#[#[..." chain descend
|
||||
// another level, which overflowed the stack (see #5104).
|
||||
bool is_ndarray = inside_ndarray;
|
||||
|
||||
get_ignore_noop();
|
||||
|
||||
@@ -2424,13 +2515,11 @@ class binary_reader
|
||||
}
|
||||
|
||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||
if (input_format == input_format_t::bjdata && is_ndarray)
|
||||
// an ndarray was read here only if the flag flipped; when it was
|
||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||
// dimension vector
|
||||
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
||||
{
|
||||
if (inside_ndarray)
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
|
||||
exception_message(input_format, "ndarray can not be recursive", "size"), nullptr));
|
||||
}
|
||||
result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
|
||||
}
|
||||
return is_error;
|
||||
@@ -2439,7 +2528,7 @@ class binary_reader
|
||||
if (current == '#')
|
||||
{
|
||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||
if (input_format == input_format_t::bjdata && is_ndarray)
|
||||
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
|
||||
exception_message(input_format, "ndarray requires both type and size", "size"), nullptr));
|
||||
|
||||
@@ -11683,23 +11683,21 @@ class binary_reader
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a CBOR string
|
||||
@brief reads a definite-length CBOR string
|
||||
|
||||
This function first reads starting bytes to determine the expected
|
||||
string length and then copies this number of bytes into a string.
|
||||
Additionally, CBOR's strings with indefinite lengths are supported.
|
||||
Reads everything @ref get_cbor_string accepts except the indefinite-length
|
||||
form, which that function handles itself. The bytes are appended to @a
|
||||
result, so consecutive chunks of an indefinite-length string can be read
|
||||
into the same string.
|
||||
|
||||
@param[out] result created string
|
||||
@param[out] result string the bytes are appended to
|
||||
|
||||
@return whether string creation completed
|
||||
*/
|
||||
bool get_cbor_string(string_t& result)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@pre @a current is not EOF
|
||||
*/
|
||||
bool get_cbor_string_chunk(string_t& result)
|
||||
{
|
||||
switch (current)
|
||||
{
|
||||
// UTF-8 string (0x00..0x17 bytes follow)
|
||||
@@ -11755,20 +11753,6 @@ class binary_reader
|
||||
return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
|
||||
}
|
||||
|
||||
case 0x7F: // UTF-8 string (indefinite length)
|
||||
{
|
||||
while (get() != 0xFF)
|
||||
{
|
||||
string_t chunk;
|
||||
if (!get_cbor_string(chunk))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.append(chunk);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
@@ -11779,23 +11763,82 @@ class binary_reader
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a CBOR byte array
|
||||
@brief reads a CBOR string
|
||||
|
||||
This function first reads starting bytes to determine the expected
|
||||
byte array length and then copies this number of bytes into the byte array.
|
||||
Additionally, CBOR's byte arrays with indefinite lengths are supported.
|
||||
string length and then copies this number of bytes into a string.
|
||||
Additionally, CBOR's strings with indefinite lengths are supported.
|
||||
|
||||
@param[out] result created byte array
|
||||
@param[out] result created string
|
||||
|
||||
@return whether string creation completed
|
||||
*/
|
||||
bool get_cbor_string(string_t& result)
|
||||
{
|
||||
// number of indefinite-length strings that have been opened and not
|
||||
// closed yet. RFC 8949, Section 3.2.3 does not permit nesting them,
|
||||
// but this reader has always accepted it, so the open levels are
|
||||
// counted instead of recursed through, which overflowed the stack for
|
||||
// an input of repeated 0x7F bytes (see #5104). Every chunk is appended
|
||||
// to the same result, so no per-level state is needed.
|
||||
std::size_t open = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current == 0x7F) // UTF-8 string (indefinite length)
|
||||
{
|
||||
++open;
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
// a break marker closes the innermost indefinite-length string;
|
||||
// outside of one it is not a string and falls through to the error
|
||||
if (open != 0 && current == 0xFF)
|
||||
{
|
||||
if (--open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_string_chunk(result)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
get();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a definite-length CBOR byte array
|
||||
|
||||
Reads everything @ref get_cbor_binary accepts except the indefinite-length
|
||||
form, which that function handles itself. The bytes are appended to @a
|
||||
result, so consecutive chunks of an indefinite-length byte array can be
|
||||
read into the same byte array.
|
||||
|
||||
@param[out] result byte array the bytes are appended to
|
||||
|
||||
@return whether byte array creation completed
|
||||
*/
|
||||
bool get_cbor_binary(binary_t& result)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@pre @a current is not EOF
|
||||
*/
|
||||
bool get_cbor_binary_chunk(binary_t& result)
|
||||
{
|
||||
switch (current)
|
||||
{
|
||||
// Binary data (0x00..0x17 bytes follow)
|
||||
@@ -11855,20 +11898,6 @@ class binary_reader
|
||||
get_binary(input_format_t::cbor, len, result);
|
||||
}
|
||||
|
||||
case 0x5F: // Binary data (indefinite length)
|
||||
{
|
||||
while (get() != 0xFF)
|
||||
{
|
||||
binary_t chunk;
|
||||
if (!get_cbor_binary(chunk))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.insert(result.end(), chunk.begin(), chunk.end());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
auto last_token = get_token_string();
|
||||
@@ -11878,6 +11907,63 @@ class binary_reader
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a CBOR byte array
|
||||
|
||||
This function first reads starting bytes to determine the expected
|
||||
byte array length and then copies this number of bytes into the byte array.
|
||||
Additionally, CBOR's byte arrays with indefinite lengths are supported.
|
||||
|
||||
@param[out] result created byte array
|
||||
|
||||
@return whether byte array creation completed
|
||||
*/
|
||||
bool get_cbor_binary(binary_t& result)
|
||||
{
|
||||
// the open indefinite-length byte arrays are counted rather than
|
||||
// recursed through, for the reason given in @ref get_cbor_string
|
||||
std::size_t open = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current == 0x5F) // Binary data (indefinite length)
|
||||
{
|
||||
++open;
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
// a break marker closes the innermost indefinite-length byte
|
||||
// array; outside of one it falls through to the error below
|
||||
if (open != 0 && current == 0xFF)
|
||||
{
|
||||
if (--open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
get();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_cbor_binary_chunk(result)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (open == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
get();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief narrow a definite CBOR array/map length to std::size_t
|
||||
|
||||
@@ -13078,7 +13164,12 @@ class binary_reader
|
||||
{
|
||||
result.first = npos; // size
|
||||
result.second = 0; // type
|
||||
bool is_ndarray = false;
|
||||
// seed the flag with the caller's context: inside an ndarray dimension
|
||||
// vector another ndarray is not allowed, and get_ubjson_size_value()
|
||||
// rejects it up front instead of reading it and reporting afterwards.
|
||||
// Seeding it with `false` made every '#' of a "[#[#[..." chain descend
|
||||
// another level, which overflowed the stack (see #5104).
|
||||
bool is_ndarray = inside_ndarray;
|
||||
|
||||
get_ignore_noop();
|
||||
|
||||
@@ -13111,13 +13202,11 @@ class binary_reader
|
||||
}
|
||||
|
||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||
if (input_format == input_format_t::bjdata && is_ndarray)
|
||||
// an ndarray was read here only if the flag flipped; when it was
|
||||
// seeded true, get_ubjson_size_value() already rejected the nested
|
||||
// dimension vector
|
||||
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
||||
{
|
||||
if (inside_ndarray)
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
|
||||
exception_message(input_format, "ndarray can not be recursive", "size"), nullptr));
|
||||
}
|
||||
result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
|
||||
}
|
||||
return is_error;
|
||||
@@ -13126,7 +13215,7 @@ class binary_reader
|
||||
if (current == '#')
|
||||
{
|
||||
const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
|
||||
if (input_format == input_format_t::bjdata && is_ndarray)
|
||||
if (input_format == input_format_t::bjdata && is_ndarray && !inside_ndarray)
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
|
||||
exception_message(input_format, "ndarray requires both type and size", "size"), nullptr));
|
||||
|
||||
@@ -3288,8 +3288,10 @@ TEST_CASE("BJData")
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR1), "[json.exception.parse_error.113] parse error at byte 6: syntax error while parsing BJData size: ndarray dimensional vector is not allowed", json::parse_error&);
|
||||
CHECK(json::from_bjdata(vR1, true, false).is_discarded());
|
||||
|
||||
// a dimension vector that opens another one is rejected where the
|
||||
// nested '[' is read, rather than after it has been descended into
|
||||
std::vector<uint8_t> const vR2 = {'[', '$', 'i', '#', '[', '#', '[', 'i', 1, ']', ']', 1};
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR2), "[json.exception.parse_error.113] parse error at byte 11: syntax error while parsing BJData size: expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x5D", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR2), "[json.exception.parse_error.113] parse error at byte 7: syntax error while parsing BJData size: ndarray dimensional vector is not allowed", json::parse_error&);
|
||||
CHECK(json::from_bjdata(vR2, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> const vR3 = {'[', '#', '[', 'i', '2', 'i', 2, ']'};
|
||||
@@ -3297,7 +3299,7 @@ TEST_CASE("BJData")
|
||||
CHECK(json::from_bjdata(vR3, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> const vR4 = {'[', '$', 'i', '#', '[', '$', 'i', '#', '[', 'i', 1, ']', 1};
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR4), "[json.exception.parse_error.110] parse error at byte 14: syntax error while parsing BJData number: unexpected end of input", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR4), "[json.exception.parse_error.113] parse error at byte 9: syntax error while parsing BJData size: ndarray dimensional vector is not allowed", json::parse_error&);
|
||||
CHECK(json::from_bjdata(vR4, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> const vR5 = {'[', '$', 'i', '#', '[', '[', '[', ']', ']', ']'};
|
||||
@@ -3305,12 +3307,25 @@ TEST_CASE("BJData")
|
||||
CHECK(json::from_bjdata(vR5, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> const vR6 = {'[', '$', 'i', '#', '[', '$', 'i', '#', '[', 'i', '2', 'i', 2, ']'};
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR6), "[json.exception.parse_error.112] parse error at byte 14: syntax error while parsing BJData size: ndarray can not be recursive", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vR6), "[json.exception.parse_error.113] parse error at byte 9: syntax error while parsing BJData size: ndarray dimensional vector is not allowed", json::parse_error&);
|
||||
CHECK(json::from_bjdata(vR6, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> const vH = {'[', 'H', '[', '#', '[', '$', 'i', '#', '[', 'i', '2', 'i', 2, ']'};
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vH), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData size: ndarray dimensional vector is not allowed", json::parse_error&);
|
||||
CHECK(json::from_bjdata(vH, true, false).is_discarded());
|
||||
|
||||
// Every "#[" of this chain used to open another dimension vector
|
||||
// and cost several stack frames before anything was rejected, so a
|
||||
// long enough chain crashed the process (see #5104). The nested
|
||||
// vector is refused where it is read, so the length is irrelevant.
|
||||
std::vector<uint8_t> vRdeep = {'['};
|
||||
for (std::size_t i = 0; i < 100000; ++i)
|
||||
{
|
||||
vRdeep.push_back('#');
|
||||
vRdeep.push_back('[');
|
||||
}
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vRdeep), "[json.exception.parse_error.113] parse error at byte 5: syntax error while parsing BJData size: ndarray dimensional vector is not allowed", json::parse_error&);
|
||||
CHECK(json::from_bjdata(vRdeep, true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("objects")
|
||||
|
||||
@@ -2035,6 +2035,58 @@ TEST_CASE("CBOR definite length equal to the indefinite-length sentinel")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("CBOR indefinite-length strings do not recurse per chunk")
|
||||
{
|
||||
// Reading an indefinite-length string or byte array used to call itself
|
||||
// once per chunk, so a payload of repeated 0x7F (or 0x5F) bytes exhausted
|
||||
// the call stack before any of the input was rejected. The open levels are
|
||||
// counted now, and the levels below prove the reader still reads the same
|
||||
// values and reports the same errors at the same byte offsets.
|
||||
json _;
|
||||
|
||||
SECTION("many open levels are reported, not crashed on")
|
||||
{
|
||||
const std::vector<uint8_t> input(200000, 0x7F);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input), "[json.exception.parse_error.110] parse error at byte 200001: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&);
|
||||
CHECK(json::from_cbor(input, true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("many open levels are reported, not crashed on (binary)")
|
||||
{
|
||||
const std::vector<uint8_t> input(200000, 0x5F);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(input), "[json.exception.parse_error.110] parse error at byte 200001: syntax error while parsing CBOR binary: unexpected end of input", json::parse_error&);
|
||||
CHECK(json::from_cbor(input, true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("chunks are still concatenated")
|
||||
{
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0xFF})) == json(""));
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0x61, 0x61, 0xFF})) == json("a"));
|
||||
// nested indefinite-length strings are concatenated across levels
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0x7F, 0x61, 0x61, 0xFF, 0x61, 0x62, 0xFF})) == json("ab"));
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0x7F, 0x7F, 0x61, 0x7A, 0xFF, 0xFF, 0xFF})) == json("z"));
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0xA1, 0x7F, 0x61, 0x61, 0xFF, 0x01})) == json({{"a", 1}}));
|
||||
}
|
||||
|
||||
SECTION("chunks are still concatenated (binary)")
|
||||
{
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x5F, 0x41, 0x61, 0xFF})) == json::binary({0x61}));
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x5F, 0x5F, 0x41, 0x61, 0xFF, 0x41, 0x62, 0xFF})) == json::binary({0x61, 0x62}));
|
||||
}
|
||||
|
||||
SECTION("a chunk that is not a string is still rejected")
|
||||
{
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x7F, 0x7F, 0x00})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x00", json::parse_error&);
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x5F, 0x5F, 0x00})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR binary: expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x00", json::parse_error&);
|
||||
}
|
||||
|
||||
SECTION("a break marker outside an indefinite-length string is not a string")
|
||||
{
|
||||
// 0xFF only closes a string that was opened; on its own it is not one
|
||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0xFF, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0xFF", json::parse_error&);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("CBOR roundtrips" * doctest::skip())
|
||||
{
|
||||
SECTION("input from flynn")
|
||||
|
||||
Reference in New Issue
Block a user