Skip to content

Commit 482de87

Browse files
committed
fix(cbor): extend overflow checks for other types
Extend negative integer overflow detection to all CBOR negative integer cases (0x38, 0x39, 0x3A) for consistency with the existing 0x3B check. Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
1 parent e1a4bd5 commit 482de87

File tree

3 files changed

+63
-60
lines changed

3 files changed

+63
-60
lines changed

include/nlohmann/detail/input/binary_reader.hpp

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,25 @@ class binary_reader
425425
426426
@return whether a valid CBOR value was passed to the SAX parser
427427
*/
428+
429+
template<typename NumberType>
430+
bool get_cbor_negative_integer()
431+
{
432+
NumberType number{};
433+
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, number)))
434+
{
435+
return false;
436+
}
437+
const auto max_val = static_cast<NumberType>((std::numeric_limits<number_integer_t>::max)());
438+
if (number > max_val)
439+
{
440+
return sax->parse_error(chars_read, get_token_string(),
441+
parse_error::create(112, chars_read,
442+
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
443+
}
444+
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
445+
}
446+
428447
bool parse_cbor_internal(const bool get_char,
429448
const cbor_tag_handler_t tag_handler)
430449
{
@@ -513,39 +532,16 @@ class binary_reader
513532
return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));
514533

515534
case 0x38: // Negative integer (one-byte uint8_t follows)
516-
{
517-
std::uint8_t number{};
518-
return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
519-
}
535+
return get_cbor_negative_integer<std::uint8_t>();
520536

521537
case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
522-
{
523-
std::uint16_t number{};
524-
return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
525-
}
538+
return get_cbor_negative_integer<std::uint16_t>();
526539

527540
case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
528-
{
529-
std::uint32_t number{};
530-
return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
531-
}
541+
return get_cbor_negative_integer<std::uint32_t>();
532542

533543
case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
534-
{
535-
std::uint64_t number{};
536-
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, number)))
537-
{
538-
return false;
539-
}
540-
const auto max_val = static_cast<std::uint64_t>((std::numeric_limits<number_integer_t>::max)());
541-
if (number > max_val)
542-
{
543-
return sax->parse_error(chars_read, get_token_string(),
544-
parse_error::create(112, chars_read,
545-
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
546-
}
547-
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
548-
}
544+
return get_cbor_negative_integer<std::uint64_t>();
549545

550546
// Binary data (0x00..0x17 bytes follow)
551547
case 0x40:

single_include/nlohmann/json.hpp

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10263,6 +10263,25 @@ class binary_reader
1026310263

1026410264
@return whether a valid CBOR value was passed to the SAX parser
1026510265
*/
10266+
10267+
template<typename NumberType>
10268+
bool get_cbor_negative_integer()
10269+
{
10270+
NumberType number{};
10271+
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, number)))
10272+
{
10273+
return false;
10274+
}
10275+
const auto max_val = static_cast<NumberType>((std::numeric_limits<number_integer_t>::max)());
10276+
if (number > max_val)
10277+
{
10278+
return sax->parse_error(chars_read, get_token_string(),
10279+
parse_error::create(112, chars_read,
10280+
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
10281+
}
10282+
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
10283+
}
10284+
1026610285
bool parse_cbor_internal(const bool get_char,
1026710286
const cbor_tag_handler_t tag_handler)
1026810287
{
@@ -10351,39 +10370,16 @@ class binary_reader
1035110370
return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));
1035210371

1035310372
case 0x38: // Negative integer (one-byte uint8_t follows)
10354-
{
10355-
std::uint8_t number{};
10356-
return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
10357-
}
10373+
return get_cbor_negative_integer<std::uint8_t>();
1035810374

1035910375
case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
10360-
{
10361-
std::uint16_t number{};
10362-
return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
10363-
}
10376+
return get_cbor_negative_integer<std::uint16_t>();
1036410377

