mirror of
https://github.com/nlohmann/json.git
synced 2026-06-18 10:24:18 +00:00
Compare commits
3 Commits
customers
...
bson-sizes
| Author | SHA1 | Date | |
|---|---|---|---|
| 42f4df1159 | |||
| c78d9dc386 | |||
| 530ab84ed6 |
@@ -170,7 +170,24 @@ class binary_reader
|
|||||||
bool parse_bson_internal()
|
bool parse_bson_internal()
|
||||||
{
|
{
|
||||||
std::int32_t document_size{};
|
std::int32_t document_size{};
|
||||||
get_number<std::int32_t, true>(input_format_t::bson, document_size);
|
if (JSON_HEDLEY_UNLIKELY((!get_number<std::int32_t, true>(input_format_t::bson, document_size))))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(document_size < 5))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format_t::bson,
|
||||||
|
concat("BSON document size must be at least 5, is ", std::to_string(document_size)),
|
||||||
|
"document size"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The document begins at the size field and ends document_size bytes later
|
||||||
|
// (including the size field itself and the trailing 0x00 terminator).
|
||||||
|
const std::size_t document_start = chars_read - sizeof(std::int32_t);
|
||||||
|
const std::size_t expected_end = document_start + static_cast<std::size_t>(document_size);
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size())))
|
||||||
{
|
{
|
||||||
@@ -182,6 +199,15 @@ class binary_reader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(chars_read != expected_end))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format_t::bson,
|
||||||
|
"BSON document terminator did not land at declared document size",
|
||||||
|
"document"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
return sax->end_object();
|
return sax->end_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +257,21 @@ class binary_reader
|
|||||||
exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
|
exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != char_traits<char_type>::eof();
|
if (JSON_HEDLEY_UNLIKELY(!get_string(input_format_t::bson, len - static_cast<NumberType>(1), result)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(get() != 0x00))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format_t::bson,
|
||||||
|
"BSON string is not null-terminated",
|
||||||
|
"string"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -398,7 +438,24 @@ class binary_reader
|
|||||||
bool parse_bson_array()
|
bool parse_bson_array()
|
||||||
{
|
{
|
||||||
std::int32_t document_size{};
|
std::int32_t document_size{};
|
||||||
get_number<std::int32_t, true>(input_format_t::bson, document_size);
|
if (JSON_HEDLEY_UNLIKELY((!get_number<std::int32_t, true>(input_format_t::bson, document_size))))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(document_size < 5))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format_t::bson,
|
||||||
|
concat("BSON document size must be at least 5, is ", std::to_string(document_size)),
|
||||||
|
"document size"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The document begins at the size field and ends document_size bytes later
|
||||||
|
// (including the size field itself and the trailing 0x00 terminator).
|
||||||
|
const std::size_t document_start = chars_read - sizeof(std::int32_t);
|
||||||
|
const std::size_t expected_end = document_start + static_cast<std::size_t>(document_size);
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size())))
|
||||||
{
|
{
|
||||||
@@ -410,6 +467,15 @@ class binary_reader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(chars_read != expected_end))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format_t::bson,
|
||||||
|
"BSON array terminator did not land at declared array size",
|
||||||
|
"array"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
return sax->end_array();
|
return sax->end_array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10312,7 +10312,24 @@ class binary_reader
|
|||||||
bool parse_bson_internal()
|
bool parse_bson_internal()
|
||||||
{
|
{
|
||||||
std::int32_t document_size{};
|
std::int32_t document_size{};
|
||||||
get_number<std::int32_t, true>(input_format_t::bson, document_size);
|
if (JSON_HEDLEY_UNLIKELY((!get_number<std::int32_t, true>(input_format_t::bson, document_size))))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(document_size < 5))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format_t::bson,
|
||||||
|
concat("BSON document size must be at least 5, is ", std::to_string(document_size)),
|
||||||
|
"document size"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The document begins at the size field and ends document_size bytes later
|
||||||
|
// (including the size field itself and the trailing 0x00 terminator).
|
||||||
|
const std::size_t document_start = chars_read - sizeof(std::int32_t);
|
||||||
|
const std::size_t expected_end = document_start + static_cast<std::size_t>(document_size);
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size())))
|
||||||
{
|
{
|
||||||
@@ -10324,6 +10341,15 @@ class binary_reader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(chars_read != expected_end))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format_t::bson,
|
||||||
|
"BSON document terminator did not land at declared document size",
|
||||||
|
"document"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
return sax->end_object();
|
return sax->end_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10373,7 +10399,21 @@ class binary_reader
|
|||||||
exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
|
exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != char_traits<char_type>::eof();
|
if (JSON_HEDLEY_UNLIKELY(!get_string(input_format_t::bson, len - static_cast<NumberType>(1), result)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(get() != 0x00))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format_t::bson,
|
||||||
|
"BSON string is not null-terminated",
|
||||||
|
"string"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -10540,7 +10580,24 @@ class binary_reader
|
|||||||
bool parse_bson_array()
|
bool parse_bson_array()
|
||||||
{
|
{
|
||||||
std::int32_t document_size{};
|
std::int32_t document_size{};
|
||||||
get_number<std::int32_t, true>(input_format_t::bson, document_size);
|
if (JSON_HEDLEY_UNLIKELY((!get_number<std::int32_t, true>(input_format_t::bson, document_size))))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(document_size < 5))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format_t::bson,
|
||||||
|
concat("BSON document size must be at least 5, is ", std::to_string(document_size)),
|
||||||
|
"document size"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The document begins at the size field and ends document_size bytes later
|
||||||
|
// (including the size field itself and the trailing 0x00 terminator).
|
||||||
|
const std::size_t document_start = chars_read - sizeof(std::int32_t);
|
||||||
|
const std::size_t expected_end = document_start + static_cast<std::size_t>(document_size);
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size())))
|
if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size())))
|
||||||
{
|
{
|
||||||
@@ -10552,6 +10609,15 @@ class binary_reader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(chars_read != expected_end))
|
||||||
|
{
|
||||||
|
auto last_token = get_token_string();
|
||||||
|
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
|
||||||
|
exception_message(input_format_t::bson,
|
||||||
|
"BSON array terminator did not land at declared array size",
|
||||||
|
"array"), nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
return sax->end_array();
|
return sax->end_array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1294,3 +1294,76 @@ TEST_CASE("BSON roundtrips" * doctest::skip())
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Invalid document size handling")
|
||||||
|
{
|
||||||
|
SECTION("document size must be at least 5")
|
||||||
|
{
|
||||||
|
std::vector<std::uint8_t> const v = {0x04, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
json _;
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bson(v), "[json.exception.parse_error.112] parse error at byte 4: syntax error while parsing BSON document size: BSON document size must be at least 5, is 4", json::parse_error&);
|
||||||
|
CHECK(json::from_bson(v, true, false).is_discarded());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("declared document size must match consumed bytes (extra trailing element)")
|
||||||
|
{
|
||||||
|
// Declares 5-byte empty document but appends an int32 element after the declared end.
|
||||||
|
std::vector<std::uint8_t> const v =
|
||||||
|
{
|
||||||
|
0x05, 0x00, 0x00, 0x00,
|
||||||
|
0x10, 'a', 'd', 'm', 'i', 'n', 0x00,
|
||||||
|
0x01, 0x00, 0x00, 0x00,
|
||||||
|
0x00
|
||||||
|
};
|
||||||
|
json _;
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bson(v), "[json.exception.parse_error.112] parse error at byte 16: syntax error while parsing BSON document: BSON document terminator did not land at declared document size", json::parse_error&);
|
||||||
|
CHECK(json::from_bson(v, true, false).is_discarded());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("declared document size must match consumed bytes (premature terminator)")
|
||||||
|
{
|
||||||
|
// Declares 32-byte document but only contains the size field followed by an immediate terminator.
|
||||||
|
std::vector<std::uint8_t> const v =
|
||||||
|
{
|
||||||
|
0x20, 0x00, 0x00, 0x00,
|
||||||
|
0x00
|
||||||
|
};
|
||||||
|
json _;
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bson(v), "[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: BSON document terminator did not land at declared document size", json::parse_error&);
|
||||||
|
CHECK(json::from_bson(v, true, false).is_discarded());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("array declared size must match consumed bytes")
|
||||||
|
{
|
||||||
|
// Outer object contains an array "a" that declares 5 bytes (empty) but
|
||||||
|
// actually contains an int32 element before its terminator.
|
||||||
|
std::vector<std::uint8_t> const v =
|
||||||
|
{
|
||||||
|
0x14, 0x00, 0x00, 0x00, // object size = 20
|
||||||
|
0x04, 'a', 0x00, // key "a", array type
|
||||||
|
0x05, 0x00, 0x00, 0x00, // array declared size = 5 (empty)
|
||||||
|
0x10, '0', 0x00, 0x01, 0x00, 0x00, 0x00, // extra int32 element "0" = 1
|
||||||
|
0x00, // array terminator
|
||||||
|
0x00 // object terminator
|
||||||
|
};
|
||||||
|
json _;
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bson(v), "[json.exception.parse_error.112] parse error at byte 19: syntax error while parsing BSON array: BSON array terminator did not land at declared array size", json::parse_error&);
|
||||||
|
CHECK(json::from_bson(v, true, false).is_discarded());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("BSON string must end with 0x00")
|
||||||
|
{
|
||||||
|
// Length-prefixed string whose terminator byte is 'X' (0x58), not 0x00.
|
||||||
|
std::vector<std::uint8_t> const v =
|
||||||
|
{
|
||||||
|
0x0F, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 's', 0x00,
|
||||||
|
0x02, 0x00, 0x00, 0x00,
|
||||||
|
'A', 'X',
|
||||||
|
0x00
|
||||||
|
};
|
||||||
|
json _;
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bson(v), "[json.exception.parse_error.112] parse error at byte 13: syntax error while parsing BSON string: BSON string is not null-terminated", json::parse_error&);
|
||||||
|
CHECK(json::from_bson(v, true, false).is_discarded());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -311,14 +311,16 @@ TEST_CASE("dump for basic_json with long double number_float_t")
|
|||||||
SECTION("round-trip dump/parse")
|
SECTION("round-trip dump/parse")
|
||||||
{
|
{
|
||||||
constexpr std::array<long double, 13> values =
|
constexpr std::array<long double, 13> values =
|
||||||
{{
|
{
|
||||||
0.0L, -0.0L, 1.0L, -1.0L,
|
{
|
||||||
0.5L, -0.5L, 1.5L, -2.25L,
|
0.0L, -0.0L, 1.0L, -1.0L,
|
||||||
1.23e45L, 1.23e-45L,
|
0.5L, -0.5L, 1.5L, -2.25L,
|
||||||
(std::numeric_limits<long double>::min)(),
|
1.23e45L, 1.23e-45L,
|
||||||
std::numeric_limits<long double>::lowest(),
|
(std::numeric_limits<long double>::min)(),
|
||||||
(std::numeric_limits<long double>::max)()
|
std::numeric_limits<long double>::lowest(),
|
||||||
}};
|
(std::numeric_limits<long double>::max)()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for (long double v : values)
|
for (long double v : values)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user