improve map search error handling
diff --git a/inc/qcbor/qcbor_common.h b/inc/qcbor/qcbor_common.h
index efde5d1..62858de 100644
--- a/inc/qcbor/qcbor_common.h
+++ b/inc/qcbor/qcbor_common.h
@@ -302,18 +302,19 @@
well-formed. */
QCBOR_ERR_ARRAY_OR_MAP_UNCONSUMED = 12,
- /** During decoding, an integer type is encoded with a bad length (
- that of an indefinite length string). The CBOR is not-well
+ /** 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,
+#define QCBOR_START_OF_UNRECOVERABLE_DECODE_ERRORS 14
+
/** 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,
-#define QCBOR_START_OF_UNRECOVERABLE_DECODE_ERRORS 15
/** During decoding, hit the end of the given data to decode. For
example, a byte string of 100 bytes was expected, but the end
of the input was hit before finding those 100 bytes. Corrupted
@@ -348,40 +349,39 @@
possible. */
QCBOR_ERR_ARRAY_DECODE_TOO_LONG = 19,
- /** More than @ref QCBOR_MAX_TAGS_PER_ITEM tags encounterd for a
- CBOR ITEM. @ref QCBOR_MAX_TAGS_PER_ITEM is a limit of this
- implementation. During decoding, too many tags in the
- caller-configured tag list, or not enough space in @ref
- QCBORTagListOut. This error makes no further decoding
- possible. */
- // TODO: probably can remove this from unrecoverable
- QCBOR_ERR_TOO_MANY_TAGS = 20,
-
/** 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
is because of corrupt input CBOR and should be treated as
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 = 21,
+ QCBOR_ERR_STRING_TOO_LONG = 20,
/** 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 = 22,
+ QCBOR_ERR_BAD_EXP_AND_MANTISSA = 21,
/** 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 = 23,
+ QCBOR_ERR_NO_STRING_ALLOCATOR = 22,
/** Error allocating space for a string, usually for an
indefinite-length string. This error makes no further decoding
possible. */
- QCBOR_ERR_STRING_ALLOCATE = 24,
+ QCBOR_ERR_STRING_ALLOCATE = 23,
-#define QCBOR_END_OF_UNRECOVERABLE_DECODE_ERRORS 24
+#define QCBOR_END_OF_UNRECOVERABLE_DECODE_ERRORS 23
+
+ /** More than @ref QCBOR_MAX_TAGS_PER_ITEM tags encounterd for a
+ CBOR ITEM. @ref QCBOR_MAX_TAGS_PER_ITEM is a limit of this
+ implementation. During decoding, too many tags in the
+ 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
diff --git a/inc/qcbor/qcbor_decode.h b/inc/qcbor/qcbor_decode.h
index 5c2e3f5..b88657c 100644
--- a/inc/qcbor/qcbor_decode.h
+++ b/inc/qcbor/qcbor_decode.h
@@ -300,7 +300,7 @@
/**
The main data structure that holds the type, value and other info for
a decoded item returned by QCBORDecode_GetNext() and
- QCBORDecode_GetNextWithTags().
+ and methods.
This size of this may vary by compiler but is roughly 56 bytes on
a 64-bit CPU and 52 bytes on a 32-bit CPU.
@@ -1115,6 +1115,8 @@
incorrect lengths or array counts are unrecoverable. Unrecoverable
errors also occur when certain implementation limits such as the
limit on array and map nesting occur.
+
+ The specific errors are a range of the errors in @ref QCBORError.
*/
static bool QCBORDecode_IsUnrecoverableError(QCBORError uErr);
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
index ae66a3b..4145f8a 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -112,11 +112,16 @@
type. Then for use one of the type-specific methods to get the item
again to take advantage of the type conversion provided.
- Error reporting when get items by label in a map is not accurate for
- some errors. They are reported as not found rather than overflow and
- such. The error may be for other than the labeled item being searched
- for. Non-well formed maps cannot be searched at all. (This may be
- improved).
+ When getting an item by label from a map the whole map is
+ traversed including traversal of nested arrays and maps. If
+ there is any unrecoverable error anywhere in the that traversal
+ the retrieval by label will fail and the unrecoverable error
+ will be returned even if it is not due to the labeled item
+ being sought. Recoverable errors will be ignored unless
+ they are on the item being sought, in which case the
+ unrecoverable error will be returned. Unrecoverable
+ errors are those indicated by QCBORDecode_IsUnrecoverableError().
+
@anchor Tag-Usage
@@ -1707,7 +1712,8 @@
QCBORDecode_GetInt64ConvertInternalInMapN(pMe,
nLabel,
uConvertTypes,
- pnValue, &Item);
+ pnValue,
+ &Item);
}
inline static void
@@ -1720,7 +1726,8 @@
QCBORDecode_GetInt64ConvertInternalInMapSZ(pMe,
szLabel,
uConvertTypes,
- pnValue, &Item);
+ pnValue,
+ &Item);
}
inline static void
@@ -1731,8 +1738,8 @@
inline static void
QCBORDecode_GetInt64InMapN(QCBORDecodeContext *pMe,
- int64_t nLabel,
- int64_t *pnValue)
+ int64_t nLabel,
+ int64_t *pnValue)
{
QCBORDecode_GetInt64ConvertInMapN(pMe,
nLabel,
@@ -1742,8 +1749,8 @@
inline static void
QCBORDecode_GetInt64InMapSZ(QCBORDecodeContext *pMe,
- const char *szLabel,
- int64_t *pnValue)
+ const char *szLabel,
+ int64_t *pnValue)
{
QCBORDecode_GetInt64ConvertInMapSZ(pMe,
szLabel,
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index a34c1d0..4cc0113 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -1300,8 +1300,9 @@
// be the real data
QCBORItem LabelItem = *pDecodedItem;
nReturn = GetNext_TaggedItem(me, pDecodedItem);
- if(nReturn)
+ if(QCBORDecode_IsUnrecoverableError(nReturn)) {
goto Done;
+ }
pDecodedItem->uLabelAlloc = LabelItem.uDataAlloc;
@@ -1603,12 +1604,6 @@
}
Done:
- if(uReturn != QCBOR_SUCCESS) {
- /* This sets uDataType and uLabelType to QCBOR_TYPE_NONE */
- pDecodedItem->uDataType = QCBOR_TYPE_NONE;
- pDecodedItem->uLabelType = QCBOR_TYPE_NONE;
- // memset(pDecodedItem, 0, sizeof(QCBORItem));
- }
return uReturn;
}
@@ -1934,8 +1929,8 @@
/*
Public function, see header qcbor/qcbor_decode.h file
*/
-QCBORError
-QCBORDecode_GetNext(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
+static QCBORError
+QCBORDecode_GetNextTag(QCBORDecodeContext *me, QCBORItem *pDecodedItem)
{
QCBORError nReturn;
@@ -2029,11 +2024,20 @@
}
Done:
- if(nReturn != QCBOR_SUCCESS) {
+ return nReturn;
+}
+
+
+QCBORError
+QCBORDecode_GetNext(QCBORDecodeContext *pMe, QCBORItem *pDecodedItem)
+{
+ QCBORError uErr;
+ uErr = QCBORDecode_GetNextTag(pMe, pDecodedItem);
+ if(uErr != QCBOR_SUCCESS) {
pDecodedItem->uDataType = QCBOR_TYPE_NONE;
pDecodedItem->uLabelType = QCBOR_TYPE_NONE;
}
- return nReturn;
+ return uErr;
}
@@ -2538,7 +2542,7 @@
for one of the labels being search for. This duplicate detection is only performed for items in pItemArray,
not every item in the map.
- @retval QCBOR_ERR_UNEXPECTED_TYPE The label was matched, but not the type.
+ @retval QCBOR_ERR_UNEXPECTED_TYPE A label was matched, but the type was wrong for the matchd label.
@retval Also errors returned by QCBORDecode_GetNext().
@@ -2597,10 +2601,21 @@
DecodeNesting_GetMapOrArrayStart(&(pMe->nesting)));
/*
- Loop over all the items in the map. They could be
- deeply nested and this should handle both definite
- and indefinite length maps and arrays, so this
- adds some complexity.
+ Loop over all the items in the map or array. Each item
+ could be a map or array, but label matching is only at
+ the main level. This handles definite and indefinite
+ length maps and arrays. The only reason this is ever
+ called on arrays is to find their end position.
+
+ This will always run over all items in order to do
+ duplicate detection.
+
+ This will exit with failure if it encounters an
+ unrecoverable error, but continue on for recoverable
+ errors.
+
+ If a recoverable error occurs on a matched item, then
+ that error code is returned.
*/
const uint8_t uMapNestLevel = DecodeNesting_GetBoundedModeLevel(&(pMe->nesting));
uint_fast8_t uNextNestLevel;
@@ -2610,21 +2625,21 @@
/* Get the item */
QCBORItem Item;
- uReturn = QCBORDecode_GetNext(pMe, &Item);
- if(QCBORDecode_IsUnrecoverableError(uReturn)) {
+ QCBORError uResult = QCBORDecode_GetNextTag(pMe, &Item);
+ if(QCBORDecode_IsUnrecoverableError(uResult)) {
/* Got non-well-formed CBOR so map can't even be decoded. */
+ uReturn = uResult;
goto Done;
}
- if(uReturn == QCBOR_ERR_NO_MORE_ITEMS) {
+ if(uResult == QCBOR_ERR_NO_MORE_ITEMS) {
// Unexpected end of map or array.
+ uReturn = uResult;
goto Done;
}
/* See if item has one of the labels that are of interest */
bool bMatched = false;
for(int nIndex = 0; pItemArray[nIndex].uLabelType != QCBOR_TYPE_NONE; nIndex++) {
- // TODO: have label filled in on invalid CBOR so error reporting
- // can work a lot better.
if(MatchLabel(Item, pItemArray[nIndex])) {
/* A label match has been found */
if(uFoundItemBitMap & (0x01ULL << nIndex)) {
@@ -2637,6 +2652,11 @@
goto Done;
}
+ if(uResult != QCBOR_SUCCESS) {
+ uReturn = uResult;
+ goto Done;
+ }
+
/* Successful match. Return the item. */
pItemArray[nIndex] = Item;
uFoundItemBitMap |= 0x01ULL << nIndex;
@@ -2679,8 +2699,8 @@
const size_t uEndOffset = UsefulInputBuf_Tell(&(pMe->InBuf));
- // Check here makes that this won't accidentally be
- // QCBOR_MAP_OFFSET_CACHE_INVALID. It is larger than
+ // Check here makes sure that this won't accidentally be
+ // QCBOR_MAP_OFFSET_CACHE_INVALID which is larger than
// QCBOR_MAX_DECODE_INPUT_SIZE.
if(uEndOffset >= QCBOR_MAX_DECODE_INPUT_SIZE) {
uReturn = QCBOR_ERR_INPUT_TOO_LARGE;
@@ -2693,10 +2713,11 @@
DecodeNesting_RestoreFromMapSearch(&(pMe->nesting), &SaveNesting);
Done2:
- /* For all items not found, set the data type to QCBOR_TYPE_NONE */
+ /* For all items not found, set the data and label type to QCBOR_TYPE_NONE */
for(int i = 0; pItemArray[i].uLabelType != 0; i++) {
if(!(uFoundItemBitMap & (0x01ULL << i))) {
- pItemArray[i].uDataType = QCBOR_TYPE_NONE;
+ pItemArray[i].uDataType = QCBOR_TYPE_NONE;
+ pItemArray[i].uLabelType = QCBOR_TYPE_NONE;
}
}
@@ -2723,16 +2744,16 @@
OneItemSeach[1].uLabelType = QCBOR_TYPE_NONE; // Indicates end of array
QCBORError uReturn = MapSearch(pMe, OneItemSeach, NULL, NULL, NULL);
+
+ *pItem = OneItemSeach[0];
+
if(uReturn != QCBOR_SUCCESS) {
goto Done;
}
if(OneItemSeach[0].uDataType == QCBOR_TYPE_NONE) {
uReturn = QCBOR_ERR_LABEL_NOT_FOUND;
- goto Done;
}
- *pItem = OneItemSeach[0];
-
Done:
pMe->uLastError = (uint8_t)uReturn;
}
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 2780097..71bde05 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -2481,7 +2481,7 @@
}
#else /* QCBOR_DISABLE_FLOAT_HW_USE */
uError = QCBORDecode_GetAndResetError(&DC);
- if(uError != QCBOR_ERR_LABEL_NOT_FOUND) {
+ if(uError != QCBOR_ERR_FLOAT_DATE_DISABLED) {
return 102;
}
#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
@@ -2495,7 +2495,7 @@
}
#else /* QCBOR_DISABLE_FLOAT_HW_USE */
uError = QCBORDecode_GetAndResetError(&DC);
- if(uError != QCBOR_ERR_LABEL_NOT_FOUND) {
+ if(uError != QCBOR_ERR_FLOAT_DATE_DISABLED) {
return 112;
}
#endif /* QCBOR_DISABLE_FLOAT_HW_USE */
@@ -4778,12 +4778,62 @@
}
*/
-
static const uint8_t spMapOfEmpty[] = {
0xa6, 0x00, 0x80, 0x09, 0x82, 0x80, 0x80, 0x08, 0xa3, 0x01,
0x80, 0x02, 0xa0, 0x03, 0x80, 0x04, 0xa0, 0x05, 0x9f, 0xff,
0x06, 0x9f, 0x80, 0x9f, 0xff, 0xff};
+/*
+ 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}
+ */
+static const uint8_t spRecoverableMapErrors[] = {
+ 0xbf,
+ 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,
+ 0x05, 0x00,
+ 0x08, 0x08,
+ 0xff
+};
+
+// Bad break
+static const uint8_t spUnRecoverableMapError1[] = {
+ 0xa2, 0xff, 0x01, 0x00, 0x02, 0x00
+};
+
+// No more items
+static const uint8_t spUnRecoverableMapError2[] = {
+ 0xbf, 0x02, 0xbf, 0xff, 0x01, 0x00, 0x02, 0x00
+};
+
+// Hit end because string is too long
+static const uint8_t spUnRecoverableMapError3[] = {
+ 0xbf, 0x02, 0x69, 0x64, 0x64, 0xff
+};
+
+// Hit end because string is too long
+static const uint8_t spUnRecoverableMapError4[] = {
+ 0xbf,
+ 0x02, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff
+};
+
+
int32_t EnterMapTest()
{
QCBORItem Item1;
@@ -4964,12 +5014,88 @@
QCBORDecode_GetInt64(&DCtx, &nDecodedInt2);
QCBORDecode_ExitArray(&DCtx);
uErr = QCBORDecode_Finish(&DCtx);
- if(uErr != QCBOR_SUCCESS){
+ if(uErr != QCBOR_SUCCESS) {
return 2014;
}
- // TODO: more testing of entered mapps and arrays with problems
- // TODO: document error handling better (maybe improve error handling)
+ int64_t nInt;
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spRecoverableMapErrors), 0);
+ QCBORDecode_EnterMap(&DCtx);
+ QCBORDecode_GetInt64InMapN(&DCtx, 0x01, &nInt);
+ uErr = QCBORDecode_GetAndResetError(&DCtx);
+ if(uErr != QCBOR_ERR_TOO_MANY_TAGS) {
+ return 2021;
+ }
+
+ 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) {
+ return 2023;
+ }
+
+ QCBORDecode_GetEpochDateInMapN(&DCtx, 0x04, QCBOR_TAG_REQUIREMENT_TAG, &nInt);
+ uErr = QCBORDecode_GetAndResetError(&DCtx);
+#ifndef QCBOR_DISABLE_FLOAT_HW_USE
+ if(uErr != QCBOR_ERR_DATE_OVERFLOW) {
+ return 2024;
+ }
+#else
+ if(uErr != QCBOR_ERR_FLOAT_DATE_DISABLED) {
+ return 2027;
+ }
+#endif
+
+ QCBORDecode_GetInt64InMapN(&DCtx, 0x05, &nInt);
+ uErr = QCBORDecode_GetAndResetError(&DCtx);
+ if(uErr != QCBOR_ERR_DUPLICATE_LABEL) {
+ return 2025;
+ }
+
+ QCBORDecode_GetInt64InMapN(&DCtx, 0x08, &nInt);
+
+ QCBORDecode_ExitMap(&DCtx);
+ uErr = QCBORDecode_Finish(&DCtx);
+ if(uErr != QCBOR_SUCCESS) {
+ return 2026;
+ }
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spUnRecoverableMapError1), 0);
+ QCBORDecode_EnterMap(&DCtx);
+ QCBORDecode_GetInt64InMapN(&DCtx, 0x01, &nInt);
+ uErr = QCBORDecode_GetAndResetError(&DCtx);
+ if(uErr != QCBOR_ERR_BAD_BREAK) {
+ return 2030;
+ }
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spUnRecoverableMapError2), 0);
+ QCBORDecode_EnterMap(&DCtx);
+ QCBORDecode_GetInt64InMapN(&DCtx, 0x01, &nInt);
+ uErr = QCBORDecode_GetAndResetError(&DCtx);
+ if(uErr != QCBOR_ERR_NO_MORE_ITEMS) {
+ return 2031;
+ }
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spUnRecoverableMapError3), 0);
+ QCBORDecode_EnterMap(&DCtx);
+ QCBORDecode_GetInt64InMapN(&DCtx, 0x01, &nInt);
+ uErr = QCBORDecode_GetAndResetError(&DCtx);
+ if(uErr != QCBOR_ERR_HIT_END) {
+ return 2032;
+ }
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spUnRecoverableMapError4), 0);
+ QCBORDecode_EnterMap(&DCtx);
+ QCBORDecode_GetInt64InMapN(&DCtx, 0x01, &nInt);
+ uErr = QCBORDecode_GetAndResetError(&DCtx);
+ if(uErr != QCBOR_ERR_ARRAY_DECODE_NESTING_TOO_DEEP) {
+ return 2033;
+ }
return 0;
}