Skip to content

Commit 755f9ef

Browse files
Parser: validate that maps have both key and value items
If a map end (Break byte) occurs before we've read the concrete item for the value, then the map is invalid. Fixes #167 Signed-off-by: Thiago Macieira <[email protected]>
1 parent 1cc264a commit 755f9ef

File tree

3 files changed

+47
-5
lines changed

3 files changed

+47
-5
lines changed

src/cbor.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/****************************************************************************
22
**
3-
** Copyright (C) 2017 Intel Corporation
3+
** Copyright (C) 2019 Intel Corporation
44
**
55
** Permission is hereby granted, free of charge, to any person obtaining a copy
66
** of this software and associated documentation files (the "Software"), to deal
@@ -270,7 +270,8 @@ enum CborParserIteratorFlags
270270
CborIteratorFlag_NegativeInteger = 0x02,
271271
CborIteratorFlag_IteratingStringChunks = 0x02,
272272
CborIteratorFlag_UnknownLength = 0x04,
273-
CborIteratorFlag_ContainerIsMap = 0x20
273+
CborIteratorFlag_ContainerIsMap = 0x20,
274+
CborIteratorFlag_NextIsMapKey = 0x40
274275
};
275276

276277
struct CborParser

src/cborparser.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ static bool is_fixed_type(uint8_t type)
214214

215215
static CborError preparse_value(CborValue *it)
216216
{
217+
enum {
218+
/* flags to keep */
219+
FlagsToKeep = CborIteratorFlag_ContainerIsMap | CborIteratorFlag_NextIsMapKey
220+
};
217221
const CborParser *parser = it->parser;
218222
it->type = CborInvalidType;
219223

@@ -224,7 +228,7 @@ static CborError preparse_value(CborValue *it)
224228
uint8_t descriptor = *it->ptr;
225229
uint8_t type = descriptor & MajorTypeMask;
226230
it->type = type;
227-
it->flags = 0;
231+
it->flags &= FlagsToKeep;
228232
it->extra = (descriptor &= SmallValueMask);
229233

230234
if (descriptor > Value64Bit) {
@@ -302,6 +306,11 @@ static CborError preparse_next_value_nodecrement(CborValue *it)
302306
{
303307
if (it->remaining == UINT32_MAX && it->ptr != it->parser->end && *it->ptr == (uint8_t)BreakByte) {
304308
/* end of map or array */
309+
if ((it->flags & CborIteratorFlag_ContainerIsMap && it->flags & CborIteratorFlag_NextIsMapKey)
310+
|| it->type == CborTagType) {
311+
/* but we weren't expecting it! */
312+
return CborErrorUnexpectedBreak;
313+
}
305314
++it->ptr;
306315
it->type = CborInvalidType;
307316
it->remaining = 0;
@@ -313,13 +322,20 @@ static CborError preparse_next_value_nodecrement(CborValue *it)
313322

314323
static CborError preparse_next_value(CborValue *it)
315324
{
325+
/* tags don't count towards item totals or whether we've successfully
326+
* read a map's key or value */
327+
bool itemCounts = it->type != CborTagType;
328+
316329
if (it->remaining != UINT32_MAX) {
317-
/* don't decrement the item count if the current item is tag: they don't count */
318-
if (it->type != CborTagType && --it->remaining == 0) {
330+
if (itemCounts && --it->remaining == 0) {
319331
it->type = CborInvalidType;
320332
return CborNoError;
321333
}
322334
}
335+
if (itemCounts) {
336+
/* toggle the flag indicating whether this was a map key */
337+
it->flags ^= CborIteratorFlag_NextIsMapKey;
338+
}
323339
return preparse_next_value_nodecrement(it);
324340
}
325341

@@ -381,6 +397,7 @@ CborError cbor_parser_init(const uint8_t *buffer, size_t size, uint32_t flags, C
381397
it->parser = parser;
382398
it->ptr = buffer;
383399
it->remaining = 1; /* there's one type altogether, usually an array or map */
400+
it->flags = 0;
384401
return preparse_value(it);
385402
}
386403

@@ -586,6 +603,7 @@ CborError cbor_value_skip_tag(CborValue *it)
586603
*/
587604
CborError cbor_value_enter_container(const CborValue *it, CborValue *recursed)
588605
{
606+
cbor_static_assert(CborIteratorFlag_ContainerIsMap == (CborMapType & ~CborArrayType));
589607
cbor_assert(cbor_value_is_container(it));
590608
*recursed = *it;
591609

@@ -618,6 +636,7 @@ CborError cbor_value_enter_container(const CborValue *it, CborValue *recursed)
618636
return CborNoError;
619637
}
620638
}
639+
recursed->flags = (recursed->type & CborIteratorFlag_ContainerIsMap);
621640
return preparse_next_value_nodecrement(recursed);
622641
}
623642

tests/parser/tst_parser.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,6 +1563,10 @@ static void addValidationData()
15631563
QTest::newRow("array-no-break2") << raw("\x81\x9f\0") << 0 << CborErrorUnexpectedEOF;
15641564
QTest::newRow("map-no-break1") << raw("\x81\xbf") << 0 << CborErrorUnexpectedEOF;
15651565
QTest::newRow("map-no-break2") << raw("\x81\xbf\0\0") << 0 << CborErrorUnexpectedEOF;
1566+
QTest::newRow("map-break-after-key") << raw("\x81\xbf\0\xff") << 0 << CborErrorUnexpectedBreak;
1567+
QTest::newRow("map-break-after-second-key") << raw("\x81\xbf\x64xyzw\x04\x00\xff") << 0 << CborErrorUnexpectedBreak;
1568+
QTest::newRow("map-break-after-value-tag") << raw("\x81\xbf\0\xc0\xff") << 0 << CborErrorUnexpectedBreak;
1569+
QTest::newRow("map-break-after-value-tag2") << raw("\x81\xbf\0\xd8\x20\xff") << 0 << CborErrorUnexpectedBreak;
15661570

15671571
// check for pointer additions wrapping over the limit of the address space
15681572
CborError tooLargeOn32bit = (sizeof(void *) == 4) ? CborErrorDataTooLarge : CborErrorUnexpectedEOF;
@@ -1674,6 +1678,24 @@ void tst_Parser::validation()
16741678
QCOMPARE(err2, expectedError);
16751679
QCOMPARE(err3, expectedError);
16761680
}
1681+
1682+
// see if we've got a map
1683+
if (QByteArray(QTest::currentDataTag()).startsWith("map")) {
1684+
w.init(data, uint32_t(flags)); // reinit
1685+
QVERIFY(cbor_value_is_array(&w.first));
1686+
1687+
CborValue map;
1688+
CborError err = cbor_value_enter_container(&w.first, &map);
1689+
if (err == CborNoError) {
1690+
QVERIFY(cbor_value_is_map(&map));
1691+
CborValue element;
1692+
err = cbor_value_map_find_value(&map, "foobar", &element);
1693+
if (err == CborNoError)
1694+
QVERIFY(!cbor_value_is_valid(&element));
1695+
}
1696+
1697+
QCOMPARE(err, expectedError);
1698+
}
16771699
}
16781700

16791701
void tst_Parser::strictValidation_data()

0 commit comments

Comments
 (0)