Fix NUL bytes in UBJSON/BJData high-precision numbers (#5760)

* Fix NUL-terminated UBJSON and BJData high-precision payloads

Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com>

* Use English comments for the high-precision NUL fix

Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com>

* Track NUL bytes while reading high-precision payloads

Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com>

* Drop dates from code comments.

Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com>

* Report a NUL in a high-precision number where it is read

Return the parse error from the read loop so the byte offset points at the NUL, and drop the separate check after lexing.

Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com>

* Use fixed text for the high-precision NUL error

2026-10-06: Use the known zero byte directly instead of formatting and concatenating it. Preserve the SAX token, error message, and byte offset.
Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com>

---------

Signed-off-by: fhgffy <102001626+fhgffy@users.noreply.github.com>
This commit is contained in:
fhgffy authored and GitHub committed 2026-10-06 22:30:52 +02:00
1 parent a7d387a762
commit 69874e4544
4 files changed
+64

No files matched your search

@@ -3249,6 +3249,12 @@ class binary_reader
{
return false;
}
// the lexer would stop at a NUL and accept the digits before it
if (JSON_HEDLEY_UNLIKELY(current == '\0'))
{
return sax->parse_error(chars_read, "00", parse_error::create(115, chars_read,
exception_message("invalid number text; last byte: 0x00", "high-precision number"), nullptr));
}
number_vector.push_back(static_cast<char>(current));
}
+6
View File
@@ -16998,6 +16998,12 @@ class binary_reader
{
return false;
}
// the lexer would stop at a NUL and accept the digits before it
if (JSON_HEDLEY_UNLIKELY(current == '\0'))
{
return sax->parse_error(chars_read, "00", parse_error::create(115, chars_read,
exception_message("invalid number text; last byte: 0x00", "high-precision number"), nullptr));
}
number_vector.push_back(static_cast<char>(current));
}
+26
View File
@@ -1286,6 +1286,32 @@ TEST_CASE("BJData")
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vec2), "[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing BJData high-precision number: invalid number text: 1A", json::parse_error);
std::vector<uint8_t> const vec3 = {'H', 'i', 2, '1', '.'};
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vec3), "[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing BJData high-precision number: invalid number text: 1.", json::parse_error);
// Reject NULs where they are read, including trailing NULs and payloads cut off after one.
SECTION("NUL in high-precision number (issue #5753)")
{
for (const auto& vec : std::vector<std::vector<uint8_t>>
{
{'H', 'i', 3, '1', 0, 'x'},
{'H', 'i', 2, '1', 0},
{'H', 'i', 3, '1', 0}
})
{
CAPTURE(vec)
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vec), "[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing BJData high-precision number: invalid number text; last byte: 0x00", json::parse_error);
CHECK(json::from_bjdata(vec, true, false).is_discarded());
CHECK(json::from_bjdata(vec, false, false).is_discarded());
}
std::vector<uint8_t> const nested = {'[', 'H', 'i', 3, '1', 0, 'x', ']'};
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(nested), "[json.exception.parse_error.115] parse error at byte 6: syntax error while parsing BJData high-precision number: invalid number text; last byte: 0x00", json::parse_error);
CHECK(json::from_bjdata(nested, true, false).is_discarded());
std::vector<uint8_t> const valid = {'H', 'i', 1, '1'};
const auto j = json::from_bjdata(valid);
CHECK(j.is_number_unsigned());
CHECK(j == json(1));
}
std::vector<uint8_t> const vec_overflow = {'H', 'i', 5, '1', 'e', '4', '0', '0'};
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vec_overflow), "[json.exception.out_of_range.406] number overflow parsing '1e400'", json::out_of_range);
std::vector<uint8_t> const vec4 = {'H', 2, '1', '0'};
+26
View File
@@ -802,6 +802,32 @@ TEST_CASE("UBJSON")
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vec2), "[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A", json::parse_error);
std::vector<uint8_t> const vec3 = {'H', 'i', 2, '1', '.'};
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vec3), "[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1.", json::parse_error);
// Reject NULs where they are read, including trailing NULs and payloads cut off after one.
SECTION("NUL in high-precision number (issue #5753)")
{
for (const auto& vec : std::vector<std::vector<uint8_t>>
{
{'H', 'i', 3, '1', 0, 'x'},
{'H', 'i', 2, '1', 0},
{'H', 'i', 3, '1', 0}
})
{
CAPTURE(vec)
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vec), "[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text; last byte: 0x00", json::parse_error);
CHECK(json::from_ubjson(vec, true, false).is_discarded());
CHECK(json::from_ubjson(vec, false, false).is_discarded());
}
std::vector<uint8_t> const nested = {'[', 'H', 'i', 3, '1', 0, 'x', ']'};
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(nested), "[json.exception.parse_error.115] parse error at byte 6: syntax error while parsing UBJSON high-precision number: invalid number text; last byte: 0x00", json::parse_error);
CHECK(json::from_ubjson(nested, true, false).is_discarded());
std::vector<uint8_t> const valid = {'H', 'i', 1, '1'};
const auto j = json::from_ubjson(valid);
CHECK(j.is_number_unsigned());
CHECK(j == json(1));
}
std::vector<uint8_t> const vec_overflow = {'H', 'i', 5, '1', 'e', '4', '0', '0'};
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vec_overflow), "[json.exception.out_of_range.406] number overflow parsing '1e400'", json::out_of_range&);
std::vector<uint8_t> const vec4 = {'H', 2, '1', '0'};