Fix crashes when creating large documents
Compact an object in regular intervals when inserting data into it, to avoid the object becoming huge. Compact an object/array before inserting into another array or object. Check that the document doesn't get so big it's overflowing the internal data structures. Task-number: QTBUG-29288 Change-Id: Id39d80dac1e7d5a11f40819f41b4b336bce16947 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
9cc106d9d7
commit
e1d3687d64
@ -149,6 +149,10 @@ bool Data::valid() const
|
|||||||
int Base::reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace)
|
int Base::reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace)
|
||||||
{
|
{
|
||||||
Q_ASSERT(posInTable >= 0 && posInTable <= (int)length);
|
Q_ASSERT(posInTable >= 0 && posInTable <= (int)length);
|
||||||
|
if (size + dataSize >= Value::MaxSize) {
|
||||||
|
qWarning("QJson: Document too large to store in data structure %d %d %d", (uint)size, dataSize, Value::MaxSize);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
offset off = tableOffset;
|
offset off = tableOffset;
|
||||||
// move table to new position
|
// move table to new position
|
||||||
@ -334,7 +338,7 @@ bool Value::isValid(const Base *b) const
|
|||||||
/*!
|
/*!
|
||||||
\internal
|
\internal
|
||||||
*/
|
*/
|
||||||
int Value::requiredStorage(const QJsonValue &v, bool *compressed)
|
int Value::requiredStorage(QJsonValue &v, bool *compressed)
|
||||||
{
|
{
|
||||||
*compressed = false;
|
*compressed = false;
|
||||||
switch (v.t) {
|
switch (v.t) {
|
||||||
@ -351,6 +355,11 @@ int Value::requiredStorage(const QJsonValue &v, bool *compressed)
|
|||||||
}
|
}
|
||||||
case QJsonValue::Array:
|
case QJsonValue::Array:
|
||||||
case QJsonValue::Object:
|
case QJsonValue::Object:
|
||||||
|
if (v.d && v.d->compactionCounter) {
|
||||||
|
v.detach();
|
||||||
|
v.d->compact();
|
||||||
|
v.base = static_cast<QJsonPrivate::Base *>(v.d->header->root());
|
||||||
|
}
|
||||||
return v.base ? v.base->size : sizeof(QJsonPrivate::Base);
|
return v.base ? v.base->size : sizeof(QJsonPrivate::Base);
|
||||||
case QJsonValue::Undefined:
|
case QJsonValue::Undefined:
|
||||||
case QJsonValue::Null:
|
case QJsonValue::Null:
|
||||||
|
@ -543,6 +543,9 @@ public:
|
|||||||
class Value
|
class Value
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum {
|
||||||
|
MaxSize = (1<<27) - 1
|
||||||
|
};
|
||||||
union {
|
union {
|
||||||
uint _dummy;
|
uint _dummy;
|
||||||
qle_bitfield<0, 3> type;
|
qle_bitfield<0, 3> type;
|
||||||
@ -564,7 +567,7 @@ public:
|
|||||||
|
|
||||||
bool isValid(const Base *b) const;
|
bool isValid(const Base *b) const;
|
||||||
|
|
||||||
static int requiredStorage(const QJsonValue &v, bool *compressed);
|
static int requiredStorage(QJsonValue &v, bool *compressed);
|
||||||
static uint valueToStore(const QJsonValue &v, uint offset);
|
static uint valueToStore(const QJsonValue &v, uint offset);
|
||||||
static void copyData(const QJsonValue &v, char *dest, bool compressed);
|
static void copyData(const QJsonValue &v, char *dest, bool compressed);
|
||||||
};
|
};
|
||||||
|
@ -391,9 +391,10 @@ QJsonValue QJsonArray::takeAt(int i)
|
|||||||
void QJsonArray::insert(int i, const QJsonValue &value)
|
void QJsonArray::insert(int i, const QJsonValue &value)
|
||||||
{
|
{
|
||||||
Q_ASSERT (i >= 0 && i <= (a ? (int)a->length : 0));
|
Q_ASSERT (i >= 0 && i <= (a ? (int)a->length : 0));
|
||||||
|
QJsonValue val = value;
|
||||||
|
|
||||||
bool compressed;
|
bool compressed;
|
||||||
int valueSize = QJsonPrivate::Value::requiredStorage(value, &compressed);
|
int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed);
|
||||||
|
|
||||||
detach(valueSize + sizeof(QJsonPrivate::Value));
|
detach(valueSize + sizeof(QJsonPrivate::Value));
|
||||||
|
|
||||||
@ -401,13 +402,16 @@ void QJsonArray::insert(int i, const QJsonValue &value)
|
|||||||
a->tableOffset = sizeof(QJsonPrivate::Array);
|
a->tableOffset = sizeof(QJsonPrivate::Array);
|
||||||
|
|
||||||
int valueOffset = a->reserveSpace(valueSize, i, 1, false);
|
int valueOffset = a->reserveSpace(valueSize, i, 1, false);
|
||||||
|
if (!valueOffset)
|
||||||
|
return;
|
||||||
|
|
||||||
QJsonPrivate::Value &v = (*a)[i];
|
QJsonPrivate::Value &v = (*a)[i];
|
||||||
v.type = (value.t == QJsonValue::Undefined ? QJsonValue::Null : value.t);
|
v.type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t);
|
||||||
v.latinOrIntValue = compressed;
|
v.latinOrIntValue = compressed;
|
||||||
v.latinKey = false;
|
v.latinKey = false;
|
||||||
v.value = QJsonPrivate::Value::valueToStore(value, valueOffset);
|
v.value = QJsonPrivate::Value::valueToStore(val, valueOffset);
|
||||||
if (valueSize)
|
if (valueSize)
|
||||||
QJsonPrivate::Value::copyData(value, (char *)a + valueOffset, compressed);
|
QJsonPrivate::Value::copyData(val, (char *)a + valueOffset, compressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -437,9 +441,10 @@ void QJsonArray::insert(int i, const QJsonValue &value)
|
|||||||
void QJsonArray::replace(int i, const QJsonValue &value)
|
void QJsonArray::replace(int i, const QJsonValue &value)
|
||||||
{
|
{
|
||||||
Q_ASSERT (a && i >= 0 && i < (int)(a->length));
|
Q_ASSERT (a && i >= 0 && i < (int)(a->length));
|
||||||
|
QJsonValue val = value;
|
||||||
|
|
||||||
bool compressed;
|
bool compressed;
|
||||||
int valueSize = QJsonPrivate::Value::requiredStorage(value, &compressed);
|
int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed);
|
||||||
|
|
||||||
detach(valueSize);
|
detach(valueSize);
|
||||||
|
|
||||||
@ -447,13 +452,16 @@ void QJsonArray::replace(int i, const QJsonValue &value)
|
|||||||
a->tableOffset = sizeof(QJsonPrivate::Array);
|
a->tableOffset = sizeof(QJsonPrivate::Array);
|
||||||
|
|
||||||
int valueOffset = a->reserveSpace(valueSize, i, 1, true);
|
int valueOffset = a->reserveSpace(valueSize, i, 1, true);
|
||||||
|
if (!valueOffset)
|
||||||
|
return;
|
||||||
|
|
||||||
QJsonPrivate::Value &v = (*a)[i];
|
QJsonPrivate::Value &v = (*a)[i];
|
||||||
v.type = (value.t == QJsonValue::Undefined ? QJsonValue::Null : value.t);
|
v.type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t);
|
||||||
v.latinOrIntValue = compressed;
|
v.latinOrIntValue = compressed;
|
||||||
v.latinKey = false;
|
v.latinKey = false;
|
||||||
v.value = QJsonPrivate::Value::valueToStore(value, valueOffset);
|
v.value = QJsonPrivate::Value::valueToStore(val, valueOffset);
|
||||||
if (valueSize)
|
if (valueSize)
|
||||||
QJsonPrivate::Value::copyData(value, (char *)a + valueOffset, compressed);
|
QJsonPrivate::Value::copyData(val, (char *)a + valueOffset, compressed);
|
||||||
|
|
||||||
++d->compactionCounter;
|
++d->compactionCounter;
|
||||||
if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u)
|
if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u)
|
||||||
|
@ -67,7 +67,8 @@ struct Q_CORE_EXPORT QJsonParseError
|
|||||||
IllegalUTF8String,
|
IllegalUTF8String,
|
||||||
UnterminatedString,
|
UnterminatedString,
|
||||||
MissingObject,
|
MissingObject,
|
||||||
DeepNesting
|
DeepNesting,
|
||||||
|
DocumentTooLarge
|
||||||
};
|
};
|
||||||
|
|
||||||
QString errorString() const;
|
QString errorString() const;
|
||||||
|
@ -317,9 +317,10 @@ QJsonObject::iterator QJsonObject::insert(const QString &key, const QJsonValue &
|
|||||||
remove(key);
|
remove(key);
|
||||||
return end();
|
return end();
|
||||||
}
|
}
|
||||||
|
QJsonValue val = value;
|
||||||
|
|
||||||
bool latinOrIntValue;
|
bool latinOrIntValue;
|
||||||
int valueSize = QJsonPrivate::Value::requiredStorage(value, &latinOrIntValue);
|
int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue);
|
||||||
|
|
||||||
bool latinKey = QJsonPrivate::useCompressed(key);
|
bool latinKey = QJsonPrivate::useCompressed(key);
|
||||||
int valueOffset = sizeof(QJsonPrivate::Entry) + QJsonPrivate::qStringSize(key, latinKey);
|
int valueOffset = sizeof(QJsonPrivate::Entry) + QJsonPrivate::qStringSize(key, latinKey);
|
||||||
@ -335,16 +336,21 @@ QJsonObject::iterator QJsonObject::insert(const QString &key, const QJsonValue &
|
|||||||
if (keyExists)
|
if (keyExists)
|
||||||
++d->compactionCounter;
|
++d->compactionCounter;
|
||||||
|
|
||||||
o->reserveSpace(requiredSize, pos, 1, keyExists);
|
uint off = o->reserveSpace(requiredSize, pos, 1, keyExists);
|
||||||
|
if (!off)
|
||||||
|
return end();
|
||||||
|
|
||||||
QJsonPrivate::Entry *e = o->entryAt(pos);
|
QJsonPrivate::Entry *e = o->entryAt(pos);
|
||||||
e->value.type = value.t;
|
e->value.type = val.t;
|
||||||
e->value.latinKey = latinKey;
|
e->value.latinKey = latinKey;
|
||||||
e->value.latinOrIntValue = latinOrIntValue;
|
e->value.latinOrIntValue = latinOrIntValue;
|
||||||
e->value.value = QJsonPrivate::Value::valueToStore(value, (char *)e - (char *)o + valueOffset);
|
e->value.value = QJsonPrivate::Value::valueToStore(val, (char *)e - (char *)o + valueOffset);
|
||||||
QJsonPrivate::copyString((char *)(e + 1), key, latinKey);
|
QJsonPrivate::copyString((char *)(e + 1), key, latinKey);
|
||||||
if (valueSize)
|
if (valueSize)
|
||||||
QJsonPrivate::Value::copyData(value, (char *)e + valueOffset, latinOrIntValue);
|
QJsonPrivate::Value::copyData(val, (char *)e + valueOffset, latinOrIntValue);
|
||||||
|
|
||||||
|
if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u)
|
||||||
|
compact();
|
||||||
|
|
||||||
return iterator(this, pos);
|
return iterator(this, pos);
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,7 @@ QT_BEGIN_NAMESPACE
|
|||||||
#define JSONERR_UTERM_STR QT_TRANSLATE_NOOP("QJsonParseError", "unterminated string")
|
#define JSONERR_UTERM_STR QT_TRANSLATE_NOOP("QJsonParseError", "unterminated string")
|
||||||
#define JSONERR_MISS_OBJ QT_TRANSLATE_NOOP("QJsonParseError", "object is missing after a comma")
|
#define JSONERR_MISS_OBJ QT_TRANSLATE_NOOP("QJsonParseError", "object is missing after a comma")
|
||||||
#define JSONERR_DEEP_NEST QT_TRANSLATE_NOOP("QJsonParseError", "too deeply nested document")
|
#define JSONERR_DEEP_NEST QT_TRANSLATE_NOOP("QJsonParseError", "too deeply nested document")
|
||||||
|
#define JSONERR_DOC_LARGE QT_TRANSLATE_NOOP("QJsonParseError", "too large document")
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\class QJsonParseError
|
\class QJsonParseError
|
||||||
@ -105,6 +106,7 @@ QT_BEGIN_NAMESPACE
|
|||||||
\value UnterminatedString A string wasn't terminated with a quote
|
\value UnterminatedString A string wasn't terminated with a quote
|
||||||
\value MissingObject An object was expected but couldn't be found
|
\value MissingObject An object was expected but couldn't be found
|
||||||
\value DeepNesting The JSON document is too deeply nested for the parser to parse it
|
\value DeepNesting The JSON document is too deeply nested for the parser to parse it
|
||||||
|
\value DocumentTooLarge The JSON document is too large for the parser to parse it
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -173,6 +175,9 @@ QString QJsonParseError::errorString() const
|
|||||||
case DeepNesting:
|
case DeepNesting:
|
||||||
sz = JSONERR_DEEP_NEST;
|
sz = JSONERR_DEEP_NEST;
|
||||||
break;
|
break;
|
||||||
|
case DocumentTooLarge:
|
||||||
|
sz = JSONERR_DOC_LARGE;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
#ifndef QT_BOOTSTRAPPED
|
#ifndef QT_BOOTSTRAPPED
|
||||||
return QCoreApplication::translate("QJsonParseError", sz);
|
return QCoreApplication::translate("QJsonParseError", sz);
|
||||||
@ -579,6 +584,10 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
|
|||||||
return false;
|
return false;
|
||||||
case Quote: {
|
case Quote: {
|
||||||
val->type = QJsonValue::String;
|
val->type = QJsonValue::String;
|
||||||
|
if (current - baseOffset >= Value::MaxSize) {
|
||||||
|
lastError = QJsonParseError::DocumentTooLarge;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
val->value = current - baseOffset;
|
val->value = current - baseOffset;
|
||||||
bool latin1;
|
bool latin1;
|
||||||
if (!parseString(&latin1))
|
if (!parseString(&latin1))
|
||||||
@ -590,6 +599,10 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
|
|||||||
}
|
}
|
||||||
case BeginArray:
|
case BeginArray:
|
||||||
val->type = QJsonValue::Array;
|
val->type = QJsonValue::Array;
|
||||||
|
if (current - baseOffset >= Value::MaxSize) {
|
||||||
|
lastError = QJsonParseError::DocumentTooLarge;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
val->value = current - baseOffset;
|
val->value = current - baseOffset;
|
||||||
if (!parseArray())
|
if (!parseArray())
|
||||||
return false;
|
return false;
|
||||||
@ -598,6 +611,10 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
|
|||||||
return true;
|
return true;
|
||||||
case BeginObject:
|
case BeginObject:
|
||||||
val->type = QJsonValue::Object;
|
val->type = QJsonValue::Object;
|
||||||
|
if (current - baseOffset >= Value::MaxSize) {
|
||||||
|
lastError = QJsonParseError::DocumentTooLarge;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
val->value = current - baseOffset;
|
val->value = current - baseOffset;
|
||||||
if (!parseObject())
|
if (!parseObject())
|
||||||
return false;
|
return false;
|
||||||
@ -707,6 +724,10 @@ bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset)
|
|||||||
|
|
||||||
int pos = reserveSpace(sizeof(double));
|
int pos = reserveSpace(sizeof(double));
|
||||||
*(quint64 *)(data + pos) = qToLittleEndian(ui);
|
*(quint64 *)(data + pos) = qToLittleEndian(ui);
|
||||||
|
if (current - baseOffset >= Value::MaxSize) {
|
||||||
|
lastError = QJsonParseError::DocumentTooLarge;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
val->value = pos - baseOffset;
|
val->value = pos - baseOffset;
|
||||||
val->latinOrIntValue = false;
|
val->latinOrIntValue = false;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user