Skip to content

Commit

Permalink
🐛 fix bug in CBOR tag handling
Browse files Browse the repository at this point in the history
  • Loading branch information
nlohmann committed Jul 24, 2020
1 parent 43e07bb commit 480ad52
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 44 deletions.
48 changes: 26 additions & 22 deletions include/nlohmann/detail/input/binary_reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,14 +393,14 @@ class binary_reader

/*!
@param[in] get_char whether a new character should be retrieved from the
input (true, default) or whether the last read
character should be considered instead
input (true) or whether the last read character should
be considered instead (false)
@param[in] tag_handler how CBOR tags should be treated
@return whether a valid CBOR value was passed to the SAX parser
*/
bool parse_cbor_internal(const bool get_char = true,
cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
bool parse_cbor_internal(const bool get_char,
const cbor_tag_handler_t tag_handler)
{
switch (get_char ? get() : current)
{
Expand Down Expand Up @@ -606,34 +606,34 @@ class binary_reader
case 0x95:
case 0x96:
case 0x97:
return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);

case 0x98: // array (one-byte uint8_t for n follows)
{
std::uint8_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
}

case 0x99: // array (two-byte uint16_t for n follow)
{
std::uint16_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
}

case 0x9A: // array (four-byte uint32_t for n follow)
{
std::uint32_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
}

case 0x9B: // array (eight-byte uint64_t for n follow)
{
std::uint64_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
}

case 0x9F: // array (indefinite length)
return get_cbor_array(std::size_t(-1));
return get_cbor_array(std::size_t(-1), tag_handler);

// map (0x00..0x17 pairs of data items follow)
case 0xA0:
Expand All @@ -660,34 +660,34 @@ class binary_reader
case 0xB5:
case 0xB6:
case 0xB7:
return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);

case 0xB8: // map (one-byte uint8_t for n follows)
{
std::uint8_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
}

case 0xB9: // map (two-byte uint16_t for n follow)
{
std::uint16_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
}

case 0xBA: // map (four-byte uint32_t for n follow)
{
std::uint32_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
}

case 0xBB: // map (eight-byte uint64_t for n follow)
{
std::uint64_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
}

case 0xBF: // map (indefinite length)
return get_cbor_object(std::size_t(-1));
return get_cbor_object(std::size_t(-1), tag_handler);

case 0xC6: // tagged item
case 0xC7:
Expand Down Expand Up @@ -1030,9 +1030,11 @@ class binary_reader
/*!
@param[in] len the length of the array or std::size_t(-1) for an
array of indefinite size
@param[in] tag_handler how CBOR tags should be treated
@return whether array creation completed
*/
bool get_cbor_array(const std::size_t len)
bool get_cbor_array(const std::size_t len,
const cbor_tag_handler_t tag_handler)
{
if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
{
Expand All @@ -1043,7 +1045,7 @@ class binary_reader
{
for (std::size_t i = 0; i < len; ++i)
{
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal()))
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{
return false;
}
Expand All @@ -1053,7 +1055,7 @@ class binary_reader
{
while (get() != 0xFF)
{
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false)))
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))
{
return false;
}
Expand All @@ -1066,9 +1068,11 @@ class binary_reader
/*!
@param[in] len the length of the object or std::size_t(-1) for an
object of indefinite size
@param[in] tag_handler how CBOR tags should be treated
@return whether object creation completed
*/
bool get_cbor_object(const std::size_t len)
bool get_cbor_object(const std::size_t len,
const cbor_tag_handler_t tag_handler)
{
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
{
Expand All @@ -1086,7 +1090,7 @@ class binary_reader
return false;
}

if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal()))
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{
return false;
}
Expand All @@ -1102,7 +1106,7 @@ class binary_reader
return false;
}

if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal()))
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{
return false;
}
Expand Down
48 changes: 26 additions & 22 deletions single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6402,14 +6402,14 @@ class binary_reader

