Important spiffy decode fix to correctly handle some bad input cases (#150)
When fetching map items by label or using QCBORDecode_VGetNextConsume some bad input would result in an infinite loop. The bad input includes labels that are not strings or integers, non-well-formed maps and arrays and invalid tag content for tags like string and epoch dates. This can result in a denial-of-service attack, but not an overrun security issue.
These errors are now considered unrecoverable:
QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED
QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED
QCBOR_ERR_MAP_LABEL_TYPE
QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT
The error codes have been renumbered.
Spiffy decode traversal of maps and arrays with these errors will now correctly and directly fail. Without the fix they might fail incorrectly or get stuck in an infinite loop.
Tests to cover these cases are added.
Thanks to Jan Brandstetter and a fuzzing project for finding this.
* Fix error classification to make Consume work correctly
* Bring tests cases up to date with new behavior
* Documentation fixes
* error code renumbering
* Minor clean up
* fix one more test case
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index 6c95c4b..031edfc 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -262,19 +262,20 @@
/**
- Error codes returned by QCBOR Encoder and Decoder.
- Encode errors are 1..8
- Decode errors are 9..43
- Not well-formed errors are 9..16
- Unrecoverable decode errors are 15..24
- (partial overlap with not well-formed errors)
- Other decode errors are 25..43
-
- The errors are order and grouped intentionally to keep the code size
- of QCBORDecode_IsNotWellFormedError() and
- QCBORDecode_IsUnrecoverableError() minimal. Error renumbering may
- occur in the future when new error codes are added for new QCBOR
- features.
+ * Error codes returned by QCBOR Encoder and Decoder.
+ *
+ * The errors are grouped to keep the code size of
+ * QCBORDecode_IsNotWellFormedError() and
+ * QCBORDecode_IsUnrecoverableError() minimal.
+ *
+ * 1..19: Encode errors
+ * 20..: Decode errors
+ * 20-39: QCBORDecode_IsNotWellFormedError()
+ * 30..59: QCBORDecode_IsUnrecoverableError()
+ * 60..: Other decode errors
+ *
+ * Error renumbering may occur in the future when new error codes are
+ * added for new QCBOR features.
*/
typedef enum {
/** The encode or decode completely correctly. */
@@ -318,41 +319,49 @@
byte strings that are not closed. */
QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN = 8,
-#define QCBOR_START_OF_NOT_WELL_FORMED_ERRORS 9
+ /** During encode, opening a byte string while a byte string is open
+ is not allowed. . */
+ QCBOR_ERR_OPEN_BYTE_STRING = 9,
+
+ /** Trying to cancel a byte string wrapping after items have been
+ added to it. */
+ QCBOR_ERR_CANNOT_CANCEL = 10,
+
+#define QCBOR_START_OF_NOT_WELL_FORMED_ERRORS 20
/** During decoding, the CBOR is not well-formed because a simple
value between 0 and 31 is encoded in a two-byte integer rather
than one. */
- QCBOR_ERR_BAD_TYPE_7 = 9,
+ QCBOR_ERR_BAD_TYPE_7 = 20,
/** During decoding, returned by QCBORDecode_Finish() if all the
inputs bytes have not been consumed. This is considered not
well-formed. */
- QCBOR_ERR_EXTRA_BYTES = 10,
+ QCBOR_ERR_EXTRA_BYTES = 21,
/** During decoding, some CBOR construct was encountered that this
decoder doesn't support, primarily this is the reserved
additional info values, 28 through 30. The CBOR is not
well-formed.*/
- QCBOR_ERR_UNSUPPORTED = 11,
+ QCBOR_ERR_UNSUPPORTED = 22,
/** During decoding, the an array or map was not fully consumed.
Returned by QCBORDecode_Finish(). The CBOR is not
well-formed. */
- QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED = 12,
+ QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED = 23,
/** During decoding, an integer type is encoded with a bad length
(that of an indefinite length string). The CBOR is not-well
formed. */
- QCBOR_ERR_BAD_INT = 13,
+ QCBOR_ERR_BAD_INT = 24,
-#define QCBOR_START_OF_UNRECOVERABLE_DECODE_ERRORS 14
+#define QCBOR_START_OF_UNRECOVERABLE_DECODE_ERRORS 30
/** During decoding, one of the chunks in an indefinite-length
string is not of the type of the start of the string. The CBOR
is not well-formed. This error makes no further decoding
possible. */
- QCBOR_ERR_INDEFINITE_STRING_CHUNK = 14,
+ QCBOR_ERR_INDEFINITE_STRING_CHUNK = 30,
/** During decoding, hit the end of the given data to decode. For
example, a byte string of 100 bytes was expected, but the end
@@ -360,19 +369,19 @@
CBOR input will often result in this error. See also @ref
QCBOR_ERR_NO_MORE_ITEMS. The CBOR is not well-formed. This
error makes no further decoding possible. */
- QCBOR_ERR_HIT_END = 15,
+ QCBOR_ERR_HIT_END = 31,
/** During decoding, a break occurred outside an indefinite-length
item. The CBOR is not well-formed. This error makes no further
decoding possible. */
- QCBOR_ERR_BAD_BREAK = 16,
+ QCBOR_ERR_BAD_BREAK = 32,
-#define QCBOR_END_OF_NOT_WELL_FORMED_ERRORS 16
+#define QCBOR_END_OF_NOT_WELL_FORMED_ERRORS 39
/** During decoding, the input is too large. It is greater than
QCBOR_MAX_DECODE_INPUT_SIZE. This is an implementation limit.
This error makes no further decoding possible. */
- QCBOR_ERR_INPUT_TOO_LARGE = 17,
+ QCBOR_ERR_INPUT_TOO_LARGE = 40,
/** During decoding, the array or map nesting was deeper than this
implementation can handle. Note that in the interest of code
@@ -380,13 +389,13 @@
array nesting. The limit is defined as the constant @ref
QCBOR_MAX_ARRAY_NESTING. This error makes no further decoding
possible. */
- QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP = 18,
+ QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP = 41,
/** During decoding, the array or map had too many items in it.
This limit @ref QCBOR_MAX_ITEMS_IN_ARRAY, typically 65,534,
UINT16_MAX - 1. This error makes no further decoding
possible. */
- QCBOR_ERR_ARRAY_DECODE_TOO_LONG = 19,
+ QCBOR_ERR_ARRAY_DECODE_TOO_LONG = 42,
/** When decoding, a string's size is greater than what a size_t
can hold less 4. In all but some very strange situations this
@@ -394,25 +403,51 @@
such. The strange situation is a CPU with a very small size_t
(e.g., a 16-bit CPU) and a large string (e.g., > 65KB). This
error makes no further decoding possible. */
- QCBOR_ERR_STRING_TOO_LONG = 20,
+ QCBOR_ERR_STRING_TOO_LONG = 43,
/** Something is wrong with a decimal fraction or bigfloat such as
it not consisting of an array with two integers. This error
makes no further decoding possible. */
- QCBOR_ERR_BAD_EXP_AND_MANTISSA = 21,
+ QCBOR_ERR_BAD_EXP_AND_MANTISSA = 44,
/** Unable to decode an indefinite-length string because no string
allocator was configured. See QCBORDecode_SetMemPool() or
QCBORDecode_SetUpAllocator(). This error makes no further
- decoding possible.*/
- QCBOR_ERR_NO_STRING_ALLOCATOR = 22,
+ decoding possible. */
+ QCBOR_ERR_NO_STRING_ALLOCATOR = 45,
/** Error allocating space for a string, usually for an
indefinite-length string. This error makes no further decoding
possible. */
- QCBOR_ERR_STRING_ALLOCATE = 23,
+ QCBOR_ERR_STRING_ALLOCATE = 46,
-#define QCBOR_END_OF_UNRECOVERABLE_DECODE_ERRORS 23
+ /** During decoding, the type of the label for a map entry is not
+ one that can be handled in the current decoding mode. Typically
+ this is because a label is not an intger or a string. This is
+ an implemation limit. */
+ QCBOR_ERR_MAP_LABEL_TYPE = 47,
+
+ /** When the built-in tag decoding encounters an unexpected type,
+ this error is returned. This error is unrecoverable because the
+ built-in tag decoding doesn't try to consume the unexpected
+ type. In previous versions of QCBOR this was considered a
+ recoverable error hence QCBOR_ERR_BAD_TAG_CONTENT. Going back
+ further, RFC 7049 use the name "optional tags". That name is no
+ longer used because "optional" was causing confusion. See
+ also @ref QCBOR_ERR_RECOVERABLE_BAD_TAG_CONTENT. */
+ QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT = 48,
+ QCBOR_ERR_BAD_TAG_CONTENT = 48,
+ QCBOR_ERR_BAD_OPT_TAG = 48,
+
+ /** Indefinite length string handling is disabled and there is an
+ indefinite length string in the input CBOR. */
+ QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED = 49,
+
+ /** Indefinite length arrays and maps handling are disabled and there is an
+ indefinite length map or array in the input CBOR. */
+ QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED = 50,
+
+#define QCBOR_END_OF_UNRECOVERABLE_DECODE_ERRORS 59
/** More than @ref QCBOR_MAX_TAGS_PER_ITEM tags encountered for a
CBOR ITEM. @ref QCBOR_MAX_TAGS_PER_ITEM is a limit of this
@@ -420,116 +455,93 @@
caller-configured tag list, or not enough space in @ref
QCBORTagListOut. This error makes no further decoding
possible. */
- QCBOR_ERR_TOO_MANY_TAGS = 24,
-
- /** During decoding, the type of the label for a map entry is not
- one that can be handled in the current decoding mode. Typically
- this is because a label is not an intger or a string. This is
- an implemation limit. */
- QCBOR_ERR_MAP_LABEL_TYPE = 25,
+ QCBOR_ERR_TOO_MANY_TAGS = 60,
/** When decoding for a specific type, the type was not was
expected. */
- QCBOR_ERR_UNEXPECTED_TYPE = 26,
+ QCBOR_ERR_UNEXPECTED_TYPE = 61,
- /** This occurs when decoding one of the tags that QCBOR processed
- internally. The content of a tag was of the wrong type. (They
- were known as "Optional Tags" in RFC 7049, but "optional" is
- misleading. The old error name is retained for backwards
- compatibility. */
- QCBOR_ERR_BAD_TAG_CONTENT = 27,
- QCBOR_ERR_BAD_OPT_TAG = 27,
-
- /** Duplicate label in map detected */
- QCBOR_ERR_DUPLICATE_LABEL = 28,
+ /** Duplicate label in map detected. */
+ QCBOR_ERR_DUPLICATE_LABEL = 62,
/** During decoding, the buffer given to QCBORDecode_SetMemPool()
is either too small, smaller than
QCBOR_DECODE_MIN_MEM_POOL_SIZE or too large, larger than
UINT32_MAX. */
- QCBOR_ERR_MEM_POOL_SIZE = 29,
+ QCBOR_ERR_MEM_POOL_SIZE = 63,
/** During decoding, an integer smaller than INT64_MIN was received
(CBOR can represent integers smaller than INT64_MIN, but C
cannot). */
- QCBOR_ERR_INT_OVERFLOW = 30,
+ QCBOR_ERR_INT_OVERFLOW = 64,
/** During decoding, a date greater than +- 292 billion years from
Jan 1 1970 encountered during parsing. This is an
implementation limit. */
- QCBOR_ERR_DATE_OVERFLOW = 31,
+ QCBOR_ERR_DATE_OVERFLOW = 65,
/** During decoding, @c QCBORDecode_ExitXxx() was called for a
different type than @c QCBORDecode_EnterXxx(). */
- QCBOR_ERR_EXIT_MISMATCH = 32,
+ QCBOR_ERR_EXIT_MISMATCH = 66,
/** All well-formed data items have been consumed and there are no
more. If parsing a CBOR stream this indicates the non-error end
of the stream. If not parsing a CBOR stream / sequence, this
probably indicates that some data items expected are not
present. See also @ref QCBOR_ERR_HIT_END. */
- QCBOR_ERR_NO_MORE_ITEMS = 33,
+ QCBOR_ERR_NO_MORE_ITEMS = 67,
/** When finding an item by label, an item with the requested label
was not found. */
- QCBOR_ERR_LABEL_NOT_FOUND = 34,
+ QCBOR_ERR_LABEL_NOT_FOUND = 68,
/** Number conversion failed because of sign. For example a
negative int64_t can't be converted to a uint64_t */
- QCBOR_ERR_NUMBER_SIGN_CONVERSION = 35,
+ QCBOR_ERR_NUMBER_SIGN_CONVERSION = 69,
/** When converting a decoded number, the value is too large or to
small for the conversion target */
- QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW = 36,
+ QCBOR_ERR_CONVERSION_UNDER_OVER_FLOW = 70,
/** Trying to get an item by label when a map has not been
entered. */
- QCBOR_ERR_MAP_NOT_ENTERED = 37,
+ QCBOR_ERR_MAP_NOT_ENTERED = 71,
- /** A callback indicates processing should not continue for some
- non-CBOR reason */
- QCBOR_ERR_CALLBACK_FAIL = 38,
+ /** A @ref QCBORItemCallback callback indicates processing should not
+ continue for some non-CBOR reason. */
+ QCBOR_ERR_CALLBACK_FAIL = 72,
/** This error code is deprecated. Instead,
- \ref QCBOR_ERR_HALF_PRECISION_DISABLED,
- \ref QCBOR_ERR_HW_FLOAT_DISABLED or \ref QCBOR_ERR_ALL_FLOAT_DISABLED
+ @ref QCBOR_ERR_HALF_PRECISION_DISABLED,
+ @ref QCBOR_ERR_HW_FLOAT_DISABLED or @ref QCBOR_ERR_ALL_FLOAT_DISABLED
is returned depending on the specific floating-point functionality
that is disabled and the type of floating-point input. */
- QCBOR_ERR_FLOAT_DATE_DISABLED = 39,
+ QCBOR_ERR_FLOAT_DATE_DISABLED = 73,
/** Support for half-precision float decoding is disabled. */
- QCBOR_ERR_HALF_PRECISION_DISABLED = 40,
+ QCBOR_ERR_HALF_PRECISION_DISABLED = 74,
/** Use of floating-point HW is disabled. This affects all type
conversions to and from double and float types. */
- QCBOR_ERR_HW_FLOAT_DISABLED = 41,
+ QCBOR_ERR_HW_FLOAT_DISABLED = 75,
/** Unable to complete operation because a floating-point value
that is a NaN (not a number), that is too large, too small,
infinity or -infinity was encountered in encoded CBOR. Usually
this because conversion of the float-point value was being
attempted. */
- QCBOR_ERR_FLOAT_EXCEPTION = 42,
-
- /** Indefinite length string handling is disabled and there is an
- indefinite length string in the input CBOR. */
- QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED = 43,
-
- /** Indefinite length arrays and maps handling are disabled and there is an
- indefinite length map or array in the input CBOR. */
- QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED = 44,
-
- /** Trying to cancel a byte string wrapping after items have been
- added to it. */
- QCBOR_ERR_CANNOT_CANCEL = 45,
+ QCBOR_ERR_FLOAT_EXCEPTION = 76,
/** Floating point support is completely turned off, encoding/decoding
floating point numbers is not possible. */
- QCBOR_ERR_ALL_FLOAT_DISABLED = 46,
+ QCBOR_ERR_ALL_FLOAT_DISABLED = 77,
- /** During encode, opening a byte string while a byte string is open
- is not allowed. . */
- QCBOR_ERR_OPEN_BYTE_STRING = 47
+ /** Like @ref QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT, but recoverable.
+ If an implementation decodes a tag and can and does consume the
+ whole tag contents when it is not the correct tag content, this
+ error can be returned. None of the built-in tag decoders do
+ this (to save object code). */
+ QCBOR_ERR_RECOVERABLE_BAD_TAG_CONTENT = 78
/* This is stored in uint8_t; never add values > 255 */
} QCBORError;
@@ -549,10 +561,9 @@
/**
- The maximum number of items in a single array or map when encoding of
- decoding.
+ * The maximum number of items in a single array or map when encoding of decoding.
*/
-// -1 is because the value UINT16_MAX is used to track indefinite-length arrays
+/* -1 because the value UINT16_MAX is used to track indefinite-length arrays */
#define QCBOR_MAX_ITEMS_IN_ARRAY (UINT16_MAX-1)
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index 4c641ae..65eff4c 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -1,6 +1,6 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2021, Laurence Lundblade.
+ Copyright (c) 2018-2022, Laurence Lundblade.
Copyright (c) 2021, Arm Limited.
All rights reserved.
@@ -896,7 +896,7 @@
* | @ref QCBOR_ERR_TOO_MANY_TAGS | Tag nesting deeper than limit, typically 4 |
* | __Configuration errors__ ||
* | @ref QCBOR_ERR_NO_STRING_ALLOCATOR | Encountered indefinite-length string with no allocator configured |
- * | @ref QCBOR_ERR_MAP_LABEL_TYPE | A map label this is not a string on an integer |
+ * | @ref QCBOR_ERR_MAP_LABEL_TYPE | A map label that is not a string on an integer |
* | @ref QCBOR_ERR_HALF_PRECISION_DISABLED | Library compiled with half-precision disabled and half-precision input encountered |
* | @ref QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED | Library compiled with indefinite maps and arrays disabled and indefinite map or array encountered |
* | @ref QCBOR_ERR_INDEF_LEN_STRINGS_DISABLED | Library compiled with indefinite strings disabled and indefinite string encountered |
@@ -1167,6 +1167,10 @@
* errors like incorrect lengths or array counts are
* unrecoverable. Unrecoverable errors also occur when implementation
* limits such as the limit on array and map nesting are encountered.
+ * When the built-in decoding of a tag like an epoch date encounters
+ * an error such as a data item of an unexpected type, this is also an
+ * unrecoverable error because the internal decoding doesn't try to
+ * decode everything in the tag.
*
* The unrecoverable errors are a range of the errors in
* @ref QCBORError.
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index 8217073..faa0218 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -1,6 +1,6 @@
/*==============================================================================
Copyright (c) 2016-2018, The Linux Foundation.
- Copyright (c) 2018-2021, Laurence Lundblade.
+ Copyright (c) 2018-2022, Laurence Lundblade.
Copyright (c) 2021, Arm Limited.
All rights reserved.
@@ -1525,7 +1525,7 @@
* combines pairs of items into one data item with a label
* and value.
*
- * This is passthrough if the current nesting leve is
+ * This is passthrough if the current nesting level is
* not a map.
*
* This also implements maps-as-array mode where a map
@@ -1913,7 +1913,7 @@
* @retval QCBOR_ERR_DATE_OVERFLOW
* @retval QCBOR_ERR_FLOAT_DATE_DISABLED
* @retval QCBOR_ERR_ALL_FLOAT_DISABLED
- * @retval QCBOR_ERR_BAD_TAG_CONTENT
+ * @retval QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT
*
* The epoch date tag defined in QCBOR allows for floating-point
* dates. It even allows a protocol to flop between date formats when
@@ -1998,7 +1998,11 @@
break;
default:
- uReturn = QCBOR_ERR_BAD_TAG_CONTENT;
+ /* It's the arrays and maps that are unrecoverable because
+ * they are not consumed here. Since this is just an error
+ * condition, no extra code is added here to make the error
+ * recoverable for non-arrays and maps like strings. */
+ uReturn = QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT;
goto Done;
}
@@ -2017,7 +2021,7 @@
* @retval QCBOR_ERR_DATE_OVERFLOW
* @retval QCBOR_ERR_FLOAT_DATE_DISABLED
* @retval QCBOR_ERR_ALL_FLOAT_DISABLED
- * @retval QCBOR_ERR_BAD_TAG_CONTENT
+ * @retval QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT
*
* This is much simpler than the other epoch date format because
* floating-porint is not allowed. This is mostly a simple type check.
@@ -2041,7 +2045,11 @@
break;
default:
- uReturn = QCBOR_ERR_BAD_TAG_CONTENT;
+ /* It's the arrays and maps that are unrecoverable because
+ * they are not consumed here. Since this is just an error
+ * condition, no extra code is added here to make the error
+ * recoverable for non-arrays and maps like strings. */
+ uReturn = QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT;
goto Done;
break;
}
@@ -2184,7 +2192,11 @@
} else if(pDecodedItem->uDataType == QCBOR_TYPE_BYTE_STRING) {
pDecodedItem->uDataType = QCBOR_TYPE_BINARY_MIME;
} else {
- return QCBOR_ERR_BAD_OPT_TAG;
+ /* It's the arrays and maps that are unrecoverable because
+ * they are not consumed here. Since this is just an error
+ * condition, no extra code is added here to make the error
+ * recoverable for non-arrays and maps like strings. */
+ return QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT;
}
return QCBOR_SUCCESS;
@@ -2232,7 +2244,7 @@
*
* @returns This returns QCBOR_SUCCESS if the tag was procssed,
* \ref QCBOR_ERR_UNSUPPORTED if the tag was not processed and
- * \ref QCBOR_ERR_BAD_OPT_TAG if the content type was wrong for the tag.
+ * \ref QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT if the content type was wrong for the tag.
*
* Process the CBOR tags that whose content is a byte string or a text
* string and for which the string is just passed on to the caller.
@@ -2269,7 +2281,11 @@
}
if(pDecodedItem->uDataType != uExpectedType) {
- return QCBOR_ERR_BAD_OPT_TAG;
+ /* It's the arrays and maps that are unrecoverable because
+ * they are not consumed here. Since this is just an error
+ * condition, no extra code is added here to make the error
+ * recoverable for non-arrays and maps like strings. */
+ return QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT;
}
pDecodedItem->uDataType = (uint8_t)(uQCBORType & QCBOR_TYPE_MASK);
@@ -2755,9 +2771,18 @@
}
-/*
- Consume an entire map or array (and do next to
- nothing for non-aggregate types).
+/**
+ * @brief Consume an entire map or array including its contents.
+ *
+ * @param[in] pMe The decoder context.
+ * @param[in] pItemToConsume The array/map whose contents are to be
+ * consumed.
+ * @param[out] puNextNestLevel The next nesting level after the item was
+ * fully consumed.
+ *
+ * This may be called when @c pItemToConsume is not an array or
+ * map. In that case, this is just a pass through for @c puNextNestLevel
+ * since there is nothing to do.
*/
static inline QCBORError
ConsumeItem(QCBORDecodeContext *pMe,
@@ -2767,18 +2792,19 @@
QCBORError uReturn;
QCBORItem Item;
- // If it is a map or array, this will tell if it is empty.
+ /* If it is a map or array, this will tell if it is empty. */
const bool bIsEmpty = (pItemToConsume->uNextNestLevel <= pItemToConsume->uNestingLevel);
if(QCBORItem_IsMapOrArray(pItemToConsume) && !bIsEmpty) {
/* There is only real work to do for non-empty maps and arrays */
- /* This works for definite- and indefinite- length
- * maps and arrays by using the nesting level
+ /* This works for definite- and indefinite-length maps and
+ * arrays by using the nesting level
*/
do {
uReturn = QCBORDecode_GetNext(pMe, &Item);
- if(QCBORDecode_IsUnrecoverableError(uReturn)) {
+ if(QCBORDecode_IsUnrecoverableError(uReturn) ||
+ uReturn == QCBOR_ERR_NO_MORE_ITEMS) {
goto Done;
}
} while(Item.uNextNestLevel >= pItemToConsume->uNextNestLevel);
@@ -2788,8 +2814,8 @@
uReturn = QCBOR_SUCCESS;
} else {
- /* item_to_consume is not a map or array */
- /* Just pass the nesting level through */
+ /* pItemToConsume is not a map or array. Just pass the nesting
+ * level through. */
*puNextNestLevel = pItemToConsume->uNextNestLevel;
uReturn = QCBOR_SUCCESS;
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 755d032..431836c 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -2561,7 +2561,7 @@
Untagged values
*/
static const uint8_t spSpiffyDateTestInput[] = {
- 0x86,
+ 0x86, // array of 6 items
0xc1,
0xfb, 0xc3, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // -9.2233720368547748E+18, too negative
@@ -2572,12 +2572,6 @@
0xc1, // tag for epoch date
0xf9, 0xfc, 0x00, // Half-precision -Infinity
- 0xc1, // tag for epoch date
- 0x80, // Erroneous empty array as content for date
-
- 0xc0, // tag for string date
- 0xa0, // Erroneous empty map as content for date
-
0xad, // Open a map for tests involving labels.
0x00,
@@ -2634,7 +2628,16 @@
/* Untagged days-count epoch date */
0x11,
- 0x19, 0x0F, 0x9A /* 3994 */
+ 0x19, 0x0F, 0x9A, /* 3994 */
+
+ // End of map, back to array
+
+ // These two at the end because they are unrecoverable errors
+ 0xc1, // tag for epoch date
+ 0x80, // Erroneous empty array as content for date
+
+ 0xc0, // tag for string date
+ 0xa0 // Erroneous empty map as content for date
};
@@ -2674,19 +2677,7 @@
return 2;
}
- // Bad content for epoch date
- QCBORDecode_GetEpochDate(&DC, QCBOR_TAG_REQUIREMENT_TAG, &nEpochDateFail);
- uError = QCBORDecode_GetAndResetError(&DC);
- if(uError != QCBOR_ERR_BAD_OPT_TAG) {
- return 3;
- }
- // Bad content for string date
- QCBORDecode_GetDateString(&DC, QCBOR_TAG_REQUIREMENT_TAG, &StringDate1);
- uError = QCBORDecode_GetAndResetError(&DC);
- if(uError != QCBOR_ERR_BAD_OPT_TAG) {
- return 4;
- }
QCBORDecode_EnterMap(&DC, NULL);
@@ -2826,9 +2817,27 @@
&nEpochDays2);
QCBORDecode_ExitMap(&DC);
+ if(QCBORDecode_GetError(&DC) != QCBOR_SUCCESS) {
+ return 3001;
+ }
+
+ // Bad content for epoch date
+ QCBORDecode_GetEpochDate(&DC, QCBOR_TAG_REQUIREMENT_TAG, &nEpochDateFail);
+ uError = QCBORDecode_GetAndResetError(&DC);
+ if(uError != QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT) {
+ return 3;
+ }
+
+ // Bad content for string date
+ QCBORDecode_GetDateString(&DC, QCBOR_TAG_REQUIREMENT_TAG, &StringDate1);
+ uError = QCBORDecode_GetAndResetError(&DC);
+ if(uError != QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT) {
+ return 4;
+ }
+
QCBORDecode_ExitArray(&DC);
uError = QCBORDecode_Finish(&DC);
- if(uError) {
+ if(uError != QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT) {
return 1000 + (int32_t)uError;
}
@@ -3031,6 +3040,10 @@
0xc0, // tag for string date
0x4a, '1','9','8','5','-','0','4','-','1','2', // Date string in byte string
+
+ // This last case makes the array untraversable because it is
+ // an uncrecoverable error. Make sure it stays last and is the only
+ // instance so the other tests can work.
};
@@ -3432,11 +3445,15 @@
}
// tagged date string with a byte string
QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_TAG, &DateString);
- if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_BAD_OPT_TAG) {
+ if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT) {
return 103;
}
+ // The exit errors out because the last item, the date string with
+ // bad content makes the array untraversable (the bad date string
+ // could have tag content of an array or such that is not consumed
+ // by the date decoding).
QCBORDecode_ExitArray(&DCtx);
- if(QCBORDecode_Finish(&DCtx) != QCBOR_SUCCESS) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT) {
return 104;
}
@@ -3465,11 +3482,12 @@
}
// tagged date string with a byte string
QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_OPTIONAL_TAG, &DateString);
- if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_BAD_OPT_TAG) {
+ if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT) {
return 203;
}
+ // See comments above
QCBORDecode_ExitArray(&DCtx);
- if(QCBORDecode_Finish(&DCtx) != QCBOR_SUCCESS) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT) {
return 204;
}
@@ -3500,11 +3518,12 @@
}
// tagged date string with a byte string
QCBORDecode_GetDateString(&DCtx, QCBOR_TAG_REQUIREMENT_NOT_A_TAG, &DateString);
- if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_BAD_OPT_TAG) {
+ if(QCBORDecode_GetAndResetError(&DCtx) != QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT) {
return 304;
}
+ // See comments above
QCBORDecode_ExitArray(&DCtx);
- if(QCBORDecode_Finish(&DCtx) != QCBOR_SUCCESS) {
+ if(QCBORDecode_Finish(&DCtx) != QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT) {
return 305;
}
@@ -5179,23 +5198,21 @@
/*
Too many tags
- Invalid tag content
Duplicate label
Integer overflow
Date overflow
{
1: 224(225(226(227(4(0))))),
- 2: 1(h''),
3: -18446744073709551616,
4: 1(1.0e+300),
- 5: 0, 8: 8
+ 5: 0,
+ 8: 8
}
*/
static const uint8_t spRecoverableMapErrors[] = {
- 0xa7,
+ 0xa6,
0x01, 0xd8, 0xe0, 0xd8, 0xe1, 0xd8, 0xe2, 0xd8, 0xe3, 0xd8, 0x04, 0x00,
- 0x02, 0xc1, 0x40,
0x03, 0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x04, 0xc1, 0xfb, 0x7e, 0x37, 0xe4, 0x3c, 0x88, 0x00, 0x75, 0x9c,
0x05, 0x00,
@@ -5234,6 +5251,37 @@
0xa1, 0x14, 0x1f,
};
+
+/* Array of length 3, but only two items. */
+const unsigned char spBadConsumeInput[] = {
+ 0x83, 0x00, 0x00
+};
+
+/* Tag nesting too deep. */
+const unsigned char spBadConsumeInput2[] = {
+ 0x81,
+ 0xD8, 0x37,
+ 0xD8, 0x2C,
+ 0xD8, 0x21,
+ 0xD6,
+ 0xCB,
+ 00
+};
+
+const unsigned char spBadConsumeInput3[] = {
+ 0x81, 0xc0, 0x81, 0x00
+};
+
+const unsigned char spBadConsumeInput4[] = {
+ 0x81, 0x9f, 0x00, 0xff
+};
+
+const unsigned char spBadConsumeInput5[] = {
+ 0xa1, 0x80, 0x00
+};
+
+
+
int32_t EnterMapTest()
{
QCBORItem Item1;
@@ -5450,12 +5498,6 @@
(void)QCBORDecode_GetAndResetError(&DCtx);
- QCBORDecode_GetEpochDateInMapN(&DCtx, 0x02, QCBOR_TAG_REQUIREMENT_TAG, &nInt);
- uErr = QCBORDecode_GetAndResetError(&DCtx);
- if(uErr != QCBOR_ERR_BAD_OPT_TAG) {
- return 2022;
- }
-
QCBORDecode_GetInt64InMapN(&DCtx, 0x03, &nInt);
uErr = QCBORDecode_GetAndResetError(&DCtx);
if(uErr != QCBOR_ERR_INT_OVERFLOW) {
@@ -5575,6 +5617,42 @@
return 2500;
}
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBadConsumeInput), 0);
+ QCBORDecode_VGetNextConsume(&DCtx, &Item1);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_ERR_NO_MORE_ITEMS) {
+ return 2600;
+ }
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBadConsumeInput2), 0);
+ QCBORDecode_VGetNextConsume(&DCtx, &Item1);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
+ return 2700;
+ }
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBadConsumeInput3), 0);
+ QCBORDecode_VGetNextConsume(&DCtx, &Item1);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_ERR_UNRECOVERABLE_TAG_CONTENT) {
+ return 2800;
+ }
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBadConsumeInput4), 0);
+ QCBORDecode_VGetNextConsume(&DCtx, &Item1);
+#ifndef QCBOR_DISABLE_INDEFINITE_LENGTH_ARRAYS
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_SUCCESS) {
+ return 2900;
+ }
+#else
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_ERR_INDEF_LEN_ARRAYS_DISABLED) {
+ return 2901;
+ }
+#endif
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spBadConsumeInput5), 0);
+ QCBORDecode_VGetNextConsume(&DCtx, &Item1);
+ if(QCBORDecode_GetError(&DCtx) != QCBOR_ERR_MAP_LABEL_TYPE) {
+ return 3000;
+ }
+
return nReturn;
}