Bug fixes for spiffy decode corner cases (#57)
* Bug fix for decoding empty maps and arrays with spiffy decode
* Bug fix for entering and exiting the same map multiple times
* Add a lot of tests for spiffy decoding of nested maps and arrays
Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/inc/qcbor/qcbor_spiffy_decode.h b/inc/qcbor/qcbor_spiffy_decode.h
index df0067b..0ea6a71 100644
--- a/inc/qcbor/qcbor_spiffy_decode.h
+++ b/inc/qcbor/qcbor_spiffy_decode.h
@@ -729,7 +729,7 @@
The items in the map that was entered do not have to have been
consumed for this to succeed.
- This sets thepre-order traversal cursor to the item after
+ This sets the pre-order traversal cursor to the item after
the map that was exited.
*/
static void QCBORDecode_ExitMap(QCBORDecodeContext *pCtx);
@@ -762,6 +762,8 @@
nesting, this is of little consequence, but may be of consequence for
large deeply nested CBOR structures on slow CPUs.
+ The position of the pre-order traversal cursor is not changed.
+
See @ref Decode-Errors for discussion on how error handling works.
See also QCBORDecode_GetItemsInMap() for error discussion.
@@ -804,6 +806,8 @@
QCBORDecode_EnterMapinMapN(), QCBORDecode_EnterArrayInMapN() and such
to descend into and process maps and arrays.
+ The position of the pre-order traversal cursor is not changed.
+
See @ref Decode-Errors for discussion on how error handling works.
The following errors are set:
@@ -1505,7 +1509,7 @@
When the wrapped CBOR is entered with this function, the pre-order
traversal and such are bounded to the wrapped
- CBOR. QCBORDecode_ExitBstrWrapped() must be called resume processing
+ CBOR. QCBORDecode_ExitBstrWrapped() must be called to resume processing
CBOR outside the wrapped CBOR.
If @c pBstr is not @c NULL the pointer and length of the wrapped
@@ -1518,7 +1522,6 @@
See also QCBORDecode_ExitBstrWrapped(), QCBORDecode_EnterMap() and
QCBORDecode_EnterArray().
-
*/
void QCBORDecode_EnterBstrWrapped(QCBORDecodeContext *pCtx,
uint8_t uTagRequirement,
@@ -1545,7 +1548,7 @@
The items in the wrapped CBOR that was entered do not have to have been
consumed for this to succeed.
- The this sets thepre-order traversal cursor to the item after
+ The this sets the pre-order traversal cursor to the item after
the byte string that was exited.
*/
void QCBORDecode_ExitBstrWrapped(QCBORDecodeContext *pCtx);
diff --git a/src/qcbor_decode.c b/src/qcbor_decode.c
index afc3651..7b41ff3 100644
--- a/src/qcbor_decode.c
+++ b/src/qcbor_decode.c
@@ -227,7 +227,8 @@
return false;
}
// Works for both definite and indefinite length maps/arrays
- if(pNesting->pCurrentBounded->u.ma.uCountCursor != 0) {
+ if(pNesting->pCurrentBounded->u.ma.uCountCursor != 0 &&
+ pNesting->pCurrentBounded->u.ma.uCountCursor != QCBOR_COUNT_INDICATES_ZERO_LENGTH) {
// Count is not zero, still unconsumed item
return false;
}
@@ -432,6 +433,13 @@
static inline void
+DecodeNesting_ResetMapOrArrayCount(QCBORDecodeNesting *pNesting)
+{
+ pNesting->pCurrentBounded->u.ma.uCountCursor = pNesting->pCurrentBounded->u.ma.uCountTotal;
+}
+
+
+static inline void
DecodeNesting_Init(QCBORDecodeNesting *pNesting)
{
/* Assumes that *pNesting has been zero'd before this call. */
@@ -445,7 +453,7 @@
{
*pSave = *pNesting;
pNesting->pCurrent = pNesting->pCurrentBounded;
- pNesting->pCurrent->u.ma.uCountCursor = pNesting->pCurrent->u.ma.uCountTotal;
+ DecodeNesting_ResetMapOrArrayCount(pNesting);
}
@@ -2964,13 +2972,25 @@
}
+/**
+ * @brief Search for a map/array by label and enter it
+ *
+ * @param[in] pMe The decode context.
+ * @param[in] pSearch The map/array to search for.
+ *
+ * @c pSearch is expected to contain one item of type map or array
+ * with the label specified. The current bounded map will be searched for
+ * this and if found will be entered.
+ *
+ * If the label is not found, or the item found is not a map or array,
+ * the error state is set.
+ */
static void SearchAndEnter(QCBORDecodeContext *pMe, QCBORItem pSearch[])
{
// The first item in pSearch is the one that is to be
// entered. It should be the only one filled in. Any other
// will be ignored unless it causes an error.
if(pMe->uLastError != QCBOR_SUCCESS) {
- // Already in error state; do nothing.
return;
}
@@ -2985,23 +3005,28 @@
return;
}
- /* Need to get the current pre-order nesting level and cursor to be
- at the map/array about to be entered.
-
- Also need the current map nesting level and start cursor to
- be at the right place.
-
- The UsefulInBuf offset could be anywhere, so no assumption is
- made about it.
-
- No assumption is made about the pre-order nesting level either.
-
- However the bounded mode nesting level is assumed to be one above
- the map level that is being entered.
+ /*
+ * QCBORDecode_EnterBoundedMapOrArray() used here, requires the
+ * next item for the pre-order traversal cursor to be the map/array
+ * found by MapSearch(). The next few lines of code force the
+ * cursor to that.
+ *
+ * There is no need to retain the old cursor because
+ * QCBORDecode_EnterBoundedMapOrArray() will set it to the
+ * beginning of the map/array being entered.
+ *
+ * The cursor is forced by: 1) setting the input buffer position to
+ * the item offset found by MapSearch(), 2) setting the map/array
+ * counter to the total in the map/array, 3) setting the nesting
+ * level. Setting the map/array counter to the total is not
+ * strictly correct, but this is OK because this cursor only needs
+ * to be used to get one item and MapSearch() has already found it
+ * confirming it exists.
*/
- /* Seek to the data item that is the map or array */
UsefulInputBuf_Seek(&(pMe->InBuf), uOffset);
+ DecodeNesting_ResetMapOrArrayCount(&(pMe->nesting));
+
DecodeNesting_SetCurrentToBoundedLevel(&(pMe->nesting));
QCBORDecode_EnterBoundedMapOrArray(pMe, pSearch->uDataType, NULL);
diff --git a/test/qcbor_decode_tests.c b/test/qcbor_decode_tests.c
index 70fd3b1..6ee22fd 100644
--- a/test/qcbor_decode_tests.c
+++ b/test/qcbor_decode_tests.c
@@ -4647,7 +4647,7 @@
"another int": 98,
"text 2": "lies, damn lies and statistics"
}
- }
+ }
*/
int32_t SpiffyDecodeBasicMap(UsefulBufC input)
@@ -4743,6 +4743,174 @@
return 0;
}
+/*
+ {
+ -75008: h'05083399',
+ 88: [
+ ],
+ 100100: {
+ "sub1": {
+ 10: [
+ 0
+ ],
+ -75009: h'A46823990001',
+ 100100: {
+ "json": "{ \"ueid\", \"xyz\"}",
+ "subsub": {
+ 100002: h'141813191001'
+ }
+ }
+ }
+ }
+ }
+ */
+
+static const uint8_t spNestedCBOR[] = {
+0xa3, 0x3a, 0x00, 0x01, 0x24, 0xff, 0x44, 0x05, 0x08, 0x33, 0x99, 0x18, 0x58, 0x80, 0x1a, 0x00,
+0x01, 0x87, 0x04, 0xa1, 0x64, 0x73, 0x75, 0x62, 0x31, 0xa3, 0x0a, 0x81, 0x00, 0x3a, 0x00, 0x01,
+0x25, 0x00, 0x46, 0xa4, 0x68, 0x23, 0x99, 0x00, 0x01, 0x1a, 0x00, 0x01, 0x87, 0x04, 0xa2, 0x64,
+0x6a, 0x73, 0x6f, 0x6e, 0x70, 0x7b, 0x20, 0x22, 0x75, 0x65, 0x69, 0x64, 0x22, 0x2c, 0x20, 0x22,
+0x78, 0x79, 0x7a, 0x22, 0x7d, 0x66, 0x73, 0x75, 0x62, 0x73, 0x75, 0x62, 0xa1, 0x1a, 0x00, 0x01,
+0x86, 0xa2, 0x46, 0x14, 0x18, 0x13, 0x19, 0x10, 0x01
+};
+
+/* Get item in multi-level nesting in spNestedCBOR */
+static int32_t DecodeNestedGetSubSub(QCBORDecodeContext *pDCtx)
+{
+ UsefulBufC String;
+
+ uint8_t test_oemid_bytes[] = {0x14, 0x18, 0x13, 0x19, 0x10, 0x01};
+ const struct q_useful_buf_c test_oemid = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(test_oemid_bytes);
+
+ QCBORDecode_EnterMapFromMapN(pDCtx, 100100);
+ QCBORDecode_EnterMap(pDCtx, NULL);
+ QCBORDecode_EnterMapFromMapN(pDCtx, 100100);
+ QCBORDecode_EnterMapFromMapSZ(pDCtx, "subsub");
+ QCBORDecode_GetByteStringInMapN(pDCtx, 100002, &String);
+ if(QCBORDecode_GetError(pDCtx)) {
+ return 4001;
+ }
+ if(UsefulBuf_Compare(String, test_oemid)) {
+ return 4002;
+ }
+ QCBORDecode_ExitMap(pDCtx);
+ QCBORDecode_ExitMap(pDCtx);
+ QCBORDecode_ExitMap(pDCtx);
+ QCBORDecode_ExitMap(pDCtx);
+
+ return 0;
+}
+
+/* Iterations on the zero-length array in spNestedCBOR */
+static int32_t DecodeNestedGetEmpty(QCBORDecodeContext *pDCtx)
+{
+ QCBORItem Item;
+ QCBORError uErr;
+
+ QCBORDecode_EnterArrayFromMapN(pDCtx, 88);
+ for(int x = 0; x < 20; x++) {
+ uErr = QCBORDecode_GetNext(pDCtx, &Item);
+ if(uErr != QCBOR_ERR_NO_MORE_ITEMS) {
+ return 4100;
+
+ }
+ }
+ QCBORDecode_ExitArray(pDCtx);
+ if(QCBORDecode_GetError(pDCtx)) {
+ return 4101;
+ }
+
+ return 0;
+}
+
+/* Various iterations on the array that contains a zero in spNestedCBOR */
+static int32_t DecodeNestedGetZero(QCBORDecodeContext *pDCtx)
+{
+ QCBORError uErr;
+
+ QCBORDecode_EnterMapFromMapN(pDCtx, 100100);
+ QCBORDecode_EnterMapFromMapSZ(pDCtx, "sub1");
+ QCBORDecode_EnterArrayFromMapN(pDCtx, 10);
+ int64_t nInt = 99;
+ QCBORDecode_GetInt64(pDCtx, &nInt);
+ if(nInt != 0) {
+ return 4200;
+ }
+ for(int x = 0; x < 20; x++) {
+ QCBORItem Item;
+ uErr = QCBORDecode_GetNext(pDCtx, &Item);
+ if(uErr != QCBOR_ERR_NO_MORE_ITEMS) {
+ return 4201;
+
+ }
+ }
+ QCBORDecode_ExitArray(pDCtx);
+ if(QCBORDecode_GetAndResetError(pDCtx)) {
+ return 4202;
+ }
+ QCBORDecode_EnterArrayFromMapN(pDCtx, 10);
+ UsefulBufC dD;
+ QCBORDecode_GetByteString(pDCtx, &dD);
+ if(QCBORDecode_GetAndResetError(pDCtx) != QCBOR_ERR_UNEXPECTED_TYPE) {
+ return 4203;
+ }
+ for(int x = 0; x < 20; x++) {
+ QCBORDecode_GetByteString(pDCtx, &dD);
+ uErr = QCBORDecode_GetAndResetError(pDCtx);
+ if(uErr != QCBOR_ERR_NO_MORE_ITEMS) {
+ return 4204;
+ }
+ }
+ QCBORDecode_ExitArray(pDCtx);
+ QCBORDecode_ExitMap(pDCtx);
+ QCBORDecode_ExitMap(pDCtx);
+
+ return 0;
+}
+
+/* Repeatedly enter and exit maps and arrays, go off the end of maps
+ and arrays and such. */
+static int32_t DecodeNestedIterate()
+{
+ QCBORDecodeContext DCtx;
+ int32_t nReturn;
+ QCBORError uErr;
+
+ QCBORDecode_Init(&DCtx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spNestedCBOR), 0);
+ QCBORDecode_EnterMap(&DCtx, NULL);
+
+ for(int j = 0; j < 5; j++) {
+ for(int i = 0; i < 20; i++) {
+ nReturn = DecodeNestedGetSubSub(&DCtx);
+ if(nReturn) {
+ return nReturn;
+ }
+ }
+
+ for(int i = 0; i < 20; i++) {
+ nReturn = DecodeNestedGetEmpty(&DCtx);
+ if(nReturn ) {
+ return nReturn;
+ }
+ }
+
+ for(int i = 0; i < 20; i++) {
+ nReturn = DecodeNestedGetZero(&DCtx);
+ if(nReturn ) {
+ return nReturn;
+ }
+ }
+ }
+
+ QCBORDecode_ExitMap(&DCtx);
+ uErr = QCBORDecode_Finish(&DCtx);
+ if(uErr) {
+ return (int32_t)uErr + 4100;
+ }
+
+ return 0;
+}
+
/*
[23,
@@ -5125,7 +5293,9 @@
return 2033;
}
- return 0;
+ nReturn = DecodeNestedIterate();
+
+ return nReturn;
}