Skip to content

Commit e692445

Browse files
CBOR-to-JSON: Limit how deep we process nested containers
1024 levels will probably be good enough for everyone, like cborparser.c. For those for whom it isn't, they can set the limit during the build. We already had this for the plain parser, so TinyCBOR wouldn't cause a stack overflow in case of a malformed stream (intentionally or not) when simply parsing and advancing over the stream. This same protection wasn't applied to the content converting from CBOR to JSON. Signed-off-by: Thiago Macieira <[email protected]>
1 parent 5bdc6ea commit e692445

File tree

2 files changed

+57
-15
lines changed

2 files changed

+57
-15
lines changed

src/cbortojson.c

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ typedef struct ConversionStatus {
167167
int flags;
168168
} ConversionStatus;
169169

170-
static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status);
170+
static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type,
171+
int nestingLevel, ConversionStatus *status);
171172

172173
static CborError dump_bytestring_base16(char **result, CborValue *it)
173174
{
@@ -328,11 +329,13 @@ static CborError add_value_metadata(FILE *out, CborType type, const ConversionSt
328329
return CborNoError;
329330
}
330331

331-
static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type)
332+
static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type, int nestingLevel)
332333
{
333334
CborError err = CborNoError;
334335
*type = cbor_value_get_type(it);
335336
while (*type == CborTagType) {
337+
if (nestingLevel-- == 0)
338+
return CborErrorNestingTooDeep;
336339
cbor_value_get_tag(it, tag); /* can't fail */
337340
err = cbor_value_advance_fixed(it);
338341
if (err)
@@ -343,7 +346,7 @@ static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type)
343346
return err;
344347
}
345348

346-
static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
349+
static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, int nestingLevel, ConversionStatus *status)
347350
{
348351
CborTag tag;
349352
CborError err;
@@ -358,7 +361,7 @@ static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, Conve
358361
return CborErrorIO;
359362

360363
CborType type = cbor_value_get_type(it);
361-
err = value_to_json(out, it, flags, type, status);
364+
err = value_to_json(out, it, flags, type, nestingLevel, status);
362365
if (err)
363366
return err;
364367
if (flags & CborConvertAddMetadata && status->flags) {
@@ -374,7 +377,7 @@ static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, Conve
374377
}
375378

376379
CborType type;
377-
err = find_tagged_type(it, &status->lastTag, &type);
380+
err = find_tagged_type(it, &status->lastTag, &type, nestingLevel);
378381
if (err)
379382
return err;
380383
tag = status->lastTag;
@@ -402,7 +405,7 @@ static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, Conve
402405
}
403406

404407
/* no special handling */
405-
err = value_to_json(out, it, flags, type, status);
408+
err = value_to_json(out, it, flags, type, nestingLevel, status);
406409
status->flags |= TypeWasTagged | type;
407410
return err;
408411
}
@@ -429,22 +432,22 @@ static CborError stringify_map_key(char **key, CborValue *it, int flags, CborTyp
429432
#endif
430433
}
431434

432-
static CborError array_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
435+
static CborError array_to_json(FILE *out, CborValue *it, int flags, int nestingLevel, ConversionStatus *status)
433436
{
434437
const char *comma = "";
435438
while (!cbor_value_at_end(it)) {
436439
if (fprintf(out, "%s", comma) < 0)
437440
return CborErrorIO;
438441
comma = ",";
439442

440-
CborError err = value_to_json(out, it, flags, cbor_value_get_type(it), status);
443+
CborError err = value_to_json(out, it, flags, cbor_value_get_type(it), nestingLevel, status);
441444
if (err)
442445
return err;
443446
}
444447
return CborNoError;
445448
}
446449

