Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion include/nlohmann/detail/input/binary_reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,19 @@ class binary_reader
std::int32_t document_size{};
get_number<std::int32_t, true>(input_format_t::bson, document_size);

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));
}

// chars_read now points just past the 4-byte size field;
// the document started 4 bytes earlier and must end at start + document_size.
const std::size_t expected_end = chars_read + static_cast<std::size_t>(document_size) - 4;

if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size())))
{
return false;
Expand All @@ -182,6 +195,15 @@ class binary_reader
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();
}

Expand Down Expand Up @@ -231,7 +253,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));
}

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;
}

/*!
Expand Down Expand Up @@ -400,6 +436,19 @@ class binary_reader
std::int32_t document_size{};
get_number<std::int32_t, true>(input_format_t::bson, document_size);

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));
}

// chars_read now points just past the 4-byte size field;
// the document started 4 bytes earlier and must end at start + document_size.
const std::size_t expected_end = chars_read + static_cast<std::size_t>(document_size) - 4;

if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size())))
{
return false;
Expand All @@ -410,6 +459,15 @@ class binary_reader
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_array();
}

Expand Down
60 changes: 59 additions & 1 deletion single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10314,6 +10314,19 @@ class binary_reader
std::int32_t document_size{};
get_number<std::int32_t, true>(input_format_t::bson, document_size);

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));
}

// chars_read now points just past the 4-byte size field;
// the document started 4 bytes earlier and must end at start + document_size.
const std::size_t expected_end = chars_read + static_cast<std::size_t>(document_size) - 4;

if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size())))
{
return false;
Expand All @@ -10324,6 +10337,15 @@ class binary_reader
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();
}

Expand Down Expand Up @@ -10373,7 +10395,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));
}

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;
}

/*!
Expand Down Expand Up @@ -10542,6 +10578,19 @@ class binary_reader
std::int32_t document_size{};
get_number<std::int32_t, true>(input_format_t::bson, document_size);

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));
}

// chars_read now points just past the 4-byte size field;
// the document started 4 bytes earlier and must end at start + document_size.
const std::size_t expected_end = chars_read + static_cast<std::size_t>(document_size) - 4;

if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size())))
{
return false;
Expand All @@ -10552,6 +10601,15 @@ class binary_reader
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_array();
}

Expand Down
39 changes: 39 additions & 0 deletions tests/src/unit-bson.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1294,3 +1294,42 @@ 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&);
}

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&);
}

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&);
}
}
18 changes: 10 additions & 8 deletions tests/src/unit-serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,14 +311,16 @@ TEST_CASE("dump for basic_json with long double number_float_t")
SECTION("round-trip dump/parse")
{
constexpr std::array<long double, 13> values =
{{
0.0L, -0.0L, 1.0L, -1.0L,
0.5L, -0.5L, 1.5L, -2.25L,
1.23e45L, 1.23e-45L,
(std::numeric_limits<long double>::min)(),
std::numeric_limits<long double>::lowest(),
(std::numeric_limits<long double>::max)()
}};
{
{
0.0L, -0.0L, 1.0L, -1.0L,
0.5L, -0.5L, 1.5L, -2.25L,
1.23e45L, 1.23e-45L,
(std::numeric_limits<long double>::min)(),
std::numeric_limits<long double>::lowest(),
(std::numeric_limits<long double>::max)()
}
};
Comment on lines +314 to +323
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This is an unrelated whitespace change that was done by astyle.


for (long double v : values)
{
Expand Down
Loading