diff --git a/src/corelib/json/qjson.cpp b/src/corelib/json/qjson.cpp index ed6ef74e3cc63e7951415ecdcd204509e58c251d..8215aeefc0fb63ca75b47b73f652ddc24eb70a4a 100644 --- a/src/corelib/json/qjson.cpp +++ b/src/corelib/json/qjson.cpp @@ -149,6 +149,10 @@ bool Data::valid() const int Base::reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace) { 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; // move table to new position @@ -334,7 +338,7 @@ bool Value::isValid(const Base *b) const /*! \internal */ -int Value::requiredStorage(const QJsonValue &v, bool *compressed) +int Value::requiredStorage(QJsonValue &v, bool *compressed) { *compressed = false; switch (v.t) { @@ -351,6 +355,11 @@ int Value::requiredStorage(const QJsonValue &v, bool *compressed) } case QJsonValue::Array: 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); case QJsonValue::Undefined: case QJsonValue::Null: diff --git a/src/corelib/json/qjson_p.h b/src/corelib/json/qjson_p.h index 81439a00ce75073b4ebb3e13e0c07d4023d28c34..06885ad972a009ed98c22066a3efc47ac25c66d7 100644 --- a/src/corelib/json/qjson_p.h +++ b/src/corelib/json/qjson_p.h @@ -543,6 +543,9 @@ public: class Value { public: + enum { + MaxSize = (1<<27) - 1 + }; union { uint _dummy; qle_bitfield<0, 3> type; @@ -564,7 +567,7 @@ public: 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 void copyData(const QJsonValue &v, char *dest, bool compressed); }; diff --git a/src/corelib/json/qjsonarray.cpp b/src/corelib/json/qjsonarray.cpp index 5f1c38a752fe4069f1f273d234e22253376b7049..fb8d2e83ff50b1fd0d73ea6f85eca46c5447e1ed 100644 --- a/src/corelib/json/qjsonarray.cpp +++ b/src/corelib/json/qjsonarray.cpp @@ -391,9 +391,10 @@ QJsonValue QJsonArray::takeAt(int i) void QJsonArray::insert(int i, const QJsonValue &value) { Q_ASSERT (i >= 0 && i <= (a ? (int)a->length : 0)); + QJsonValue val = value; bool compressed; - int valueSize = QJsonPrivate::Value::requiredStorage(value, &compressed); + int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); detach(valueSize + sizeof(QJsonPrivate::Value)); @@ -401,13 +402,16 @@ void QJsonArray::insert(int i, const QJsonValue &value) a->tableOffset = sizeof(QJsonPrivate::Array); int valueOffset = a->reserveSpace(valueSize, i, 1, false); + if (!valueOffset) + return; + 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.latinKey = false; - v.value = QJsonPrivate::Value::valueToStore(value, valueOffset); + v.value = QJsonPrivate::Value::valueToStore(val, valueOffset); 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) { Q_ASSERT (a && i >= 0 && i < (int)(a->length)); + QJsonValue val = value; bool compressed; - int valueSize = QJsonPrivate::Value::requiredStorage(value, &compressed); + int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); detach(valueSize); @@ -447,13 +452,16 @@ void QJsonArray::replace(int i, const QJsonValue &value) a->tableOffset = sizeof(QJsonPrivate::Array); int valueOffset = a->reserveSpace(valueSize, i, 1, true); + if (!valueOffset) + return; + 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.latinKey = false; - v.value = QJsonPrivate::Value::valueToStore(value, valueOffset); + v.value = QJsonPrivate::Value::valueToStore(val, valueOffset); if (valueSize) - QJsonPrivate::Value::copyData(value, (char *)a + valueOffset, compressed); + QJsonPrivate::Value::copyData(val, (char *)a + valueOffset, compressed); ++d->compactionCounter; if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u) diff --git a/src/corelib/json/qjsondocument.h b/src/corelib/json/qjsondocument.h index 4d4f3885dce74841f3a3dfd741837c37afd1e00a..0354262e2c6c5b357ba85ceb4d4de5198bb7dee7 100644 --- a/src/corelib/json/qjsondocument.h +++ b/src/corelib/json/qjsondocument.h @@ -67,7 +67,8 @@ struct Q_CORE_EXPORT QJsonParseError IllegalUTF8String, UnterminatedString, MissingObject, - DeepNesting + DeepNesting, + DocumentTooLarge }; QString errorString() const; diff --git a/src/corelib/json/qjsonobject.cpp b/src/corelib/json/qjsonobject.cpp index 55c736afcefc55b7dca9affd7cb7f18ef07e39a4..2be9d8891dd228640156183640049bbabc83e9d5 100644 --- a/src/corelib/json/qjsonobject.cpp +++ b/src/corelib/json/qjsonobject.cpp @@ -317,9 +317,10 @@ QJsonObject::iterator QJsonObject::insert(const QString &key, const QJsonValue & remove(key); return end(); } + QJsonValue val = value; bool latinOrIntValue; - int valueSize = QJsonPrivate::Value::requiredStorage(value, &latinOrIntValue); + int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); bool latinKey = QJsonPrivate::useCompressed(key); int valueOffset = sizeof(QJsonPrivate::Entry) + QJsonPrivate::qStringSize(key, latinKey); @@ -335,16 +336,21 @@ QJsonObject::iterator QJsonObject::insert(const QString &key, const QJsonValue & if (keyExists) ++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); - e->value.type = value.t; + e->value.type = val.t; e->value.latinKey = latinKey; 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); 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); } diff --git a/src/corelib/json/qjsonparser.cpp b/src/corelib/json/qjsonparser.cpp index e569cbf435fd0d4cc39020130a27b798f9cefde6..7989d18901654c9ca218b6111d6627330938c6b1 100644 --- a/src/corelib/json/qjsonparser.cpp +++ b/src/corelib/json/qjsonparser.cpp @@ -76,6 +76,7 @@ QT_BEGIN_NAMESPACE #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_DEEP_NEST QT_TRANSLATE_NOOP("QJsonParseError", "too deeply nested document") +#define JSONERR_DOC_LARGE QT_TRANSLATE_NOOP("QJsonParseError", "too large document") /*! \class QJsonParseError @@ -105,6 +106,7 @@ QT_BEGIN_NAMESPACE \value UnterminatedString A string wasn't terminated with a quote \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 DocumentTooLarge The JSON document is too large for the parser to parse it */ /*! @@ -173,6 +175,9 @@ QString QJsonParseError::errorString() const case DeepNesting: sz = JSONERR_DEEP_NEST; break; + case DocumentTooLarge: + sz = JSONERR_DOC_LARGE; + break; } #ifndef QT_BOOTSTRAPPED return QCoreApplication::translate("QJsonParseError", sz); @@ -579,6 +584,10 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) return false; case Quote: { val->type = QJsonValue::String; + if (current - baseOffset >= Value::MaxSize) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } val->value = current - baseOffset; bool latin1; if (!parseString(&latin1)) @@ -590,6 +599,10 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) } case BeginArray: val->type = QJsonValue::Array; + if (current - baseOffset >= Value::MaxSize) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } val->value = current - baseOffset; if (!parseArray()) return false; @@ -598,6 +611,10 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) return true; case BeginObject: val->type = QJsonValue::Object; + if (current - baseOffset >= Value::MaxSize) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } val->value = current - baseOffset; if (!parseObject()) return false; @@ -707,6 +724,10 @@ bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset) int pos = reserveSpace(sizeof(double)); *(quint64 *)(data + pos) = qToLittleEndian(ui); + if (current - baseOffset >= Value::MaxSize) { + lastError = QJsonParseError::DocumentTooLarge; + return false; + } val->value = pos - baseOffset; val->latinOrIntValue = false;