Merge pull request #2308 from nlohmann/cbor_tags

Fix bug in CBOR tag handling
This commit is contained in:
Niels Lohmann 2020-07-25 19:43:11 +02:00 committed by GitHub
commit 40b78d3847
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 44 deletions

View file

@ -393,14 +393,14 @@ class binary_reader
/*! /*!
@param[in] get_char whether a new character should be retrieved from the @param[in] get_char whether a new character should be retrieved from the
input (true, default) or whether the last read input (true) or whether the last read character should
character should be considered instead be considered instead (false)
@param[in] tag_handler how CBOR tags should be treated @param[in] tag_handler how CBOR tags should be treated
@return whether a valid CBOR value was passed to the SAX parser @return whether a valid CBOR value was passed to the SAX parser
*/ */
bool parse_cbor_internal(const bool get_char = true, bool parse_cbor_internal(const bool get_char,
cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) const cbor_tag_handler_t tag_handler)
{ {
switch (get_char ? get() : current) switch (get_char ? get() : current)
{ {
@ -606,34 +606,34 @@ class binary_reader
case 0x95: case 0x95:
case 0x96: case 0x96:
case 0x97: 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) case 0x98: // array (one-byte uint8_t for n follows)
{ {
std::uint8_t len{}; 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) case 0x99: // array (two-byte uint16_t for n follow)
{ {
std::uint16_t len{}; 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) case 0x9A: // array (four-byte uint32_t for n follow)
{ {
std::uint32_t len{}; 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) case 0x9B: // array (eight-byte uint64_t for n follow)
{ {
std::uint64_t len{}; 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) 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) // map (0x00..0x17 pairs of data items follow)
case 0xA0: case 0xA0:
@ -660,34 +660,34 @@ class binary_reader
case 0xB5: case 0xB5:
case 0xB6: case 0xB6:
case 0xB7: 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) case 0xB8: // map (one-byte uint8_t for n follows)
{ {
std::uint8_t len{}; 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) case 0xB9: // map (two-byte uint16_t for n follow)
{ {
std::uint16_t len{}; 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) case 0xBA: // map (four-byte uint32_t for n follow)
{ {
std::uint32_t len{}; 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) case 0xBB: // map (eight-byte uint64_t for n follow)
{ {
std::uint64_t len{}; 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) 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 0xC6: // tagged item
case 0xC7: case 0xC7:
@ -1030,9 +1030,11 @@ class binary_reader
/*! /*!
@param[in] len the length of the array or std::size_t(-1) for an @param[in] len the length of the array or std::size_t(-1) for an
array of indefinite size array of indefinite size
@param[in] tag_handler how CBOR tags should be treated
@return whether array creation completed @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))) if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
{ {
@ -1043,7 +1045,7 @@ class binary_reader
{ {
for (std::size_t i = 0; i < len; ++i) 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; return false;
} }
@ -1053,7 +1055,7 @@ class binary_reader
{ {
while (get() != 0xFF) while (get() != 0xFF)
{ {
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false))) if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))
{ {
return false; return false;
} }
@ -1066,9 +1068,11 @@ class binary_reader
/*! /*!
@param[in] len the length of the object or std::size_t(-1) for an @param[in] len the length of the object or std::size_t(-1) for an
object of indefinite size object of indefinite size
@param[in] tag_handler how CBOR tags should be treated
@return whether object creation completed @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))) if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
{ {
@ -1086,7 +1090,7 @@ class binary_reader
return false; return false;
} }
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal())) if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{ {
return false; return false;
} }
@ -1102,7 +1106,7 @@ class binary_reader
return false; return false;
} }
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal())) if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{ {
return false; return false;
} }

View file

@ -6402,14 +6402,14 @@ class binary_reader
/*! /*!
@param[in] get_char whether a new character should be retrieved from the @param[in] get_char whether a new character should be retrieved from the
input (true, default) or whether the last read input (true) or whether the last read character should
character should be considered instead be considered instead (false)
@param[in] tag_handler how CBOR tags should be treated @param[in] tag_handler how CBOR tags should be treated
@return whether a valid CBOR value was passed to the SAX parser @return whether a valid CBOR value was passed to the SAX parser
*/ */
bool parse_cbor_internal(const bool get_char = true, bool parse_cbor_internal(const bool get_char,
cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) const cbor_tag_handler_t tag_handler)
{ {
switch (get_char ? get() : current) switch (get_char ? get() : current)
{ {
@ -6615,34 +6615,34 @@ class binary_reader
case 0x95: case 0x95:
case 0x96: case 0x96:
case 0x97: 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) case 0x98: // array (one-byte uint8_t for n follows)
{ {
std::uint8_t len{}; 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) case 0x99: // array (two-byte uint16_t for n follow)
{ {
std::uint16_t len{}; 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) case 0x9A: // array (four-byte uint32_t for n follow)
{ {
std::uint32_t len{}; 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) case 0x9B: // array (eight-byte uint64_t for n follow)
{ {
std::uint64_t len{}; 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) 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) // map (0x00..0x17 pairs of data items follow)
case 0xA0: case 0xA0:
@ -6669,34 +6669,34 @@ class binary_reader
case 0xB5: case 0xB5:
case 0xB6: case 0xB6:
case 0xB7: 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) case 0xB8: // map (one-byte uint8_t for n follows)
{ {
std::uint8_t len{}; 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) case 0xB9: // map (two-byte uint16_t for n follow)
{ {
std::uint16_t len{}; 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) case 0xBA: // map (four-byte uint32_t for n follow)
{ {
std::uint32_t len{}; 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) case 0xBB: // map (eight-byte uint64_t for n follow)
{ {
std::uint64_t len{}; 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) 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 0xC6: // tagged item
case 0xC7: case 0xC7:
@ -7039,9 +7039,11 @@ class binary_reader
/*! /*!
@param[in] len the length of the array or std::size_t(-1) for an @param[in] len the length of the array or std::size_t(-1) for an
array of indefinite size array of indefinite size
@param[in] tag_handler how CBOR tags should be treated
@return whether array creation completed @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))) if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
{ {
@ -7052,7 +7054,7 @@ class binary_reader
{ {
for (std::size_t i = 0; i < len; ++i) 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; return false;
} }
@ -7062,7 +7064,7 @@ class binary_reader
{ {
while (get() != 0xFF) while (get() != 0xFF)
{ {
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false))) if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))
{ {
return false; return false;
} }
@ -7075,9 +7077,11 @@ class binary_reader
/*! /*!
@param[in] len the length of the object or std::size_t(-1) for an @param[in] len the length of the object or std::size_t(-1) for an
object of indefinite size object of indefinite size
@param[in] tag_handler how CBOR tags should be treated
@return whether object creation completed @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))) if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
{ {
@ -7095,7 +7099,7 @@ class binary_reader
return false; return false;
} }
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal())) if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{ {
return false; return false;
} }
@ -7111,7 +7115,7 @@ class binary_reader
return false; return false;
} }
if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal())) if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
{ {
return false; return false;
} }

View file

@ -2705,4 +2705,25 @@ TEST_CASE("Tagged values")
CHECK_THROWS_AS(json::from_cbor(v_tagged, true, true, json::cbor_tag_handler_t::ignore), json::parse_error); 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());
}
} }