diff options
author | Jüri Valdmann <juri.valdmann@qt.io> | 2018-07-26 16:03:50 +0200 |
---|---|---|
committer | Jüri Valdmann <juri.valdmann@qt.io> | 2018-07-31 09:33:42 +0000 |
commit | f43e947dc405b6a2324656f631c804db8e8dec3d (patch) | |
tree | 50b90213d6ecf8ca00b3aa94e7fd580f655dbd28 | |
parent | 0ef66e98ccf4946a0e4513ab5fc157df0f0aca4e (diff) |
QJsonDocument: Make emptyObject an object
A default-constructed QJsonObject has no data payload, it is only a pair of null
pointers. So, when it becomes necessary to 'materialize' such an object, a
special global emptyObject constant is used as the substitute payload. There is
a small problem with this global constant though, namely that it's is_object
flag is unset. In other words, the emptyObject is not an object, but an array.
Fix by setting the is_object flag on emptyObject.
The example code in the bug report
QJsonObject parent;
QJsonObject child;
parent["child"] = child; // 1
child = parent["child"].toObject(); // 2
child["test"] = "test"; // 3
runs into this problem on line 1. Inserting the default-constructed child means
inserting a copy of emptyObject. On line 2 a pointer to this copy of emptyObject
is retrieved and cast to an object. But it's not an object, it's an array, so
things go wrong hereafter.
Specifically, on line 3, two inserts are performed, one from operator[] and one
from operator=. Each insert increments a compaction counter. The second insert
triggers compaction (QJsonObject::insert calls Value::requiredStorage calls
Data::compact) and compaction branches based on the is_object flag. Replacing
line 3 with
child.insert("test", "test");
causes the example to appear to work since compaction is not triggered and the
JSON serializer does not look at the is_object flag. Still, any further insert()
calls would trigger compaction and memory corruption.
Task-number: QTBUG-69626
Change-Id: I8bd5174dce95998bac479c4b4ffea70bca1a4d04
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
-rw-r--r-- | src/corelib/serialization/qjson.cpp | 2 | ||||
-rw-r--r-- | tests/auto/corelib/serialization/json/tst_qtjson.cpp | 2 |
2 files changed, 3 insertions, 1 deletions
diff --git a/src/corelib/serialization/qjson.cpp b/src/corelib/serialization/qjson.cpp index 7912b5040c..b82923fe0c 100644 --- a/src/corelib/serialization/qjson.cpp +++ b/src/corelib/serialization/qjson.cpp @@ -46,7 +46,7 @@ namespace QJsonPrivate { static Q_CONSTEXPR Base emptyArray = { { qle_uint(sizeof(Base)) }, { 0 }, { qle_uint(0) } }; -static Q_CONSTEXPR Base emptyObject = { { qle_uint(sizeof(Base)) }, { 0 }, { qle_uint(0) } }; +static Q_CONSTEXPR Base emptyObject = { { qle_uint(sizeof(Base)) }, { qToLittleEndian(1u) }, { qle_uint(0) } }; void Data::compact() { diff --git a/tests/auto/corelib/serialization/json/tst_qtjson.cpp b/tests/auto/corelib/serialization/json/tst_qtjson.cpp index 41c8f760dc..4651258ef3 100644 --- a/tests/auto/corelib/serialization/json/tst_qtjson.cpp +++ b/tests/auto/corelib/serialization/json/tst_qtjson.cpp @@ -648,6 +648,7 @@ void tst_QtJson::testArrayNestedEmpty() object.insert("inner", inner); QJsonValue val = object.value("inner"); QJsonArray value = object.value("inner").toArray(); + QVERIFY(QJsonDocument(value).isArray()); QCOMPARE(value.size(), 0); QCOMPARE(value, inner); QCOMPARE(value.size(), 0); @@ -666,6 +667,7 @@ void tst_QtJson::testObjectNestedEmpty() object.insert("inner", inner); object.insert("inner2", inner2); QJsonObject value = object.value("inner").toObject(); + QVERIFY(QJsonDocument(value).isObject()); QCOMPARE(value.size(), 0); QCOMPARE(value, inner); QCOMPARE(value.size(), 0); |