From a2cd9d2a70575c37153d43e9c5396a9144af1923 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 02 Sep 2026 12:04:25 -0300 Subject: [PATCH] JSON/CBOR: fix crash converting an empty URL tag to JSON A CBOR URL (tag 32) wrapping an empty, definite-length text string has no backing ByteData after decoding via QCborValue::fromCbor(), so byteData() returns nullptr. convertExtendedTypeToJson() dereferenced it unconditionally and crashed. Guard against the null pointer. Fixes: QTBUG-149841 Pick-to: 6.8 6.5 5.15 Change-Id: I919b870f7a23d41fdb98fffde1fa84247d5c2d18 Reviewed-by: Ivan Solovev (cherry picked from commit 9e9988ee57c0210a5ba57435182b094f50b50f28) Reviewed-by: Qt Cherry-pick Bot --- diff --git a/src/corelib/serialization/qjsoncbor.cpp b/src/corelib/serialization/qjsoncbor.cpp index cbee0a4..fd641a3 100644 --- a/src/corelib/serialization/qjsoncbor.cpp +++ b/src/corelib/serialization/qjsoncbor.cpp @@ -174,15 +174,21 @@ qint64 tag = d->elements.at(0).value; switch (tag) { - case qint64(QCborKnownTags::Url): + case qint64(QCborKnownTags::Url): { #ifdef QT_BOOTSTRAPPED break; #else // use the fully-encoded URL form - if (d->elements.at(1).type == QCborValue::String) - return QUrl::fromEncoded(d->byteData(1)->asByteArrayView()).toString(QUrl::FullyEncoded); + Element e = d->elements.at(1); + const ByteData *bd = d->byteData(e); + if (e.type == QCborValue::String) { + if (!bd) + return QString(); + return QUrl::fromEncoded(bd->asByteArrayView()).toString(QUrl::FullyEncoded); + } Q_FALLTHROUGH(); #endif + } case qint64(QCborKnownTags::DateTimeString): case qint64(QCborKnownTags::ExpectedBase64url): diff --git a/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp b/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp index c7b9b3b..e56622f 100644 --- a/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp +++ b/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp @@ -51,8 +51,13 @@ return QTest::addRow("Double:%g", exp.toDouble()); if (v.type() == QCborValue::ByteArray || v.type() == QCborValue::String) return QTest::addRow("%s:%zd", typeString, size_t(exp.toString().size())); - if (v.type() >= 0x10000) + if (v.type() == QCborValue::Uuid) return QTest::newRow(exp.typeName()); + if (v.type() >= 0x10000) { + // all others are based on string + return QTest::addRow("%s:%zd", exp.typeName(), + size_t(v.taggedValue().toString().size())); + } return QTest::newRow(typeString); }; addRow() << v << exp << json; @@ -75,10 +80,15 @@ add(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); // converts to string in JSON: + add(QByteArray(), QByteArray(), QString()); add(QByteArray("Hello"), QByteArray("Hello"), "SGVsbG8"); + // Invalid datetimes are not QCborValue::DateTime + //add(QCborValue(QDateTime()), QString(), QString()); add(QCborValue(dt), dt, dt.toString(Qt::ISODateWithMs)); + add(QCborValue(QUrl()), QUrl(), QString()); add(QCborValue(QUrl("http://example.com/{q}")), QUrl("http://example.com/{q}"), "http://example.com/%7Bq%7D"); // note the encoded form in JSON + add(QCborValue(QRegularExpression()), QRegularExpression(), QString()); add(QCborValue(QRegularExpression(".")), QRegularExpression("."), "."); add(QCborValue(uuid), uuid, uuid.toString(QUuid::WithoutBraces)); @@ -116,6 +126,7 @@ } QCOMPARE(v.toVariant(), variant); + if (variant.isValid()) { QVariant variant2 = QVariant::fromValue(v); QVERIFY(variant2.canConvert(variant.metaType())); @@ -123,6 +134,12 @@ QCOMPARE(variant2, variant); } +#if QT_CONFIG(cborstreamreader) && QT_CONFIG(cborstreamwriter) + // repeat after a round-trip through CBOR encoding, which exercises the + // decoding paths in QCborValue::fromCbor() + QCOMPARE(QCborValue::fromCbor(v.toCbor()).toVariant(), variant); +#endif + // tags get ignored: QCOMPARE(QCborValue(QCborKnownTags::Signature, v).toVariant(), variant); @@ -147,6 +164,12 @@ QCOMPARE(v.toJsonValue(), json); QCOMPARE(QVariant::fromValue(v).toJsonValue(), json); +#if QT_CONFIG(cborstreamreader) && QT_CONFIG(cborstreamwriter) + // repeat after a round-trip through CBOR encoding, which exercises the + // decoding paths in QCborValue::fromCbor() + QCOMPARE(QCborValue::fromCbor(v.toCbor()).toJsonValue(), json); +#endif + // most tags get ignored: QCOMPARE(QCborValue(QCborKnownTags::Signature, v).toJsonValue(), json);