From 112607ce0705fa5db61d0ee19d693f86532abfdf 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.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) (cherry picked from commit ca6d005dceb293729be56c96c26c2a7ae19877ef) --- diff --git a/src/xml/dom/qdomhelpers.cpp b/src/xml/dom/qdomhelpers.cpp index b751ccd..be925d4 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 a994e81..ecf25fa 100644 --- a/tests/auto/xml/dom/qdom/tst_qdom.cpp +++ b/tests/auto/xml/dom/qdom/tst_qdom.cpp @@ -99,6 +99,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; @@ -2045,6 +2047,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;