From 03f1a69e9cffe919597373471f7609521a465470 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 18 Mar 2015 08:49:39 +0100 Subject: Avoid size overflows when inserting into very large JSON objects QJson has a size limitation for arrays and objects. Make sure we don't go over that size limit and create corrupt objects when inserting data. Change-Id: I45be3caefc282d8041f38acd120b985ed4389b8c Reviewed-by: Oswald Buddenhagen Reviewed-by: Simon Hausmann Reviewed-by: Thiago Macieira --- src/corelib/json/qjsonarray.cpp | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'src/corelib/json/qjsonarray.cpp') diff --git a/src/corelib/json/qjsonarray.cpp b/src/corelib/json/qjsonarray.cpp index bb33dbde747..e8d54b5b879 100644 --- a/src/corelib/json/qjsonarray.cpp +++ b/src/corelib/json/qjsonarray.cpp @@ -382,7 +382,7 @@ void QJsonArray::removeAt(int i) if (!a || i < 0 || i >= (int)a->length) return; - detach(); + detach2(); a->removeItems(i, 1); ++d->compactionCounter; if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u) @@ -442,7 +442,8 @@ void QJsonArray::insert(int i, const QJsonValue &value) bool compressed; int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); - detach(valueSize + sizeof(QJsonPrivate::Value)); + if (!detach2(valueSize + sizeof(QJsonPrivate::Value))) + return; if (!a->length) a->tableOffset = sizeof(QJsonPrivate::Array); @@ -492,7 +493,8 @@ void QJsonArray::replace(int i, const QJsonValue &value) bool compressed; int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); - detach(valueSize); + if (!detach2(valueSize)) + return; if (!a->length) a->tableOffset = sizeof(QJsonPrivate::Array); @@ -1122,22 +1124,39 @@ bool QJsonArray::operator!=(const QJsonArray &other) const \internal */ void QJsonArray::detach(uint reserve) +{ + Q_UNUSED(reserve) + Q_ASSERT(!reserve); + detach2(0); +} + +/*! + \internal + */ +bool QJsonArray::detach2(uint reserve) { if (!d) { + if (reserve >= QJsonPrivate::Value::MaxSize) { + qWarning("QJson: Document too large to store in data structure"); + return false; + } d = new QJsonPrivate::Data(reserve, QJsonValue::Array); a = static_cast(d->header->root()); d->ref.ref(); - return; + return true; } if (reserve == 0 && d->ref.load() == 1) - return; + return true; QJsonPrivate::Data *x = d->clone(a, reserve); + if (!x) + return false; x->ref.ref(); if (!d->ref.deref()) delete d; d = x; a = static_cast(d->header->root()); + return true; } /*! @@ -1148,7 +1167,7 @@ void QJsonArray::compact() if (!d || !d->compactionCounter) return; - detach(); + detach2(); d->compact(); a = static_cast(d->header->root()); } -- cgit v1.2.3 From 4889269ff0fb37130b332863e82dd7c19564116c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 18 Mar 2015 08:49:09 +0100 Subject: Fix quadratic behavior when converting from QVariant The old code called insert for each item, leading to constant reallocation of the data structure. Instead rely on the fact that a QVariantMap (as well as the variant list) is sorted, so we can convert to the QJson data structure in one go without lots of reallocations. Task-number: QTBUG-44737 Change-Id: Id2d38d278fb9afa5e062c7353b4d4215bdfb993c Reviewed-by: Thiago Macieira --- src/corelib/json/qjsonarray.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) (limited to 'src/corelib/json/qjsonarray.cpp') diff --git a/src/corelib/json/qjsonarray.cpp b/src/corelib/json/qjsonarray.cpp index e8d54b5b879..dc8851e8e7e 100644 --- a/src/corelib/json/qjsonarray.cpp +++ b/src/corelib/json/qjsonarray.cpp @@ -256,8 +256,45 @@ QJsonArray QJsonArray::fromStringList(const QStringList &list) QJsonArray QJsonArray::fromVariantList(const QVariantList &list) { QJsonArray array; - for (QVariantList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it) - array.append(QJsonValue::fromVariant(*it)); + if (list.isEmpty()) + return array; + + array.detach2(1024); + + QVector values; + values.resize(list.size()); + QJsonPrivate::Value *valueData = values.data(); + uint currentOffset = sizeof(QJsonPrivate::Base); + + for (int i = 0; i < list.size(); ++i) { + QJsonValue val = QJsonValue::fromVariant(list.at(i)); + + bool latinOrIntValue; + int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); + + if (!array.detach2(valueSize)) + return QJsonArray(); + + QJsonPrivate::Value *v = valueData + i; + v->type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); + v->latinOrIntValue = latinOrIntValue; + v->latinKey = false; + v->value = QJsonPrivate::Value::valueToStore(val, currentOffset); + if (valueSize) + QJsonPrivate::Value::copyData(val, (char *)array.a + currentOffset, latinOrIntValue); + + currentOffset += valueSize; + array.a->size = currentOffset; + } + + // write table + array.a->tableOffset = currentOffset; + if (!array.detach2(sizeof(QJsonPrivate::offset)*values.size())) + return QJsonArray(); + memcpy(array.a->table(), values.constData(), values.size()*sizeof(uint)); + array.a->length = values.size(); + array.a->size = currentOffset + sizeof(QJsonPrivate::offset)*values.size(); + return array; } -- cgit v1.2.3