Check integer-to-float fallbacks for overflow in the binary readers

emit_signed, emit_unsigned, and the CBOR negative integer fallback now
pass their number_float_t fallback through emit_float, so a value that
overflows number_float_t is rejected with out_of_range.406 like a
floating-point value, instead of silently becoming infinity. This only
matters for a number_float_t that cannot represent 2^64, such as a
half-precision type. The CBOR value -1 - n is computed as long double so
that emit_float sees a finite value.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-09-28 20:19:28 +02:00
parent 0c630d4c30
commit 44325873ea
2 changed files with 92 additions and 70 deletions
+46 -35
View File
@@ -600,19 +600,19 @@ class binary_reader
case 0x10: // int32 case 0x10: // int32
{ {
std::int32_t value{}; std::int32_t value{};
return get_number<std::int32_t, true>(input_format_t::bson, value) && emit_signed(value); return get_number<std::int32_t, true>(input_format_t::bson, value) && emit_signed(input_format_t::bson, value);
} }
case 0x12: // int64 case 0x12: // int64
{ {
std::int64_t value{}; std::int64_t value{};
return get_number<std::int64_t, true>(input_format_t::bson, value) && emit_signed(value); return get_number<std::int64_t, true>(input_format_t::bson, value) && emit_signed(input_format_t::bson, value);
} }
case 0x11: // uint64 case 0x11: // uint64
{ {
std::uint64_t value{}; std::uint64_t value{};
return get_number<std::uint64_t, true>(input_format_t::bson, value) && emit_unsigned(value); return get_number<std::uint64_t, true>(input_format_t::bson, value) && emit_unsigned(input_format_t::bson, value);
} }
default: // anything else is not supported (yet) default: // anything else is not supported (yet)
@@ -647,8 +647,10 @@ class binary_reader
} }
// like the lexer does for JSON text, store a value too small for // like the lexer does for JSON text, store a value too small for
// number_integer_t as number_float_t // number_integer_t as number_float_t; compute it as long double so
return sax->number_float(static_cast<number_float_t>(-1) - static_cast<number_float_t>(number), ""); // that emit_float sees a finite value and can detect an overflow of
// number_float_t
return emit_float(input_format_t::cbor, static_cast<long double>(-1) - static_cast<long double>(number));
} }
/*! /*!
@@ -705,25 +707,25 @@ class binary_reader
case 0x18: // Unsigned integer (one-byte uint8_t follows) case 0x18: // Unsigned integer (one-byte uint8_t follows)
{ {
std::uint8_t number{}; std::uint8_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(number); return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
} }
case 0x19: // Unsigned integer (two-byte uint16_t follows) case 0x19: // Unsigned integer (two-byte uint16_t follows)
{ {
std::uint16_t number{}; std::uint16_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(number); return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
} }
case 0x1A: // Unsigned integer (four-byte uint32_t follows) case 0x1A: // Unsigned integer (four-byte uint32_t follows)
{ {
std::uint32_t number{}; std::uint32_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(number); return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
} }
case 0x1B: // Unsigned integer (eight-byte uint64_t follows) case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
{ {
std::uint64_t number{}; std::uint64_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(number); return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
} }
// Negative integer -1-0x00..-1-0x17 (-1..-24) // Negative integer -1-0x00..-1-0x17 (-1..-24)
@@ -1876,49 +1878,49 @@ class binary_reader
case 0xCC: // uint 8 case 0xCC: // uint 8
{ {
std::uint8_t number{}; std::uint8_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(number); return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
} }
case 0xCD: // uint 16 case 0xCD: // uint 16
{ {
std::uint16_t number{}; std::uint16_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(number); return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
} }
case 0xCE: // uint 32 case 0xCE: // uint 32
{ {
std::uint32_t number{}; std::uint32_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(number); return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
} }
case 0xCF: // uint 64 case 0xCF: // uint 64
{ {
std::uint64_t number{}; std::uint64_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(number); return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
} }
case 0xD0: // int 8 case 0xD0: // int 8
{ {
std::int8_t number{}; std::int8_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(number); return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
} }
case 0xD1: // int 16 case 0xD1: // int 16
{ {
std::int16_t number{}; std::int16_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(number); return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
} }
case 0xD2: // int 32 case 0xD2: // int 32
{ {
std::int32_t number{}; std::int32_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(number); return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
} }
case 0xD3: // int 64 case 0xD3: // int 64
{ {
std::int64_t number{}; std::int64_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(number); return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
} }
case 0xDC: // array 16 case 0xDC: // array 16
@@ -2759,7 +2761,7 @@ class binary_reader
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr)); return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(i))) if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(input_format, i)))
{ {
return false; return false;
} }
@@ -2891,37 +2893,37 @@ class binary_reader
break; break;
} }
std::uint8_t number{}; std::uint8_t number{};
return get_number(input_format, number) && emit_unsigned(number); return get_number(input_format, number) && emit_unsigned(input_format, number);
} }
case 'U': case 'U':
{ {
std::uint8_t number{}; std::uint8_t number{};
return get_number(input_format, number) && emit_unsigned(number); return get_number(input_format, number) && emit_unsigned(input_format, number);
} }
case 'i': case 'i':
{ {
std::int8_t number{}; std::int8_t number{};
return get_number(input_format, number) && emit_signed(number); return get_number(input_format, number) && emit_signed(input_format, number);
} }
case 'I': case 'I':
{ {
std::int16_t number{}; std::int16_t number{};
return get_number(input_format, number) && emit_signed(number); return get_number(input_format, number) && emit_signed(input_format, number);
} }
case 'l': case 'l':
{ {
std::int32_t number{}; std::int32_t number{};
return get_number(input_format, number) && emit_signed(number); return get_number(input_format, number) && emit_signed(input_format, number);
} }
case 'L': case 'L':
{ {
std::int64_t number{}; std::int64_t number{};
return get_number(input_format, number) && emit_signed(number); return get_number(input_format, number) && emit_signed(input_format, number);
} }
case 'u': case 'u':
@@ -2931,7 +2933,7 @@ class binary_reader
break; break;
} }
std::uint16_t number{}; std::uint16_t number{};
return get_number(input_format, number) && emit_unsigned(number); return get_number(input_format, number) && emit_unsigned(input_format, number);
} }
case 'm': case 'm':
@@ -2941,7 +2943,7 @@ class binary_reader
break; break;
} }
std::uint32_t number{}; std::uint32_t number{};
return get_number(input_format, number) && emit_unsigned(number); return get_number(input_format, number) && emit_unsigned(input_format, number);
} }
case 'M': case 'M':
@@ -2951,7 +2953,7 @@ class binary_reader
break; break;
} }
std::uint64_t number{}; std::uint64_t number{};
return get_number(input_format, number) && emit_unsigned(number); return get_number(input_format, number) && emit_unsigned(input_format, number);
} }
case 'h': case 'h':
@@ -3565,9 +3567,9 @@ class binary_reader
{ {
if (number >= 0) if (number >= 0)
{ {
return emit_unsigned(static_cast<std::uint64_t>(number)); return emit_unsigned(input_format_t::bon8, static_cast<std::uint64_t>(number));
} }
return emit_signed(number); return emit_signed(input_format_t::bon8, number);
} }
/*! /*!
@@ -3931,11 +3933,15 @@ class binary_reader
matters for narrower custom number types. matters for narrower custom number types.
@tparam NumberType a signed integer type @tparam NumberType a signed integer type
@param[in] format the current format (for diagnostics)
@param[in] number the integer @param[in] number the integer
@return whether the SAX parser accepted the value @return whether the SAX parser accepted the value
@throw out_of_range.406 if @a number overflows number_float_t (see
@ref emit_float)
*/ */
template<typename NumberType> template<typename NumberType>
bool emit_signed(const NumberType number) bool emit_signed(const input_format_t format, const NumberType number)
{ {
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_integer_t>(number))) if (JSON_HEDLEY_LIKELY(value_in_range_of<number_integer_t>(number)))
{ {
@@ -3945,7 +3951,7 @@ class binary_reader
{ {
return sax->number_unsigned(static_cast<number_unsigned_t>(number)); return sax->number_unsigned(static_cast<number_unsigned_t>(number));
} }
return sax->number_float(static_cast<number_float_t>(number), ""); return emit_float(format, number);
} }
/*! /*!
@@ -3955,17 +3961,21 @@ class binary_reader
number_unsigned_t is passed as number_float_t. number_unsigned_t is passed as number_float_t.
@tparam NumberType an unsigned integer type @tparam NumberType an unsigned integer type
@param[in] format the current format (for diagnostics)
@param[in] number the integer @param[in] number the integer
@return whether the SAX parser accepted the value @return whether the SAX parser accepted the value
@throw out_of_range.406 if @a number overflows number_float_t (see
@ref emit_float)
*/ */
template<typename NumberType> template<typename NumberType>
bool emit_unsigned(const NumberType number) bool emit_unsigned(const input_format_t format, const NumberType number)
{ {
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_unsigned_t>(number))) if (JSON_HEDLEY_LIKELY(value_in_range_of<number_unsigned_t>(number)))
{ {
return sax->number_unsigned(static_cast<number_unsigned_t>(number)); return sax->number_unsigned(static_cast<number_unsigned_t>(number));
} }
return sax->number_float(static_cast<number_float_t>(number), ""); return emit_float(format, number);
} }
/*! /*!
@@ -3973,9 +3983,10 @@ class binary_reader
Like the lexer does for JSON text, a finite value that overflows Like the lexer does for JSON text, a finite value that overflows
number_float_t is rejected instead of silently becoming infinity. Infinity number_float_t is rejected instead of silently becoming infinity. Infinity
and NaN in the input are passed on unchanged. and NaN in the input are passed on unchanged. Integers only overflow if
number_float_t cannot represent 2^64, e.g., a half-precision type.
@tparam NumberType a floating-point type @tparam NumberType a floating-point or integer type
@param[in] format the current format (for diagnostics) @param[in] format the current format (for diagnostics)
@param[in] number the number @param[in] number the number
@return whether the SAX parser accepted the value @return whether the SAX parser accepted the value
+46 -35
View File
@@ -13335,19 +13335,19 @@ class binary_reader
case 0x10: // int32 case 0x10: // int32
{ {
std::int32_t value{}; std::int32_t value{};
return get_number<std::int32_t, true>(input_format_t::bson, value) && emit_signed(value); return get_number<std::int32_t, true>(input_format_t::bson, value) && emit_signed(input_format_t::bson, value);
} }
case 0x12: // int64 case 0x12: // int64
{ {
std::int64_t value{}; std::int64_t value{};
return get_number<std::int64_t, true>(input_format_t::bson, value) && emit_signed(value); return get_number<std::int64_t, true>(input_format_t::bson, value) && emit_signed(input_format_t::bson, value);
} }
case 0x11: // uint64 case 0x11: // uint64
{ {
std::uint64_t value{}; std::uint64_t value{};
return get_number<std::uint64_t, true>(input_format_t::bson, value) && emit_unsigned(value); return get_number<std::uint64_t, true>(input_format_t::bson, value) && emit_unsigned(input_format_t::bson, value);
} }
default: // anything else is not supported (yet) default: // anything else is not supported (yet)
@@ -13382,8 +13382,10 @@ class binary_reader
} }
// like the lexer does for JSON text, store a value too small for // like the lexer does for JSON text, store a value too small for
// number_integer_t as number_float_t // number_integer_t as number_float_t; compute it as long double so
return sax->number_float(static_cast<number_float_t>(-1) - static_cast<number_float_t>(number), ""); // that emit_float sees a finite value and can detect an overflow of
// number_float_t
return emit_float(input_format_t::cbor, static_cast<long double>(-1) - static_cast<long double>(number));
} }
/*! /*!
@@ -13440,25 +13442,25 @@ class binary_reader
case 0x18: // Unsigned integer (one-byte uint8_t follows) case 0x18: // Unsigned integer (one-byte uint8_t follows)
{ {
std::uint8_t number{}; std::uint8_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(number); return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
} }
case 0x19: // Unsigned integer (two-byte uint16_t follows) case 0x19: // Unsigned integer (two-byte uint16_t follows)
{ {
std::uint16_t number{}; std::uint16_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(number); return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
} }
case 0x1A: // Unsigned integer (four-byte uint32_t follows) case 0x1A: // Unsigned integer (four-byte uint32_t follows)
{ {
std::uint32_t number{}; std::uint32_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(number); return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
} }
case 0x1B: // Unsigned integer (eight-byte uint64_t follows) case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
{ {
std::uint64_t number{}; std::uint64_t number{};
return get_number(input_format_t::cbor, number) && emit_unsigned(number); return get_number(input_format_t::cbor, number) && emit_unsigned(input_format_t::cbor, number);
} }
// Negative integer -1-0x00..-1-0x17 (-1..-24) // Negative integer -1-0x00..-1-0x17 (-1..-24)
@@ -14611,49 +14613,49 @@ class binary_reader
case 0xCC: // uint 8 case 0xCC: // uint 8
{ {
std::uint8_t number{}; std::uint8_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(number); return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
} }
case 0xCD: // uint 16 case 0xCD: // uint 16
{ {
std::uint16_t number{}; std::uint16_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(number); return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
} }
case 0xCE: // uint 32 case 0xCE: // uint 32
{ {
std::uint32_t number{}; std::uint32_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(number); return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
} }
case 0xCF: // uint 64 case 0xCF: // uint 64
{ {
std::uint64_t number{}; std::uint64_t number{};
return get_number(input_format_t::msgpack, number) && emit_unsigned(number); return get_number(input_format_t::msgpack, number) && emit_unsigned(input_format_t::msgpack, number);
} }
case 0xD0: // int 8 case 0xD0: // int 8
{ {
std::int8_t number{}; std::int8_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(number); return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
} }
case 0xD1: // int 16 case 0xD1: // int 16
{ {
std::int16_t number{}; std::int16_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(number); return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
} }
case 0xD2: // int 32 case 0xD2: // int 32
{ {
std::int32_t number{}; std::int32_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(number); return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
} }
case 0xD3: // int 64 case 0xD3: // int 64
{ {
std::int64_t number{}; std::int64_t number{};
return get_number(input_format_t::msgpack, number) && emit_signed(number); return get_number(input_format_t::msgpack, number) && emit_signed(input_format_t::msgpack, number);
} }
case 0xDC: // array 16 case 0xDC: // array 16
@@ -15494,7 +15496,7 @@ class binary_reader
{ {
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr)); return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
} }
if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(i))) if (JSON_HEDLEY_UNLIKELY(!emit_unsigned(input_format, i)))
{ {
return false; return false;
} }
@@ -15626,37 +15628,37 @@ class binary_reader
break; break;
} }
std::uint8_t number{}; std::uint8_t number{};
return get_number(input_format, number) && emit_unsigned(number); return get_number(input_format, number) && emit_unsigned(input_format, number);
} }
case 'U': case 'U':
{ {
std::uint8_t number{}; std::uint8_t number{};
return get_number(input_format, number) && emit_unsigned(number); return get_number(input_format, number) && emit_unsigned(input_format, number);
} }
case 'i': case 'i':
{ {
std::int8_t number{}; std::int8_t number{};
return get_number(input_format, number) && emit_signed(number); return get_number(input_format, number) && emit_signed(input_format, number);
} }
case 'I': case 'I':
{ {
std::int16_t number{}; std::int16_t number{};
return get_number(input_format, number) && emit_signed(number); return get_number(input_format, number) && emit_signed(input_format, number);
} }
case 'l': case 'l':
{ {
std::int32_t number{}; std::int32_t number{};
return get_number(input_format, number) && emit_signed(number); return get_number(input_format, number) && emit_signed(input_format, number);
} }
case 'L': case 'L':
{ {
std::int64_t number{}; std::int64_t number{};
return get_number(input_format, number) && emit_signed(number); return get_number(input_format, number) && emit_signed(input_format, number);
} }
case 'u': case 'u':
@@ -15666,7 +15668,7 @@ class binary_reader
break; break;
} }
std::uint16_t number{}; std::uint16_t number{};
return get_number(input_format, number) && emit_unsigned(number); return get_number(input_format, number) && emit_unsigned(input_format, number);
} }
case 'm': case 'm':
@@ -15676,7 +15678,7 @@ class binary_reader
break; break;
} }
std::uint32_t number{}; std::uint32_t number{};
return get_number(input_format, number) && emit_unsigned(number); return get_number(input_format, number) && emit_unsigned(input_format, number);
} }
case 'M': case 'M':
@@ -15686,7 +15688,7 @@ class binary_reader
break; break;
} }
std::uint64_t number{}; std::uint64_t number{};
return get_number(input_format, number) && emit_unsigned(number); return get_number(input_format, number) && emit_unsigned(input_format, number);
} }
case 'h': case 'h':
@@ -16300,9 +16302,9 @@ class binary_reader
{ {
if (number >= 0) if (number >= 0)
{ {
return emit_unsigned(static_cast<std::uint64_t>(number)); return emit_unsigned(input_format_t::bon8, static_cast<std::uint64_t>(number));
} }
return emit_signed(number); return emit_signed(input_format_t::bon8, number);
} }
/*! /*!
@@ -16666,11 +16668,15 @@ class binary_reader
matters for narrower custom number types. matters for narrower custom number types.
@tparam NumberType a signed integer type @tparam NumberType a signed integer type
@param[in] format the current format (for diagnostics)
@param[in] number the integer @param[in] number the integer
@return whether the SAX parser accepted the value @return whether the SAX parser accepted the value
@throw out_of_range.406 if @a number overflows number_float_t (see
@ref emit_float)
*/ */
template<typename NumberType> template<typename NumberType>
bool emit_signed(const NumberType number) bool emit_signed(const input_format_t format, const NumberType number)
{ {
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_integer_t>(number))) if (JSON_HEDLEY_LIKELY(value_in_range_of<number_integer_t>(number)))
{ {
@@ -16680,7 +16686,7 @@ class binary_reader
{ {
return sax->number_unsigned(static_cast<number_unsigned_t>(number)); return sax->number_unsigned(static_cast<number_unsigned_t>(number));
} }
return sax->number_float(static_cast<number_float_t>(number), ""); return emit_float(format, number);
} }
/*! /*!
@@ -16690,17 +16696,21 @@ class binary_reader
number_unsigned_t is passed as number_float_t. number_unsigned_t is passed as number_float_t.
@tparam NumberType an unsigned integer type @tparam NumberType an unsigned integer type
@param[in] format the current format (for diagnostics)
@param[in] number the integer @param[in] number the integer
@return whether the SAX parser accepted the value @return whether the SAX parser accepted the value
@throw out_of_range.406 if @a number overflows number_float_t (see
@ref emit_float)
*/ */
template<typename NumberType> template<typename NumberType>
bool emit_unsigned(const NumberType number) bool emit_unsigned(const input_format_t format, const NumberType number)
{ {
if (JSON_HEDLEY_LIKELY(value_in_range_of<number_unsigned_t>(number))) if (JSON_HEDLEY_LIKELY(value_in_range_of<number_unsigned_t>(number)))
{ {
return sax->number_unsigned(static_cast<number_unsigned_t>(number)); return sax->number_unsigned(static_cast<number_unsigned_t>(number));
} }
return sax->number_float(static_cast<number_float_t>(number), ""); return emit_float(format, number);
} }
/*! /*!
@@ -16708,9 +16718,10 @@ class binary_reader
Like the lexer does for JSON text, a finite value that overflows Like the lexer does for JSON text, a finite value that overflows
number_float_t is rejected instead of silently becoming infinity. Infinity number_float_t is rejected instead of silently becoming infinity. Infinity
and NaN in the input are passed on unchanged. and NaN in the input are passed on unchanged. Integers only overflow if
number_float_t cannot represent 2^64, e.g., a half-precision type.
@tparam NumberType a floating-point type @tparam NumberType a floating-point or integer type
@param[in] format the current format (for diagnostics) @param[in] format the current format (for diagnostics)
@param[in] number the number @param[in] number the number
@return whether the SAX parser accepted the value @return whether the SAX parser accepted the value