1036510378
case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
10366-
{
10367-
std::uint32_t number{};
10368-
return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
10369-
}
10379+
return get_cbor_negative_integer<std::uint32_t>();
1037010380

1037110381
case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
10372-
{
10373-
std::uint64_t number{};
10374-
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, number)))
10375-
{
10376-
return false;
10377-
}
10378-
const auto max_val = static_cast<std::uint64_t>((std::numeric_limits<number_integer_t>::max)());
10379-
if (number > max_val)
10380-
{
10381-
return sax->parse_error(chars_read, get_token_string(),
10382-
parse_error::create(112, chars_read,
10383-
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
10384-
}
10385-
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
10386-
}
10382+
return get_cbor_negative_integer<std::uint64_t>();
1038710383

1038810384
// Binary data (0x00..0x17 bytes follow)
1038910385
case 0x40:

tests/src/unit-cbor.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,13 @@ TEST_CASE("CBOR")
17051705
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 7: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
17061706
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
17071707
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 9: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
1708+
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x38})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
1709+
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x39})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
1710+
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x39, 0x00})), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
1711+
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x3a})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
1712+
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x3a, 0x00})), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
1713+
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x3a, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
1714+
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x3a, 0x00, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
17081715
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x3b})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
17091716
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x3b, 0x00})), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
17101717
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), "[json.exception.parse_error.110] parse error at byte 9: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
@@ -1736,6 +1743,13 @@ TEST_CASE("CBOR")
17361743
CHECK(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00}), true, false).is_discarded());
17371744
CHECK(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), true, false).is_discarded());
17381745
CHECK(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), true, false).is_discarded());
1746+
CHECK(json::from_cbor(std::vector<uint8_t>({0x38}), true, false).is_discarded());
1747+
CHECK(json::from_cbor(std::vector<uint8_t>({0x39}), true, false).is_discarded());
1748+
CHECK(json::from_cbor(std::vector<uint8_t>({0x39, 0x00}), true, false).is_discarded());
1749+
CHECK(json::from_cbor(std::vector<uint8_t>({0x3a}), true, false).is_discarded());
1750+
CHECK(json::from_cbor(std::vector<uint8_t>({0x3a, 0x00}), true, false).is_discarded());
1751+
CHECK(json::from_cbor(std::vector<uint8_t>({0x3a, 0x00, 0x00}), true, false).is_discarded());
1752+
CHECK(json::from_cbor(std::vector<uint8_t>({0x3a, 0x00, 0x00, 0x00}), true, false).is_discarded());
17391753
CHECK(json::from_cbor(std::vector<uint8_t>({0x3b}), true, false).is_discarded());
17401754
CHECK(json::from_cbor(std::vector<uint8_t>({0x3b, 0x00}), true, false).is_discarded());
17411755
CHECK(json::from_cbor(std::vector<uint8_t>({0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), true, false).is_discarded());
@@ -2689,13 +2703,10 @@ TEST_CASE("Tagged values")
26892703

26902704
SECTION("negative integer overflow")
26912705
{
2692-
// CBOR type 0x3B encodes negative integers as: result = -1 - n
2693-
// where n is an 8-byte uint64_t. Valid range for n is [0, INT64_MAX]
2694-
// which produces results in [INT64_MIN, -1].
2706+
// CBOR encodes negative integers as: result = -1 - n
2707+
// For type 0x3B, n is an 8-byte uint64_t. Valid range for n with
2708+
// the default int64_t is [0, INT64_MAX], producing results in [INT64_MIN, -1].
26952709
// When n > INT64_MAX, the result exceeds int64_t range and is rejected.
2696-
//
2697-
// Note: result = 0 is not possible with type 0x3B since n is an
2698-
// unsigned integer, so the formula -1 - n always produces a negative result.
26992710

27002711
SECTION("n = 0 is valid (result = -1)")
27012712
{

0 commit comments

Comments
 (0)