Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CBOR-to-JSON: Limit how deep we process nested containers #273

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 23 additions & 15 deletions src/cbortojson.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ typedef struct ConversionStatus {
int flags;
} ConversionStatus;

static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status);
static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type,
int nestingLevel, ConversionStatus *status);

static CborError dump_bytestring_base16(char **result, CborValue *it)
{
Expand Down Expand Up @@ -328,11 +329,13 @@ static CborError add_value_metadata(FILE *out, CborType type, const ConversionSt
return CborNoError;
}

static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type)
static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type, int nestingLevel)
{
CborError err = CborNoError;
*type = cbor_value_get_type(it);
while (*type == CborTagType) {
if (nestingLevel-- == 0)
return CborErrorNestingTooDeep;
cbor_value_get_tag(it, tag); /* can't fail */
err = cbor_value_advance_fixed(it);
if (err)
Expand All @@ -343,7 +346,7 @@ static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type)
return err;
}

static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, int nestingLevel, ConversionStatus *status)
{
CborTag tag;
CborError err;
Expand All @@ -358,7 +361,7 @@ static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, Conve
return CborErrorIO;

CborType type = cbor_value_get_type(it);
err = value_to_json(out, it, flags, type, status);
err = value_to_json(out, it, flags, type, nestingLevel, status);
if (err)
return err;
if (flags & CborConvertAddMetadata && status->flags) {
Expand All @@ -374,7 +377,7 @@ static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, Conve
}

CborType type;
err = find_tagged_type(it, &status->lastTag, &type);
err = find_tagged_type(it, &status->lastTag, &type, nestingLevel);
if (err)
return err;
tag = status->lastTag;
Expand Down Expand Up @@ -402,7 +405,7 @@ static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, Conve
}

/* no special handling */
err = value_to_json(out, it, flags, type, status);
err = value_to_json(out, it, flags, type, nestingLevel, status);
status->flags |= TypeWasTagged | type;
return err;
}
Expand All @@ -429,22 +432,22 @@ static CborError stringify_map_key(char **key, CborValue *it, int flags, CborTyp
#endif
}

static CborError array_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
static CborError array_to_json(FILE *out, CborValue *it, int flags, int nestingLevel, ConversionStatus *status)
{
const char *comma = "";
while (!cbor_value_at_end(it)) {
if (fprintf(out, "%s", comma) < 0)
return CborErrorIO;
comma = ",";

CborError err = value_to_json(out, it, flags, cbor_value_get_type(it), status);
CborError err = value_to_json(out, it, flags, cbor_value_get_type(it), nestingLevel, status);
if (err)
return err;
}
return CborNoError;
}

static CborError map_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status)
static CborError map_to_json(FILE *out, CborValue *it, int flags, int nestingLevel, ConversionStatus *status)
{
const char *comma = "";
CborError err;
Expand Down Expand Up @@ -474,7 +477,7 @@ static CborError map_to_json(FILE *out, CborValue *it, int flags, ConversionStat

/* then, print the value */
CborType valueType = cbor_value_get_type(it);
err = value_to_json(out, it, flags, valueType, status);
err = value_to_json(out, it, flags, valueType, nestingLevel, status);

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

static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status)
static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type,
int nestingLevel, ConversionStatus *status)
{
CborError err;
status->flags = 0;

if (nestingLevel == 0)
return CborErrorNestingTooDeep;

switch (type) {
case CborArrayType:
case CborMapType: {
Expand All @@ -516,8 +523,8 @@ static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType typ
return CborErrorIO;

err = (type == CborArrayType) ?
array_to_json(out, &recursed, flags, status) :
map_to_json(out, &recursed, flags, status);
array_to_json(out, &recursed, flags, nestingLevel - 1, status) :
map_to_json(out, &recursed, flags, nestingLevel - 1, status);
if (err) {
copy_current_position(it, &recursed);
return err; /* parse error */
Expand Down Expand Up @@ -574,7 +581,7 @@ static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType typ
}

case CborTagType:
return tagged_value_to_json(out, it, flags, status);
return tagged_value_to_json(out, it, flags, nestingLevel - 1, status);

case CborSimpleType: {
uint8_t simple_type;
Expand Down Expand Up @@ -704,7 +711,8 @@ static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType typ
CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags)
{
ConversionStatus status;
return value_to_json(out, value, flags, cbor_value_get_type(value), &status);
return value_to_json(out, value, flags, cbor_value_get_type(value), CBOR_PARSER_MAX_RECURSIONS,
&status);
}

/** @} */
34 changes: 34 additions & 0 deletions tests/tojson/tst_tojson.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
**
****************************************************************************/

#define __STDC_WANT_IEC_60559_TYPES_EXT__
#include <QtTest>
#include "cbor.h"
#include "cborinternal_p.h"
#include "cborjson.h"
#include <locale.h>

Expand Down Expand Up @@ -73,6 +75,9 @@ private slots:
void metaDataAndTagsToObjects();
void metaDataForKeys_data();
void metaDataForKeys();

void recursionLimit_data();
void recursionLimit();
};
#include "tst_tojson.moc"

Expand Down Expand Up @@ -718,4 +723,33 @@ void tst_ToJson::metaDataForKeys()
CborConvertAddMetadata | CborConvertStringifyMapKeys);
}

void tst_ToJson::recursionLimit_data()
{
static const int recursions = CBOR_PARSER_MAX_RECURSIONS + 2;
QTest::addColumn<QByteArray>("data");

QTest::newRow("arrays") << QByteArray(recursions, '\x81');
QTest::newRow("tags") << QByteArray(recursions, '\xc1');

QByteArray mapData;
mapData.reserve(2 * recursions);
for (int i = 0; i < recursions; ++i)
mapData.append("\xa1\x60", 2);
QTest::newRow("maps") << mapData;
}

void tst_ToJson::recursionLimit()
{
QFETCH(QByteArray, data);
CborParser parser;
CborValue first;
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");

QString parsed;
err = parseOne(&first, &parsed, 0);

QCOMPARE(err, CborErrorNestingTooDeep);
}

QTEST_MAIN(tst_ToJson)
Loading