From ca6d005dceb293729be56c96c26c2a7ae19877ef Mon Sep 17 00:00:00 2001 From: Magdalena Stojek Date: Tue, 04 Aug 2026 14:26:05 +0200 Subject: [PATCH] QDom: Guard against null nodes returned by factory functions Since the default InvalidDataPolicy changed to ReturnNullNode, the QDomDocumentPrivate factory functions return nullptr when the input contains data that is invalid per XML 1.0. QDomBuilder::characters(), QDomBuilder::skippedEntity() and QDomBuilder::comment() used the returned node without checking, causing a crash. Check for nullptr and return false, as startElement() and processingInstruction() already do. Pick-to: 6.8 6.5 5.15 Fixes: QTBUG-148791 Change-Id: Id46c8438e648008d7edad50ae411d682ec5152f6 Reviewed-by: Thiago Macieira (cherry picked from commit 8b5670485050cbedffe92c9ca48537f89dc896bd) Reviewed-by: Qt Cherry-pick Bot (cherry picked from commit ec02738a2f8edea9b578c4ed914f575d47d54b61) --- diff --git a/src/xml/dom/qdomhelpers.cpp b/src/xml/dom/qdomhelpers.cpp index 6b73f1b..d3213ab 100644 --- a/src/xml/dom/qdomhelpers.cpp +++ b/src/xml/dom/qdomhelpers.cpp @@ -151,6 +151,8 @@ } else { n.reset(doc->createTextNode(characters)); } + if (!n) + return false; n->setLocation(int(reader->lineNumber()), int(reader->columnNumber())); node->appendChild(n.get()); @@ -172,6 +174,8 @@ bool QDomBuilder::skippedEntity(const QString &name) { QDomNodePrivate *n = doc->createEntityReference(name); + if (!n) + return false; n->setLocation(int(reader->lineNumber()), int(reader->columnNumber())); node->appendChild(n); return true; @@ -200,6 +204,8 @@ { QDomNodePrivate *n; n = doc->createComment(characters); + if (!n) + return false; n->setLocation(int(reader->lineNumber()), int(reader->columnNumber())); node->appendChild(n); return true; diff --git a/tests/auto/xml/dom/qdom/tst_qdom.cpp b/tests/auto/xml/dom/qdom/tst_qdom.cpp index 0c68221..ad81148 100644 --- a/tests/auto/xml/dom/qdom/tst_qdom.cpp +++ b/tests/auto/xml/dom/qdom/tst_qdom.cpp @@ -105,6 +105,8 @@ void germanUmlautToFile() const; void setInvalidDataPolicy() const; void crashInSetContent() const; + void invalidCharDataInSetContent() const; + void invalidCharDataInSetContent_data() const; void doubleNamespaceDeclarations() const; void setContentQXmlReaderOverload() const; void toStringWithoutNewlines() const; @@ -2336,6 +2338,60 @@ QVERIFY(docImport.setContent(QLatin1String(""))); } +void tst_QDom::invalidCharDataInSetContent_data() const +{ + QTest::addColumn("input"); + QTest::addColumn("parsed"); + QTest::addColumn("document"); + + QTest::newRow("control") + << "TextNode"_ba << true + << u"TextNode"_s; + QTest::newRow("text-with-nul") + << "te\0xt"_ba << false + << u"te"_s; + QTest::newRow("cdata") + << ""_ba << false + << u""_s; + QTest::newRow("comment") + << ""_ba << false + << u""_s; + QTest::newRow("pi-data") + << ""_ba << false + << u""_s; + QTest::newRow("start-element") + << ""_ba << false + << u""_s; + QTest::newRow("nul-at-end") + << "\0"_ba << false + << u""_s; +} + +void tst_QDom::invalidCharDataInSetContent() const +{ + QFETCH(const QByteArray, input); + QFETCH(const bool, parsed); + QFETCH(const QString, document); + + const auto policy = QDomImplementation::invalidDataPolicy(); + const auto restoreInvalidDataPolicy = qScopeGuard([policy] { + QDomImplementation::setInvalidDataPolicy(policy); + }); + QDomImplementation::setInvalidDataPolicy(QDomImplementation::ReturnNullNode); + + QDomDocument doc; + const QDomDocument::ParseResult result = + doc.setContent(input, QDomDocument::ParseOption::UseNamespaceProcessing); + + QCOMPARE(bool(result), parsed); + if (!parsed) { + QVERIFY(!result.errorMessage.isEmpty()); + QCOMPARE(result.errorLine, qsizetype(1)); + QVERIFY(result.errorColumn > 0); + } + QCOMPARE(doc.toString(-1), document); +} + void tst_QDom::doubleNamespaceDeclarations() const { QDomDocument doc;