From d48bd33d130a52f99f400a46160e455b5749c4e8 Mon Sep 17 00:00:00 2001 From: hua_yadong Date: Fri, 24 Nov 2023 14:27:59 +0800 Subject: [PATCH] qtbase5.15.2-CVE-2023-38197 --- qt5-qtbase.spec | 8 +- qtbase5.15.2-CVE-2023-38197.patch | 374 ++++++++++++++++++++++++++++++ 2 files changed, 381 insertions(+), 1 deletion(-) create mode 100644 qtbase5.15.2-CVE-2023-38197.patch diff --git a/qt5-qtbase.spec b/qt5-qtbase.spec index ec8ad82..3826089 100644 --- a/qt5-qtbase.spec +++ b/qt5-qtbase.spec @@ -34,7 +34,7 @@ BuildRequires: pkgconfig(libsystemd) Name: qt5-qtbase Summary: Qt5 - QtBase components Version: 5.15.2 -Release: 11 +Release: 12 # See LGPL_EXCEPTIONS.txt, for exception details @@ -124,6 +124,8 @@ Patch0026: CVE-2023-37369-pre.patch Patch0027: CVE-2023-37369.patch Patch0028: CVE-2023-33285.patch Patch0029: qtbase5.15-CVE-2023-34410.patch +##https://codereview.qt-project.org/c/qt/qtbase/+/488960 +Patch0030: qtbase5.15.2-CVE-2023-38197.patch Patch1000: 1000-add-loongarch64-support-for-syscall_fork.patch Patch1001: 1001-add-sw_64-support-for-syscall_fork.patch @@ -392,6 +394,7 @@ Qt5 libraries used for drawing widgets and OpenGL items. %patch0027 -p1 %patch0028 -p1 %patch0029 -p1 +%patch0030 -p1 %patch1000 -p1 %patch1001 -p1 @@ -1036,6 +1039,9 @@ fi %changelog +* Fri Nov 24 2023 hua_yadong - 5.15.2-12 +- fix qtbase5.15.2-CVE-2023-38197.patch + * Thu Nov 02 2023 peijiankang - 5.15.2-11 - fix CVE-2023-34410 diff --git a/qtbase5.15.2-CVE-2023-38197.patch b/qtbase5.15.2-CVE-2023-38197.patch new file mode 100644 index 0000000..e52283e --- /dev/null +++ b/qtbase5.15.2-CVE-2023-38197.patch @@ -0,0 +1,374 @@ +From 4fb3cf1ad0efe261a7a6fb0b36224baccc407143 Mon Sep 17 00:00:00 2001 +From: hua_yadong +Date: Fri, 24 Nov 2023 14:23:58 +0800 +Subject: [PATCH] qtbase5.15.2-CVE-2023-38197 + +--- + src/corelib/serialization/qxmlstream.cpp | 140 +++++++++++++++++- + src/corelib/serialization/qxmlstream_p.h | 11 ++ + .../qxmlstream/tokenError/dtdInBody.xml | 21 +++ + .../qxmlstream/tokenError/multipleDtd.xml | 21 +++ + .../qxmlstream/tokenError/wellFormed.xml | 16 ++ + .../qxmlstream/tst_qxmlstream.cpp | 39 +++++ + 6 files changed, 240 insertions(+), 8 deletions(-) + create mode 100644 tests/auto/corelib/serialization/qxmlstream/tokenError/dtdInBody.xml + create mode 100644 tests/auto/corelib/serialization/qxmlstream/tokenError/multipleDtd.xml + create mode 100644 tests/auto/corelib/serialization/qxmlstream/tokenError/wellFormed.xml + +diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp +index 6c98e7c0..3172239e 100644 +--- a/src/corelib/serialization/qxmlstream.cpp ++++ b/src/corelib/serialization/qxmlstream.cpp +@@ -43,6 +43,7 @@ + + #include "qxmlutils_p.h" + #include ++#include + #include + #include + #if QT_CONFIG(textcodec) +@@ -160,7 +161,7 @@ enum { StreamEOF = ~0U }; + addData() or by waiting for it to arrive on the device(). + + \value UnexpectedElementError The parser encountered an element +- that was different to those it expected. ++ or token that was different to those it expected. + + */ + +@@ -295,13 +296,34 @@ QXmlStreamEntityResolver *QXmlStreamReader::entityResolver() const + + QXmlStreamReader is a well-formed XML 1.0 parser that does \e not + include external parsed entities. As long as no error occurs, the +- application code can thus be assured that the data provided by the +- stream reader satisfies the W3C's criteria for well-formed XML. For +- example, you can be certain that all tags are indeed nested and +- closed properly, that references to internal entities have been +- replaced with the correct replacement text, and that attributes have +- been normalized or added according to the internal subset of the +- DTD. ++ application code can thus be assured, that ++ \list ++ \li the data provided by the stream reader satisfies the W3C's ++ criteria for well-formed XML, ++ \li tokens are provided in a valid order. ++ \endlist ++ ++ Unless QXmlStreamReader raises an error, it guarantees the following: ++ \list ++ \li All tags are nested and closed properly. ++ \li References to internal entities have been replaced with the ++ correct replacement text. ++ \li Attributes have been normalized or added according to the ++ internal subset of the \l DTD. ++ \li Tokens of type \l StartDocument happen before all others, ++ aside from comments and processing instructions. ++ \li At most one DOCTYPE element (a token of type \l DTD) is present. ++ \li If present, the DOCTYPE appears before all other elements, ++ aside from StartDocument, comments and processing instructions. ++ \endlist ++ ++ In particular, once any token of type \l StartElement, \l EndElement, ++ \l Characters, \l EntityReference or \l EndDocument is seen, no ++ tokens of type StartDocument or DTD will be seen. If one is present in ++ the input stream, out of order, an error is raised. ++ ++ \note The token types \l Comment and \l ProcessingInstruction may appear ++ anywhere in the stream. + + If an error occurs while parsing, atEnd() and hasError() return + true, and error() returns the error that occurred. The functions +@@ -620,6 +642,7 @@ QXmlStreamReader::TokenType QXmlStreamReader::readNext() + d->token = -1; + return readNext(); + } ++ d->checkToken(); + return d->type; + } + +@@ -739,6 +762,10 @@ static const short QXmlStreamReader_tokenTypeString_indices[] = { + 0, 8, 16, 30, 42, 55, 66, 77, 85, 89, 105, 0 + }; + ++static constexpr auto QXmlStreamReader_XmlContextString = qOffsetStringArray( ++ "Prolog", ++ "Body" ++); + + /*! + \property QXmlStreamReader::namespaceProcessing +@@ -775,6 +802,15 @@ QString QXmlStreamReader::tokenString() const + QXmlStreamReader_tokenTypeString_indices[d->type]); + } + ++/*! ++ \internal ++ \return \param loc (Prolog/Body) as a string. ++ */ ++static constexpr QLatin1String contextString(QXmlStreamReaderPrivate::XmlContext ctxt) ++{ ++ return QLatin1String(QXmlStreamReader_XmlContextString.at(static_cast(ctxt))); ++} ++ + #endif // QT_NO_XMLSTREAMREADER + + QXmlStreamPrivateTagStack::QXmlStreamPrivateTagStack() +@@ -866,6 +902,8 @@ void QXmlStreamReaderPrivate::init() + + type = QXmlStreamReader::NoToken; + error = QXmlStreamReader::NoError; ++ currentContext = XmlContext::Prolog; ++ foundDTD = false; + } + + /* +@@ -4061,6 +4099,92 @@ void QXmlStreamWriter::writeCurrentToken(const QXmlStreamReader &reader) + } + } + ++static bool isTokenAllowedInContext(QXmlStreamReader::TokenType type, ++ QXmlStreamReaderPrivate::XmlContext loc) ++{ ++ switch (type) { ++ case QXmlStreamReader::StartDocument: ++ case QXmlStreamReader::DTD: ++ return loc == QXmlStreamReaderPrivate::XmlContext::Prolog; ++ ++ case QXmlStreamReader::StartElement: ++ case QXmlStreamReader::EndElement: ++ case QXmlStreamReader::Characters: ++ case QXmlStreamReader::EntityReference: ++ case QXmlStreamReader::EndDocument: ++ return loc == QXmlStreamReaderPrivate::XmlContext::Body; ++ ++ case QXmlStreamReader::Comment: ++ case QXmlStreamReader::ProcessingInstruction: ++ return true; ++ ++ case QXmlStreamReader::NoToken: ++ case QXmlStreamReader::Invalid: ++ return false; ++ } ++ ++ return false; ++} ++ ++/*! ++ \internal ++ \brief QXmlStreamReader::isValidToken ++ \return \c true if \param type is a valid token type. ++ \return \c false if \param type is an unexpected token, ++ which indicates a non-well-formed or invalid XML stream. ++ */ ++bool QXmlStreamReaderPrivate::isValidToken(QXmlStreamReader::TokenType type) ++{ ++ // Don't change currentContext, if Invalid or NoToken occur in the prolog ++ if (type == QXmlStreamReader::Invalid || type == QXmlStreamReader::NoToken) ++ return false; ++ ++ // If a token type gets rejected in the body, there is no recovery ++ const bool result = isTokenAllowedInContext(type, currentContext); ++ if (result || currentContext == XmlContext::Body) ++ return result; ++ ++ // First non-Prolog token observed => switch context to body and check again. ++ currentContext = XmlContext::Body; ++ return isTokenAllowedInContext(type, currentContext); ++} ++ ++/*! ++ \internal ++ Checks token type and raises an error, if it is invalid ++ in the current context (prolog/body). ++ */ ++void QXmlStreamReaderPrivate::checkToken() ++{ ++ Q_Q(QXmlStreamReader); ++ ++ // The token type must be consumed, to keep track if the body has been reached. ++ const XmlContext context = currentContext; ++ const bool ok = isValidToken(type); ++ ++ // Do nothing if an error has been raised already (going along with an unexpected token) ++ if (error != QXmlStreamReader::Error::NoError) ++ return; ++ ++ if (!ok) { ++ raiseError(QXmlStreamReader::UnexpectedElementError, ++ QStringLiteral("Unexpected token type %1 in %2.") ++ .arg(q->tokenString(), contextString(context))); ++ return; ++ } ++ ++ if (type != QXmlStreamReader::DTD) ++ return; ++ ++ // Raise error on multiple DTD tokens ++ if (foundDTD) { ++ raiseError(QXmlStreamReader::UnexpectedElementError, ++ QStringLiteral("Found second DTD token in %1.").arg(contextString(context))); ++ } else { ++ foundDTD = true; ++ } ++} ++ + /*! + \fn bool QXmlStreamAttributes::hasAttribute(const QString &qualifiedName) const + \since 4.5 +diff --git a/src/corelib/serialization/qxmlstream_p.h b/src/corelib/serialization/qxmlstream_p.h +index 80e7f740..6db58386 100644 +--- a/src/corelib/serialization/qxmlstream_p.h ++++ b/src/corelib/serialization/qxmlstream_p.h +@@ -804,6 +804,17 @@ public: + #endif + bool atEnd; + ++ enum class XmlContext ++ { ++ Prolog, ++ Body, ++ }; ++ ++ XmlContext currentContext = XmlContext::Prolog; ++ bool foundDTD = false; ++ bool isValidToken(QXmlStreamReader::TokenType type); ++ void checkToken(); ++ + /*! + \sa setType() + */ +diff --git a/tests/auto/corelib/serialization/qxmlstream/tokenError/dtdInBody.xml b/tests/auto/corelib/serialization/qxmlstream/tokenError/dtdInBody.xml +new file mode 100644 +index 00000000..68ef2962 +--- /dev/null ++++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/dtdInBody.xml +@@ -0,0 +1,21 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++]> ++ ++ ++ tst_QXmlStream ++ ++ ++ ++ ++ ]> ++ ++ +diff --git a/tests/auto/corelib/serialization/qxmlstream/tokenError/multipleDtd.xml b/tests/auto/corelib/serialization/qxmlstream/tokenError/multipleDtd.xml +new file mode 100644 +index 00000000..1dbe75c4 +--- /dev/null ++++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/multipleDtd.xml +@@ -0,0 +1,21 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++]> ++ ++ ++ ++]> ++ ++ ++ tst_QXmlStream ++ ++ ++ +diff --git a/tests/auto/corelib/serialization/qxmlstream/tokenError/wellFormed.xml b/tests/auto/corelib/serialization/qxmlstream/tokenError/wellFormed.xml +new file mode 100644 +index 00000000..9dfbc0f9 +--- /dev/null ++++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/wellFormed.xml +@@ -0,0 +1,16 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++]> ++ ++ ++ tst_QXmlStream ++ ++ ++ +diff --git a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp +index 28922574..abbdc9fc 100644 +--- a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp ++++ b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp +@@ -583,6 +583,9 @@ private slots: + + void entityExpansionLimit() const; + ++ void tokenErrorHandling_data() const; ++ void tokenErrorHandling() const; ++ + private: + static QByteArray readFile(const QString &filename); + +@@ -1812,5 +1815,41 @@ void tst_QXmlStream::roundTrip() const + QCOMPARE(out, in); + } + ++void tst_QXmlStream::tokenErrorHandling_data() const ++{ ++ QTest::addColumn("fileName"); ++ QTest::addColumn("expectedError"); ++ QTest::addColumn("errorKeyWord"); ++ ++ constexpr auto invalid = QXmlStreamReader::Error::UnexpectedElementError; ++ constexpr auto valid = QXmlStreamReader::Error::NoError; ++ QTest::newRow("DtdInBody") << "dtdInBody.xml" << invalid << "DTD"; ++ QTest::newRow("multipleDTD") << "multipleDtd.xml" << invalid << "second DTD"; ++ QTest::newRow("wellFormed") << "wellFormed.xml" << valid << ""; ++} ++ ++void tst_QXmlStream::tokenErrorHandling() const ++{ ++ QFETCH(const QString, fileName); ++ QFETCH(const QXmlStreamReader::Error, expectedError); ++ QFETCH(const QString, errorKeyWord); ++ ++ const QDir dir(QFINDTESTDATA("tokenError")); ++ QFile file(dir.absoluteFilePath(fileName)); ++ ++ // Cross-compiling: File will be on host only ++ if (!file.exists()) ++ QSKIP("Testfile not found."); ++ ++ file.open(QIODevice::ReadOnly); ++ QXmlStreamReader reader(&file); ++ while (!reader.atEnd()) ++ reader.readNext(); ++ ++ QCOMPARE(reader.error(), expectedError); ++ if (expectedError != QXmlStreamReader::Error::NoError) ++ QVERIFY(reader.errorString().contains(errorKeyWord)); ++} ++ + #include "tst_qxmlstream.moc" + // vim: et:ts=4:sw=4:sts=4 +-- +2.41.0 + -- Gitee