/*!
@param[in] get_char whether a new character should be retrieved from the
input (true, default) or whether the last read
character should be considered instead
input (true) or whether the last read character should
be considered instead (false)
@param[in] tag_handler how CBOR tags should be treated

@return whether a valid CBOR value was passed to the SAX parser
*/
bool parse_cbor_internal(const bool get_char = true,
cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
bool parse_cbor_internal(const bool get_char,
const cbor_tag_handler_t tag_handler)
{
switch (get_char ? get() : current)
{
Expand Down Expand Up @@ -6615,34 +6615,34 @@ class binary_reader
case 0x95:
case 0x96:
case 0x97:
return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
return get_cbor_array(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);

case 0x98: // array (one-byte uint8_t for n follows)
{
std::uint8_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
}

case 0x99: // array (two-byte uint16_t for n follow)
{
std::uint16_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
}

case 0x9A: // array (four-byte uint32_t for n follow)
{
std::uint32_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
}

case 0x9B: // array (eight-byte uint64_t for n follow)
{
std::uint64_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
}

case 0x9F: // array (indefinite length)
return get_cbor_array(std::size_t(-1));
return get_cbor_array(std::size_t(-1), tag_handler);

// map (0x00..0x17 pairs of data items follow)
case 0xA0:
Expand All @@ -6669,34 +6669,34 @@ class binary_reader
case 0xB5:
case 0xB6:
case 0xB7:
return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu));
return get_cbor_object(static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);

case 0xB8: // map (one-byte uint8_t for n follows)
{
std::uint8_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
}

case 0xB9: // map (two-byte uint16_t for n follow)
{
std::uint16_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
}

case 0xBA: // map (four-byte uint32_t for n follow)
{
std::uint32_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
}

case 0xBB: // map (eight-byte uint64_t for n follow)
{
std::uint64_t len{};
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len));
return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
}

case 0xBF: // map (indefinite length)
return get_cbor_object(std::size_t(-1));
return get_cbor_object(std::size_t(-1), tag_handler);

case 0xC6: // tagged item
case 0xC7:
Expand Down Expand Up @@ -7039,9 +7039,11 @@ class binary_reader
/*!
@param[in] len the length of the array or std::size_t(-1) for an
array of indefinite size
@param[in] tag_handler how CBOR tags should be treated
@return whether array creation completed
*/
bool get_cbor_array(const std::size_t len)
bool get_cbor_array(const std::size_t len,
const cbor_tag_handler_t tag_handler)
{
if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
{
Expand All @@ -7052,7 +7054,7 @@ class binary_reader
{
for (std::size_t i = 0; i < len; ++i)
{
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal()))
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{
return false;
}
Expand All @@ -7062,7 +7064,7 @@ class binary_reader
{
while (get() != 0xFF)
{
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false)))
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))
{
return false;
}
Expand All @@ -7075,9 +7077,11 @@ class binary_reader
/*!
@param[in] len the length of the object or std::size_t(-1) for an
object of indefinite size
@param[in] tag_handler how CBOR tags should be treated
@return whether object creation completed
*/
bool get_cbor_object(const std::size_t len)
bool get_cbor_object(const std::size_t len,
const cbor_tag_handler_t tag_handler)
{
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
{
Expand All @@ -7095,7 +7099,7 @@ class binary_reader
return false;
}

if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal()))
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{
return false;
}
Expand All @@ -7111,7 +7115,7 @@ class binary_reader
return false;
}

if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal()))
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{
return false;
}
Expand Down
21 changes: 21 additions & 0 deletions test/src/unit-cbor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2722,4 +2722,25 @@ TEST_CASE("Tagged values")
CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error);
}
}

SECTION("tagged binary")
{
// create a binary value of subtype 42
json j;
j["binary"] = json::binary({0xCA, 0xFE, 0xBA, 0xBE}, 42);

// convert to CBOR
const auto v = json::to_cbor(j);
CHECK(v == std::vector<std::uint8_t> {0xA1, 0x66, 0x62, 0x69, 0x6E, 0x61, 0x72, 0x79, 0xD8, 0x2A, 0x44, 0xCA, 0xFE, 0xBA, 0xBE});

// parse error when parsing tagged value
CHECK_THROWS_AS(json::from_cbor(v), json::parse_error);
CHECK_THROWS_WITH(json::from_cbor(v), "[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: invalid byte: 0xD8");

// binary without subtype when tags are ignored
json jb = json::from_cbor(v, true, true, json::cbor_tag_handler_t::ignore);
CHECK(jb.is_object());
CHECK(jb["binary"].is_binary());
CHECK(!jb["binary"].get_binary().has_subtype());
}
}

0 comments on commit 480ad52

Please sign in to comment.