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));
|
int objectOffset = reserveSpace(sizeof(QJsonPrivate::Object));
|
||||||
|
if (objectOffset < 0)
|
||||||
|
return false;
|
||||||
BEGIN << "parseObject pos=" << objectOffset << current << json;
|
BEGIN << "parseObject pos=" << objectOffset << current << json;
|
||||||
|
|
||||||
ParsedObject parsedObject(this, objectOffset);
|
ParsedObject parsedObject(this, objectOffset);
|
||||||
@ -417,6 +419,9 @@ bool Parser::parseObject()
|
|||||||
if (parsedObject.offsets.size()) {
|
if (parsedObject.offsets.size()) {
|
||||||
int tableSize = parsedObject.offsets.size()*sizeof(uint);
|
int tableSize = parsedObject.offsets.size()*sizeof(uint);
|
||||||
table = reserveSpace(tableSize);
|
table = reserveSpace(tableSize);
|
||||||
|
if (table < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||||
memcpy(data + table, parsedObject.offsets.constData(), tableSize);
|
memcpy(data + table, parsedObject.offsets.constData(), tableSize);
|
||||||
#else
|
#else
|
||||||
@ -446,6 +451,8 @@ bool Parser::parseObject()
|
|||||||
bool Parser::parseMember(int baseOffset)
|
bool Parser::parseMember(int baseOffset)
|
||||||
{
|
{
|
||||||
int entryOffset = reserveSpace(sizeof(QJsonPrivate::Entry));
|
int entryOffset = reserveSpace(sizeof(QJsonPrivate::Entry));
|
||||||
|
if (entryOffset < 0)
|
||||||
|
return false;
|
||||||
BEGIN << "parseMember pos=" << entryOffset;
|
BEGIN << "parseMember pos=" << entryOffset;
|
||||||
|
|
||||||
bool latin1;
|
bool latin1;
|
||||||
@ -469,6 +476,42 @@ bool Parser::parseMember(int baseOffset)
|
|||||||
return true;
|
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
|
array = begin-array [ value *( value-separator value ) ] end-array
|
||||||
*/
|
*/
|
||||||
@ -482,8 +525,10 @@ bool Parser::parseArray()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int arrayOffset = reserveSpace(sizeof(QJsonPrivate::Array));
|
int arrayOffset = reserveSpace(sizeof(QJsonPrivate::Array));
|
||||||
|
if (arrayOffset < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
QVarLengthArray<QJsonPrivate::Value, 64> values;
|
ValueArray values;
|
||||||
|
|
||||||
if (!eatSpace()) {
|
if (!eatSpace()) {
|
||||||
lastError = QJsonParseError::UnterminatedArray;
|
lastError = QJsonParseError::UnterminatedArray;
|
||||||
@ -496,7 +541,10 @@ bool Parser::parseArray()
|
|||||||
QJsonPrivate::Value val;
|
QJsonPrivate::Value val;
|
||||||
if (!parseValue(&val, arrayOffset))
|
if (!parseValue(&val, arrayOffset))
|
||||||
return false;
|
return false;
|
||||||
values.append(val);
|
if (!values.append(val)) {
|
||||||
|
lastError = QJsonParseError::DocumentTooLarge;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
char token = nextToken();
|
char token = nextToken();
|
||||||
if (token == EndArray)
|
if (token == EndArray)
|
||||||
break;
|
break;
|
||||||
@ -510,20 +558,22 @@ bool Parser::parseArray()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG << "size =" << values.size();
|
DEBUG << "size =" << values.size;
|
||||||
int table = arrayOffset;
|
int table = arrayOffset;
|
||||||
// finalize the object
|
// finalize the object
|
||||||
if (values.size()) {
|
if (values.size) {
|
||||||
int tableSize = values.size()*sizeof(QJsonPrivate::Value);
|
int tableSize = values.size*sizeof(QJsonPrivate::Value);
|
||||||
table = reserveSpace(tableSize);
|
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);
|
QJsonPrivate::Array *a = (QJsonPrivate::Array *)(data + arrayOffset);
|
||||||
a->tableOffset = table - arrayOffset;
|
a->tableOffset = table - arrayOffset;
|
||||||
a->size = current - arrayOffset;
|
a->size = current - arrayOffset;
|
||||||
a->is_object = false;
|
a->is_object = false;
|
||||||
a->length = values.size();
|
a->length = values.size;
|
||||||
|
|
||||||
DEBUG << "current=" << current;
|
DEBUG << "current=" << current;
|
||||||
END;
|
END;
|
||||||
@ -732,6 +782,8 @@ bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int pos = reserveSpace(sizeof(double));
|
int pos = reserveSpace(sizeof(double));
|
||||||
|
if (pos < 0)
|
||||||
|
return false;
|
||||||
qToLittleEndian(ui, reinterpret_cast<uchar *>(data + pos));
|
qToLittleEndian(ui, reinterpret_cast<uchar *>(data + pos));
|
||||||
if (current - baseOffset >= Value::MaxSize) {
|
if (current - baseOffset >= Value::MaxSize) {
|
||||||
lastError = QJsonParseError::DocumentTooLarge;
|
lastError = QJsonParseError::DocumentTooLarge;
|
||||||
@ -850,6 +902,9 @@ bool Parser::parseString(bool *latin1)
|
|||||||
// try to write out a latin1 string
|
// try to write out a latin1 string
|
||||||
|
|
||||||
int stringPos = reserveSpace(2);
|
int stringPos = reserveSpace(2);
|
||||||
|
if (stringPos < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
BEGIN << "parse string stringPos=" << stringPos << json;
|
BEGIN << "parse string stringPos=" << stringPos << json;
|
||||||
while (json < end) {
|
while (json < end) {
|
||||||
uint ch = 0;
|
uint ch = 0;
|
||||||
@ -872,6 +927,8 @@ bool Parser::parseString(bool *latin1)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int pos = reserveSpace(1);
|
int pos = reserveSpace(1);
|
||||||
|
if (pos < 0)
|
||||||
|
return false;
|
||||||
DEBUG << " " << ch << (char)ch;
|
DEBUG << " " << ch << (char)ch;
|
||||||
data[pos] = (uchar)ch;
|
data[pos] = (uchar)ch;
|
||||||
}
|
}
|
||||||
@ -887,6 +944,8 @@ bool Parser::parseString(bool *latin1)
|
|||||||
// write string length
|
// write string length
|
||||||
*(QJsonPrivate::qle_ushort *)(data + stringPos) = ushort(current - outStart - sizeof(ushort));
|
*(QJsonPrivate::qle_ushort *)(data + stringPos) = ushort(current - outStart - sizeof(ushort));
|
||||||
int pos = reserveSpace((4 - current) & 3);
|
int pos = reserveSpace((4 - current) & 3);
|
||||||
|
if (pos < 0)
|
||||||
|
return false;
|
||||||
while (pos & 3)
|
while (pos & 3)
|
||||||
data[pos++] = 0;
|
data[pos++] = 0;
|
||||||
END;
|
END;
|
||||||
@ -916,10 +975,14 @@ bool Parser::parseString(bool *latin1)
|
|||||||
}
|
}
|
||||||
if (QChar::requiresSurrogates(ch)) {
|
if (QChar::requiresSurrogates(ch)) {
|
||||||
int pos = reserveSpace(4);
|
int pos = reserveSpace(4);
|
||||||
|
if (pos < 0)
|
||||||
|
return false;
|
||||||
*(QJsonPrivate::qle_ushort *)(data + pos) = QChar::highSurrogate(ch);
|
*(QJsonPrivate::qle_ushort *)(data + pos) = QChar::highSurrogate(ch);
|
||||||
*(QJsonPrivate::qle_ushort *)(data + pos + 2) = QChar::lowSurrogate(ch);
|
*(QJsonPrivate::qle_ushort *)(data + pos + 2) = QChar::lowSurrogate(ch);
|
||||||
} else {
|
} else {
|
||||||
int pos = reserveSpace(2);
|
int pos = reserveSpace(2);
|
||||||
|
if (pos < 0)
|
||||||
|
return false;
|
||||||
*(QJsonPrivate::qle_ushort *)(data + pos) = (ushort)ch;
|
*(QJsonPrivate::qle_ushort *)(data + pos) = (ushort)ch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -933,6 +996,8 @@ bool Parser::parseString(bool *latin1)
|
|||||||
// write string length
|
// write string length
|
||||||
*(QJsonPrivate::qle_int *)(data + stringPos) = (current - outStart - sizeof(int))/2;
|
*(QJsonPrivate::qle_int *)(data + stringPos) = (current - outStart - sizeof(int))/2;
|
||||||
int pos = reserveSpace((4 - current) & 3);
|
int pos = reserveSpace((4 - current) & 3);
|
||||||
|
if (pos < 0)
|
||||||
|
return false;
|
||||||
while (pos & 3)
|
while (pos & 3)
|
||||||
data[pos++] = 0;
|
data[pos++] = 0;
|
||||||
END;
|
END;
|
||||||
|
@ -102,6 +102,10 @@ private:
|
|||||||
if (current + space >= dataLength) {
|
if (current + space >= dataLength) {
|
||||||
dataLength = 2*dataLength + space;
|
dataLength = 2*dataLength + space;
|
||||||
data = (char *)realloc(data, dataLength);
|
data = (char *)realloc(data, dataLength);
|
||||||
|
if (!data) {
|
||||||
|
lastError = QJsonParseError::DocumentTooLarge;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
int pos = current;
|
int pos = current;
|
||||||
current += space;
|
current += space;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user