json_view: keep tokens without an integer digit out of the float batch (#6049)

dump() batches float tokens of 16 or 17 digits with at least 8 fraction
digits. A damaged image loaded with image_check::bounds can record a
token with no digit before the point and 17 after it, which no JSON
number has. Its q = -17 is outside the range of locate_token(), which
then read fives[17] past its 17 entries; on x86-64 the resulting digits
had 18 digits and failed the assertion of write_decimal() (OSS-Fuzz
issue 583898810, json_view_image_fuzzer).

The batch now takes 8 to 16 fraction digits, which every valid token of
16 or 17 digits has, so it batches the same tokens of valid documents.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann authored and GitHub committed 2026-11-10 15:24:33 +01:00
1 parent eac1b4343b
commit 5901d861ff
3 files changed
+50 -8

No files matched your search

+6 -4
View File
@@ -393,7 +393,7 @@ struct depth_counter<false>
/*!
@brief where the double x nearest to a token w * 10^q lies, for 10^15 <= w <
10^17 and -17 <= q <= -1 (Eisel-Lemire, then a fixed-point position)
10^17 and -16 <= q <= -1 (Eisel-Lemire, then a fixed-point position)
dump() writes the shortest decimal in the rounding interval of x (the closest
one if there are several, see zmij::to_shortest()). Its digits are those of a
@@ -1258,13 +1258,15 @@ class view_serializer
}
for (; p != stop && batch.count != float_batch::capacity(); ++p)
{
// a token of the source: 16 or 17 digits, at least 8 after the
// point, no exponent, 32 bytes to read
// a token of the source: 16 or 17 digits, 8 to 16 after the
// point, no exponent, 32 bytes to read (17 after the point, with
// no digit before it, only in a damaged image loaded with
// image_check::bounds: beyond the range of locate_token())
const unsigned frac_digits = p->extra >> 8u;
const unsigned int_digits = p->extra & 0xFFu;
const unsigned count = int_digits + frac_digits;
if (p->kind == node_kind::number_float && (p->flags & node_flags::storage) == 0
&& count - 16u <= 1u && frac_digits >= 8 && src_end - (src + p->off) >= 33)
&& count - 16u <= 1u && frac_digits - 8u <= 8u && src_end - (src + p->off) >= 33)
{
const auto* const token = reinterpret_cast<const unsigned char*>(src + p->off); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
const unsigned sign = token[0] == '-' ? 1 : 0;