Index: src/OFDataArray+MessagePackValue.h ================================================================== --- src/OFDataArray+MessagePackValue.h +++ src/OFDataArray+MessagePackValue.h @@ -31,8 +31,17 @@ * @brief Parses the MessagePack representation and returns it as an object. * * @return The MessagePack representation as an object */ - (id)messagePackValue; + +/*! + * @brief Parses the MessagePack representation and returns it as an object. + * + * @param depthLimit The maximum depth the parser should accept (defaults to 32 + * if not specified, 0 means no limit (insecure!)) + * @return The MessagePack representation as an object + */ +- (id)messagePackValueWithDepthLimit: (size_t)depthLimit; @end OF_ASSUME_NONNULL_END Index: src/OFDataArray+MessagePackValue.m ================================================================== --- src/OFDataArray+MessagePackValue.m +++ src/OFDataArray+MessagePackValue.m @@ -27,11 +27,12 @@ #import "OFInvalidFormatException.h" int _OFDataArray_MessagePackValue_reference; -static size_t parseObject(const uint8_t *, size_t, id *); +static size_t parseObject(const uint8_t *buffer, size_t length, id *object, + size_t depthLimit); static uint16_t readUInt16(const uint8_t *buffer) { return ((uint16_t)buffer[0] << 8) | buffer[1]; @@ -52,14 +53,20 @@ ((uint64_t)buffer[4] << 24) | ((uint64_t)buffer[5] << 16) | ((uint64_t)buffer[6] << 8) | buffer[7]; } static size_t -parseArray(const uint8_t *buffer, size_t length, id *object, size_t count) +parseArray(const uint8_t *buffer, size_t length, id *object, size_t count, + size_t depthLimit) { void *pool; size_t pos = 0; + + if (--depthLimit == 0) { + *object = nil; + return 0; + } /* * Don't use capacity! For data and strings, this is safe, as we can * check if we still have enough bytes left. For an array however, we * can't know this, as every child can be more than one byte. @@ -70,11 +77,12 @@ id child; size_t childLength; pool = objc_autoreleasePoolPush(); - childLength = parseObject(buffer + pos, length - pos, &child); + childLength = parseObject(buffer + pos, length - pos, &child, + depthLimit); if (childLength == 0 || child == nil) { objc_autoreleasePoolPop(pool); *object = nil; return 0; @@ -88,14 +96,20 @@ return pos; } static size_t -parseTable(const uint8_t *buffer, size_t length, id *object, size_t count) +parseTable(const uint8_t *buffer, size_t length, id *object, size_t count, + size_t depthLimit) { void *pool; size_t pos = 0; + + if (--depthLimit == 0) { + *object = nil; + return 0; + } /* * Don't use capacity! For data and strings, this is safe, as we can * check if we still have enough bytes left. For a dictionary however, * we can't know this, as every key / value can be more than one byte. @@ -106,20 +120,22 @@ id key, value; size_t keyLength, valueLength; pool = objc_autoreleasePoolPush(); - keyLength = parseObject(buffer + pos, length - pos, &key); + keyLength = parseObject(buffer + pos, length - pos, &key, + depthLimit); if (keyLength == 0 || key == nil) { objc_autoreleasePoolPop(pool); *object = nil; return 0; } pos += keyLength; - valueLength = parseObject(buffer + pos, length - pos, &value); + valueLength = parseObject(buffer + pos, length - pos, &value, + depthLimit); if (valueLength == 0 || value == nil) { objc_autoreleasePoolPop(pool); *object = nil; return 0; @@ -134,11 +150,12 @@ return pos; } static size_t -parseObject(const uint8_t *buffer, size_t length, id *object) +parseObject(const uint8_t *buffer, size_t length, id *object, + size_t depthLimit) { size_t count; int8_t type; OFDataArray *data; @@ -171,16 +188,16 @@ } /* fixarray */ if ((buffer[0] & 0xF0) == 0x90) return parseArray(buffer + 1, length - 1, object, - buffer[0] & 0xF) + 1; + buffer[0] & 0xF, depthLimit) + 1; /* fixmap */ if ((buffer[0] & 0xF0) == 0x80) return parseTable(buffer + 1, length - 1, object, - buffer[0] & 0xF) + 1; + buffer[0] & 0xF, depthLimit) + 1; /* Prefix byte */ switch (*buffer) { /* Unsigned integers */ case 0xCC: /* uint8 */ @@ -425,11 +442,11 @@ } @finally { [data release]; } return 4; - case 0xD6: /* fixtext 4 */ + case 0xD6: /* fixext 4 */ if (length < 6) goto error; type = buffer[1]; @@ -444,11 +461,11 @@ } @finally { [data release]; } return 6; - case 0xD7: /* fixtext 8 */ + case 0xD7: /* fixext 8 */ if (length < 10) goto error; type = buffer[1]; @@ -528,30 +545,30 @@ case 0xDC: /* array 16 */ if (length < 3) goto error; return parseArray(buffer + 3, length - 3, object, - readUInt16(buffer + 1)) + 3; + readUInt16(buffer + 1), depthLimit) + 3; case 0xDD: /* array 32 */ if (length < 5) goto error; return parseArray(buffer + 5, length - 5, object, - readUInt32(buffer + 1)) + 5; + readUInt32(buffer + 1), depthLimit) + 5; /* Maps */ case 0xDE: /* map 16 */ if (length < 3) goto error; return parseTable(buffer + 3, length - 3, object, - readUInt16(buffer + 1)) + 3; + readUInt16(buffer + 1), depthLimit) + 3; case 0xDF: /* map 32 */ if (length < 5) goto error; return parseTable(buffer + 5, length - 5, object, - readUInt32(buffer + 1)) + 5; + readUInt32(buffer + 1), depthLimit) + 5; } error: *object = nil; return 0; @@ -558,15 +575,25 @@ } @implementation OFDataArray (MessagePackValue) - (id)messagePackValue { + return [self messagePackValueWithDepthLimit: 32]; +} + +- (id)messagePackValueWithDepthLimit: (size_t)depthLimit +{ + void *pool = objc_autoreleasePoolPush(); size_t count = [self count]; id object; - if (parseObject([self items], count, &object) != count || + if (parseObject([self items], count, &object, depthLimit) != count || object == nil) @throw [OFInvalidFormatException exception]; + [object retain]; + + objc_autoreleasePoolPop(pool); + return object; } @end Index: src/OFString+JSONValue.m ================================================================== --- src/OFString+JSONValue.m +++ src/OFString+JSONValue.m @@ -32,11 +32,11 @@ #import "OFInvalidJSONException.h" int _OFString_JSONValue_reference; static id nextObject(const char **pointer, const char *stop, size_t *line, - size_t depth, size_t depthLimit); + size_t depthLimit); static void skipWhitespaces(const char **pointer, const char *stop, size_t *line) { while (*pointer < stop && (**pointer == ' ' || **pointer == '\t' || @@ -388,18 +388,18 @@ return nil; } static inline OFMutableArray * parseArray(const char **pointer, const char *stop, size_t *line, - size_t depth, size_t depthLimit) + size_t depthLimit) { OFMutableArray *array = [OFMutableArray array]; if (++(*pointer) >= stop) return nil; - if (++depth > depthLimit) + if (--depthLimit == 0) return nil; while (**pointer != ']') { id object; @@ -418,11 +418,11 @@ return nil; break; } - object = nextObject(pointer, stop, line, depth, depthLimit); + object = nextObject(pointer, stop, line, depthLimit); if (object == nil) return nil; [array addObject: object]; @@ -445,18 +445,18 @@ return array; } static inline OFMutableDictionary * parseDictionary(const char **pointer, const char *stop, size_t *line, - size_t depth, size_t depthLimit) + size_t depthLimit) { OFMutableDictionary *dictionary = [OFMutableDictionary dictionary]; if (++(*pointer) >= stop) return nil; - if (++depth > depthLimit) + if (--depthLimit == 0) return nil; while (**pointer != '}') { id key, object; @@ -484,12 +484,11 @@ if ((**pointer >= 'a' && **pointer <= 'z') || (**pointer >= 'A' && **pointer <= 'Z') || **pointer == '_' || **pointer == '$' || **pointer == '\\') key = parseIdentifier(pointer, stop); else - key = nextObject(pointer, stop, line, - depth, depthLimit); + key = nextObject(pointer, stop, line, depthLimit); if (key == nil) return nil; skipWhitespacesAndComments(pointer, stop, line); @@ -496,11 +495,11 @@ if (*pointer + 1 >= stop || **pointer != ':') return nil; (*pointer)++; - object = nextObject(pointer, stop, line, depth, depthLimit); + object = nextObject(pointer, stop, line, depthLimit); if (object == nil) return nil; [dictionary setObject: object forKey: key]; @@ -573,11 +572,11 @@ return number; } static id nextObject(const char **pointer, const char *stop, size_t *line, - size_t depth, size_t depthLimit) + size_t depthLimit) { skipWhitespacesAndComments(pointer, stop, line); if (*pointer >= stop) return nil; @@ -585,13 +584,13 @@ switch (**pointer) { case '"': case '\'': return parseString(pointer, stop, line); case '[': - return parseArray(pointer, stop, line, depth, depthLimit); + return parseArray(pointer, stop, line, depthLimit); case '{': - return parseDictionary(pointer, stop, line, depth, depthLimit); + return parseDictionary(pointer, stop, line, depthLimit); case 't': if (*pointer + 3 >= stop) return nil; if (memcmp(*pointer, "true", 4) != 0) @@ -652,11 +651,11 @@ const char *pointer = [self UTF8String]; const char *stop = pointer + [self UTF8StringLength]; id object; size_t line = 1; - object = nextObject(&pointer, stop, &line, 0, depthLimit); + object = nextObject(&pointer, stop, &line, depthLimit); skipWhitespacesAndComments(&pointer, stop, &line); if (pointer < stop || object == nil) @throw [OFInvalidJSONException exceptionWithString: self line: line]; Index: tests/OFJSONTests.m ================================================================== --- tests/OFJSONTests.m +++ tests/OFJSONTests.m @@ -66,9 +66,33 @@ [@"]" JSONValue]) EXPECT_EXCEPTION(@"-[JSONValue] #4", OFInvalidJSONException, [@"bar" JSONValue]) EXPECT_EXCEPTION(@"-[JSONValue] #5", OFInvalidJSONException, [@"[\"a\" \"b\"]" JSONValue]) + + TEST(@"-[JSONValue] #6", + [[@"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + JSONValue] isEqual: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: [OFArray arrayWithObject: + [OFArray arrayWithObject: + [OFDictionary dictionary]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]) + + EXPECT_EXCEPTION(@"-[JSONValue] #7", OFInvalidJSONException, + [@"[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[{}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]" + JSONValue]) [pool drain]; } @end