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>
This commit is contained in:
parent
4b6784b49c
commit
15df60239d
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user