diff options
author | Lars Knoll <lars.knoll@qt.io> | 2016-11-02 09:37:42 +0100 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2016-11-04 07:53:39 +0000 |
commit | 15df60239d2dd3b0f0844e3ec8c91300fb7a4b67 (patch) | |
tree | e0c863154de5e1766ffb1310711d36a63b6283e6 /src/corelib/json | |
parent | 4b6784b49c6dcf0add9ec0cbb4ad97cd191c2aa3 (diff) |
Fix OOM crashes for huge json documents
Check all places where we reallocate our internal data structure
and return a DocumentTooLarge parse error if we can't get enough
memory.
Change-Id: I006d0170d941837220c7dad0508571b68e2cbfd7
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Kati Kankaanpaa <kati.kankaanpaa@qt.io>
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Diffstat (limited to 'src/corelib/json')
-rw-r--r-- | src/corelib/json/qjsonparser.cpp | 79 | ||||
-rw-r--r-- | src/corelib/json/qjsonparser_p.h | 4 |
2 files changed, 76 insertions, 7 deletions
diff --git a/src/corelib/json/qjsonparser.cpp b/src/corelib/json/qjsonparser.cpp index b8a628fdcc..c7b16d5ec9 100644 --- a/src/corelib/json/qjsonparser.cpp +++ b/src/corelib/json/qjsonparser.cpp @@ -385,6 +385,8 @@ bool Parser::parseObject() } int objectOffset = reserveSpace(sizeof(QJsonPrivate::Object)); + if (objectOffset < 0) + return false; BEGIN << "parseObject pos=" << objectOffset << current << json; ParsedObject parsedObject(this, objectOffset); @@ -417,6 +419,9 @@ bool Parser::parseObject() if (parsedObject.offsets.size()) { int tableSize = parsedObject.offsets.size()*sizeof(uint); table = reserveSpace(tableSize); + if (table < 0) + return false; + #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN memcpy(data + table, parsedObject.offsets.constData(), tableSize); #else @@ -446,6 +451,8 @@ bool Parser::parseObject() bool Parser::parseMember(int baseOffset) { int entryOffset = reserveSpace(sizeof(QJsonPrivate::Entry)); + if (entryOffset < 0) + return false; BEGIN << "parseMember pos=" << entryOffset; bool latin1; @@ -469,6 +476,42 @@ bool Parser::parseMember(int baseOffset) return true; } +namespace { + struct ValueArray { + static const int prealloc = 128; + ValueArray() : data(stackValues), alloc(prealloc), size(0) {} + ~ValueArray() { if (data != stackValues) free(data); } + + inline bool grow() { + alloc *= 2; + if (data == stackValues) { + QJsonPrivate::Value *newValues = static_cast<QJsonPrivate::Value *>(malloc(alloc*sizeof(QJsonPrivate::Value))); + if (!newValues) + return false; + memcpy(newValues, data, size*sizeof(QJsonPrivate::Value)); + data = newValues; + } else { + data = static_cast<QJsonPrivate::Value *>(realloc(data, alloc*sizeof(QJsonPrivate::Value))); + if (!data) + return false; + } + return true; + } + bool append(const QJsonPrivate::Value &v) { + if (alloc == size && !grow()) + return false; + data[size] = v; + ++size; + return true; + } + + QJsonPrivate::Value stackValues[prealloc]; + QJsonPrivate::Value *data; + int alloc; + int size; + }; +} + /* array = begin-array [ value *( value-separator value ) ] end-array */ @@ -482,8 +525,10 @@ bool Parser::parseArray() } int arrayOffset = reserveSpace(sizeof(QJsonPrivate::Array)); + if (arrayOffset < 0) + return false; - QVarLengthArray<QJsonPrivate::Value, 64> values; + ValueArray values; if (!eatSpace()) { lastError = QJsonParseError::UnterminatedArray; @@ -496,7 +541,10 @@ bool Parser::parseArray() QJsonPrivate::Value val; if (!parseValue(&val, arrayOffset)) return false; - values.append(val); + if (!values.append(val)) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } char token = nextToken(); if (token == EndArray) break; @@ -510,20 +558,22 @@ bool Parser::parseArray() } } - DEBUG << "size =" << values.size(); + DEBUG << "size =" << values.size; int table = arrayOffset; // finalize the object - if (values.size()) { - int tableSize = values.size()*sizeof(QJsonPrivate::Value); + if (values.size) { + int tableSize = values.size*sizeof(QJsonPrivate::Value); table = reserveSpace(tableSize); - memcpy(data + table, values.constData(), tableSize); + if (table < 0) + return false; + memcpy(data + table, values.data, tableSize); } QJsonPrivate::Array *a = (QJsonPrivate::Array *)(data + arrayOffset); a->tableOffset = table - arrayOffset; a->size = current - arrayOffset; a->is_object = false; - a->length = values.size(); + a->length = values.size; DEBUG << "current=" << current; END; @@ -732,6 +782,8 @@ bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset) } int pos = reserveSpace(sizeof(double)); + if (pos < 0) + return false; qToLittleEndian(ui, reinterpret_cast<uchar *>(data + pos)); if (current - baseOffset >= Value::MaxSize) { lastError = QJsonParseError::DocumentTooLarge; @@ -850,6 +902,9 @@ bool Parser::parseString(bool *latin1) // try to write out a latin1 string int stringPos = reserveSpace(2); + if (stringPos < 0) + return false; + BEGIN << "parse string stringPos=" << stringPos << json; while (json < end) { uint ch = 0; @@ -872,6 +927,8 @@ bool Parser::parseString(bool *latin1) break; } int pos = reserveSpace(1); + if (pos < 0) + return false; DEBUG << " " << ch << (char)ch; data[pos] = (uchar)ch; } @@ -887,6 +944,8 @@ bool Parser::parseString(bool *latin1) // write string length *(QJsonPrivate::qle_ushort *)(data + stringPos) = ushort(current - outStart - sizeof(ushort)); int pos = reserveSpace((4 - current) & 3); + if (pos < 0) + return false; while (pos & 3) data[pos++] = 0; END; @@ -916,10 +975,14 @@ bool Parser::parseString(bool *latin1) } if (QChar::requiresSurrogates(ch)) { int pos = reserveSpace(4); + if (pos < 0) + return false; *(QJsonPrivate::qle_ushort *)(data + pos) = QChar::highSurrogate(ch); *(QJsonPrivate::qle_ushort *)(data + pos + 2) = QChar::lowSurrogate(ch); } else { int pos = reserveSpace(2); + if (pos < 0) + return false; *(QJsonPrivate::qle_ushort *)(data + pos) = (ushort)ch; } } @@ -933,6 +996,8 @@ bool Parser::parseString(bool *latin1) // write string length *(QJsonPrivate::qle_int *)(data + stringPos) = (current - outStart - sizeof(int))/2; int pos = reserveSpace((4 - current) & 3); + if (pos < 0) + return false; while (pos & 3) data[pos++] = 0; END; diff --git a/src/corelib/json/qjsonparser_p.h b/src/corelib/json/qjsonparser_p.h index a395c0c92e..82a7899a51 100644 --- a/src/corelib/json/qjsonparser_p.h +++ b/src/corelib/json/qjsonparser_p.h @@ -102,6 +102,10 @@ private: if (current + space >= dataLength) { dataLength = 2*dataLength + space; data = (char *)realloc(data, dataLength); + if (!data) { + lastError = QJsonParseError::DocumentTooLarge; + return -1; + } } int pos = current; current += space; |