447-
static CborError map_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
450+
static CborError map_to_json(FILE *out, CborValue *it, int flags, int nestingLevel, ConversionStatus *status)
448451
{
449452
const char *comma = "";
450453
CborError err;
@@ -474,7 +477,7 @@ static CborError map_to_json(FILE *out, CborValue *it, int flags, ConversionStat
474477

475478
/* then, print the value */
476479
CborType valueType = cbor_value_get_type(it);
477-
err = value_to_json(out, it, flags, valueType, status);
480+
err = value_to_json(out, it, flags, valueType, nestingLevel, status);
478481

479482
/* finally, print any metadata we may have */
480483
if (flags & CborConvertAddMetadata) {
@@ -497,11 +500,15 @@ static CborError map_to_json(FILE *out, CborValue *it, int flags, ConversionStat
497500
return CborNoError;
498501
}
499502

500-
static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status)
503+
static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type,
504+
int nestingLevel, ConversionStatus *status)
501505
{
502506
CborError err;
503507
status->flags = 0;
504508

509+
if (nestingLevel == 0)
510+
return CborErrorNestingTooDeep;
511+
505512
switch (type) {
506513
case CborArrayType:
507514
case CborMapType: {
@@ -516,8 +523,8 @@ static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType typ
516523
return CborErrorIO;
517524

518525
err = (type == CborArrayType) ?
519-
array_to_json(out, &recursed, flags, status) :
520-
map_to_json(out, &recursed, flags, status);
526+
array_to_json(out, &recursed, flags, nestingLevel - 1, status) :
527+
map_to_json(out, &recursed, flags, nestingLevel - 1, status);
521528
if (err) {
522529
copy_current_position(it, &recursed);
523530
return err; /* parse error */
@@ -574,7 +581,7 @@ static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType typ
574581
}
575582

576583
case CborTagType:
577-
return tagged_value_to_json(out, it, flags, status);
584+
return tagged_value_to_json(out, it, flags, nestingLevel - 1, status);
578585

579586
case CborSimpleType: {
580587
uint8_t simple_type;
@@ -704,7 +711,8 @@ static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType typ
704711
CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags)
705712
{
706713
ConversionStatus status;
707-
return value_to_json(out, value, flags, cbor_value_get_type(value), &status);
714+
return value_to_json(out, value, flags, cbor_value_get_type(value), CBOR_PARSER_MAX_RECURSIONS,
715+
&status);
708716
}
709717

710718
/** @} */

tests/tojson/tst_tojson.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
**
2323
****************************************************************************/
2424

25+
#define __STDC_WANT_IEC_60559_TYPES_EXT__
2526
#include <QtTest>
2627
#include "cbor.h"
28+
#include "cborinternal_p.h"
2729
#include "cborjson.h"
2830
#include <locale.h>
2931

@@ -73,6 +75,9 @@ private slots:
7375
void metaDataAndTagsToObjects();
7476
void metaDataForKeys_data();
7577
void metaDataForKeys();
78+
79+
void recursionLimit_data();
80+
void recursionLimit();
7681
};
7782
#include "tst_tojson.moc"
7883

@@ -718,4 +723,33 @@ void tst_ToJson::metaDataForKeys()
718723
CborConvertAddMetadata | CborConvertStringifyMapKeys);
719724
}
720725

726+
void tst_ToJson::recursionLimit_data()
727+
{
728+
static const int recursions = CBOR_PARSER_MAX_RECURSIONS + 2;
729+
QTest::addColumn<QByteArray>("data");
730+
731+
QTest::newRow("arrays") << QByteArray(recursions, '\x81');
732+
QTest::newRow("tags") << QByteArray(recursions, '\xc1');
733+
734+
QByteArray mapData;
735+
mapData.reserve(2 * recursions);
736+
for (int i = 0; i < recursions; ++i)
737+
mapData.append("\xa1\x60", 2);
738+
QTest::newRow("maps") << mapData;
739+
}
740+
741+
void tst_ToJson::recursionLimit()
742+
{
743+
QFETCH(QByteArray, data);
744+
CborParser parser;
745+
CborValue first;
746+
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
747+
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
748+
749+
QString parsed;
750+
err = parseOne(&first, &parsed, 0);
751+
752+
QCOMPARE(err, CborErrorNestingTooDeep);
753+
}
754+
721755
QTEST_MAIN(tst_ToJson)

0 commit comments

Comments
 (0)