Skip to content

Commit fd60a2a

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 fd60a2a

File tree

3 files changed

+89
-12
lines changed

3 files changed

+89
-12
lines changed

include/nlohmann/detail/input/binary_reader.hpp

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -515,19 +515,52 @@ class binary_reader
515515
case 0x38: // Negative integer (one-byte uint8_t follows)
516516
{
517517
std::uint8_t number{};
518-
return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
518+
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, number)))
519+
{
520+
return false;
521+
}
522+
const auto max_val = static_cast<std::uint8_t>((std::numeric_limits<number_integer_t>::max)());
523+
if (number > max_val)
524+
{
525+
return sax->parse_error(chars_read, get_token_string(),
526+
parse_error::create(112, chars_read,
527+
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
528+
}
529+
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
519530
}
520531

521532
case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
522533
{
523534
std::uint16_t number{};
524-
return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
535+
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, number)))
536+
{
537+
return false;
538+
}
539+
const auto max_val = static_cast<std::uint16_t>((std::numeric_limits<number_integer_t>::max)());
540+
if (number > max_val)
541+
{
542+
return sax->parse_error(chars_read, get_token_string(),
543+
parse_error::create(112, chars_read,
544+
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
545+
}
546+
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
525547
}
526548

527549
case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
528550
{
529551
std::uint32_t number{};
530-
return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
552+
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, number)))
553+
{
554+
return false;
555+
}
556+
const auto max_val = static_cast<std::uint32_t>((std::numeric_limits<number_integer_t>::max)());
557+
if (number > max_val)
558+
{
559+
return sax->parse_error(chars_read, get_token_string(),
560+
parse_error::create(112, chars_read,
561+
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
562+
}
563+
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
531564
}
532565

533566
case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)

single_include/nlohmann/json.hpp

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10353,19 +10353,52 @@ class binary_reader
1035310353
case 0x38: // Negative integer (one-byte uint8_t follows)
1035410354
{
1035510355
std::uint8_t number{};
10356-
return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
10356+
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, number)))
10357+
{
10358+
return false;
10359+
}
10360+
const auto max_val = static_cast<std::uint8_t>((std::numeric_limits<number_integer_t>::max)());
10361+
if (number > max_val)
10362+
{
10363+
return sax->parse_error(chars_read, get_token_string(),
10364+
parse_error::create(112, chars_read,
10365+
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
10366+
}
10367+
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
1035710368
}
1035810369

1035910370
case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
1036010371
{
1036110372
std::uint16_t number{};
10362-
return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
10373+
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, number)))
10374+
{
10375+
return false;
10376+
}
10377+
const auto max_val = static_cast<std::uint16_t>((std::numeric_limits<number_integer_t>::max)());
10378+
if (number > max_val)
10379+
{
10380+
return sax->parse_error(chars_read, get_token_string(),
10381+
parse_error::create(112, chars_read,
10382+
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
10383+
}
10384+
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
1036310385
}
1036410386

1036510387
case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
1036610388
{
1036710389
std::uint32_t number{};
10368-
return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
10390+
if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::cbor, number)))
10391+
{
10392+
return false;
10393+
}
10394+
const auto max_val = static_cast<std::uint32_t>((std::numeric_limits<number_integer_t>::max)());
10395+
if (number > max_val)
10396+
{
10397+
return sax->parse_error(chars_read, get_token_string(),
10398+
parse_error::create(112, chars_read,
10399+
exception_message(input_format_t::cbor, "negative integer overflow", "value"), nullptr));
10400+
}
10401+
return sax->number_integer(static_cast<number_integer_t>(-1) - static_cast<number_integer_t>(number));
1036910402
}
1037010403

1037110404
case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)

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)