Your message dated Sat, 29 Jun 2024 10:46:15 +0000
with message-id <e1snvax-002byb...@coccia.debian.org>
and subject line Released with 12.6
has caused the Debian Bug report #1055802,
regarding bookworm-pu: package qtbase-opensource-src/5.15.8+dfsg-11+deb12u1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact ow...@bugs.debian.org
immediately.)


-- 
1055802: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1055802
Debian Bug Tracking System
Contact ow...@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bookworm
User: release.debian....@packages.debian.org
Usertags: pu
X-Debbugs-Cc: qtbase-opensource-...@packages.debian.org
Control: affects -1 + src:qtbase-opensource-src

[ Reason ]
The main goal of the proposed update is to fix bug #1055280: broken Unicode
support in libqt5sql5-odbc because of patch for CVE-2023-24607.

Additionally, I backported fixes for three more CVEs which were discovered
in the meantime: CVE-2023-34410, CVE-2023-37369 and CVE-2023-38197.

[ Impact ]
The ODBC backend of Qt SQL is broken without this fix. In particular, it's
known to be broken when using it with Microsoft SQL server.

[ Tests ]
Unfortunately we do not run upstream testsuite in qtbase, due to known issues
with it. But all patches come from Qt upstream, where they have been tested of
course.

[ Risks ]
- The patches to fix Unicode regressions are quite trivial.
- The patch to fix CVE-2023-34410 is quite trivial too.
- This cannot be said about the remaining two patches (for CVE-2023-37369 and
  CVE-2023-38197), however they are present in Debian unstable since version
  5.15.10+dfsg-3 from 2023-07-27, and nobody has complained since then.
  Also, all these CVE patches are present in KDE's Qt 5.15 branch. In fact,
  our CVE-2023-37369.diff is based on the KDE's patch.

If you consider those last two patches risky, I will be happy to upload
without them, that is, with the regression fixes and CVE-2023-34410 only.
This way the upload will be equivalent to 5.15.8+dfsg-13 from 2023-07-05.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]
* Backport upstream patches to fix regression caused by CVE-2023-24607.diff
  (closes: #1055280).
* Backport fixes for three CVEs from Debian unstable:
  - CVE-2023-34410: use of system CA certificates when not wanted
    (closes: #1037210).
  - CVE-2023-37369: potential buffer overflow in QXmlStreamReader.
  - CVE-2023-38197: infinite loop in XML recursive entity expansion
    (closes: #1041105).

[ Other info ]
See also the Security Tracker:
https://security-tracker.debian.org/tracker/source-package/qtbase-opensource-src

--
Dmitry Shachnev
diff --git a/debian/changelog b/debian/changelog
index 7215917..526e1c3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,19 @@
+qtbase-opensource-src (5.15.8+dfsg-11+deb12u1) UNRELEASED; urgency=medium
+
+  [ Alexander Volkov ]
+  * Backport upstream patches to fix regression caused by CVE-2023-24607.diff
+    (closes: #1055280).
+
+  [ Dmitry Shachnev ]
+  * Backport fixes for three CVEs from Debian unstable:
+    - CVE-2023-34410: use of system CA certificates when not wanted
+      (closes: #1037210).
+    - CVE-2023-37369: potential buffer overflow in QXmlStreamReader.
+    - CVE-2023-38197: infinite loop in XML recursive entity expansion
+      (closes: #1041105).
+
+ -- Debian Qt/KDE Maintainers <debian-qt-...@lists.debian.org>  Sun, 05 Nov 2023 18:18:43 +0300
+
 qtbase-opensource-src (5.15.8+dfsg-11) unstable; urgency=medium
 
   * Rename the patches for consistency and add DEP-3 headers.
diff --git a/debian/patches/CVE-2023-34410.diff b/debian/patches/CVE-2023-34410.diff
new file mode 100644
index 0000000..bc5e30d
--- /dev/null
+++ b/debian/patches/CVE-2023-34410.diff
@@ -0,0 +1,34 @@
+Description: Ssl: Copy the on-demand cert loading bool from default config
+ Otherwise individual sockets will still load system certificates when
+ a chain doesn't match against the configured CA certificates.
+ That's not intended behavior, since specifically setting the CA
+ certificates means you don't want the system certificates to be used.
+ .
+ This is potentially a breaking change because now, if you ever add a
+ CA to the default config, it will disable loading system certificates
+ on demand for all sockets. And the only way to re-enable it is to
+ create a null-QSslConfiguration and set it as the new default.
+Origin: upstream, https://code.qt.io/cgit/qt/qtbase.git/commit/?id=57ba6260c0801055
+Last-Update: 2023-06-08
+
+--- a/src/network/ssl/qsslsocket.cpp
++++ b/src/network/ssl/qsslsocket.cpp
+@@ -2221,6 +2221,10 @@ QSslSocketPrivate::QSslSocketPrivate()
+     , flushTriggered(false)
+ {
+     QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration);
++    // If the global configuration doesn't allow root certificates to be loaded
++    // on demand then we have to disable it for this socket as well.
++    if (!configuration.allowRootCertOnDemandLoading)
++        allowRootCertOnDemandLoading = false;
+ }
+ 
+ /*!
+@@ -2470,6 +2474,7 @@ void QSslConfigurationPrivate::deepCopyD
+     ptr->sessionProtocol = global->sessionProtocol;
+     ptr->ciphers = global->ciphers;
+     ptr->caCertificates = global->caCertificates;
++    ptr->allowRootCertOnDemandLoading = global->allowRootCertOnDemandLoading;
+     ptr->protocol = global->protocol;
+     ptr->peerVerifyMode = global->peerVerifyMode;
+     ptr->peerVerifyDepth = global->peerVerifyDepth;
diff --git a/debian/patches/CVE-2023-37369.diff b/debian/patches/CVE-2023-37369.diff
new file mode 100644
index 0000000..8b0af91
--- /dev/null
+++ b/debian/patches/CVE-2023-37369.diff
@@ -0,0 +1,289 @@
+Description: QXmlStreamReader: make fastScanName() indicate parsing status to callers
+ This fixes a crash while parsing an XML file with garbage data, the file
+ starts with '<' then garbage data:
+ - The loop in the parse() keeps iterating until it hits "case 262:",
+   which calls fastScanName()
+ - fastScanName() iterates over the text buffer scanning for the
+   attribute name (e.g. "xml:lang"), until it finds ':'
+ - Consider a Value val, fastScanName() is called on it, it would set
+   val.prefix to a number > val.len, then it would hit the 4096 condition
+   and return (returned 0, now it returns the equivalent of
+   std::null_opt), which means that val.len doesn't get modified, making
+   it smaller than val.prefix
+ - The code would try constructing an XmlStringRef with negative length,
+   which would hit an assert in one of QStringView's constructors
+ .
+ Add an assert to the XmlStringRef constructor.
+ .
+ Add unittest based on the file from the bug report.
+ .
+ Credit to OSS-Fuzz.
+Origin: upstream, commits
+ https://code.qt.io/cgit/qt/qtbase.git/commit/?id=1a423ce4372d18a7
+ https://code.qt.io/cgit/qt/qtbase.git/commit/?id=6326bec46a618c72
+ https://code.qt.io/cgit/qt/qtbase.git/commit/?id=bdc8dc51380d2ce4
+ https://code.qt.io/cgit/qt/qtbase.git/commit/?id=3bc3b8d69a291aa5
+ .
+ Based on KDE's backport:
+ https://invent.kde.org/qt/qt/qtbase/-/merge_requests/263
+Last-Update: 2023-07-15
+
+--- a/src/corelib/serialization/qxmlstream.cpp
++++ b/src/corelib/serialization/qxmlstream.cpp
+@@ -1302,15 +1302,18 @@ inline int QXmlStreamReaderPrivate::fast
+     return n;
+ }
+ 
+-inline int QXmlStreamReaderPrivate::fastScanName(int *prefix)
++// Fast scan an XML attribute name (e.g. "xml:lang").
++inline QXmlStreamReaderPrivate::FastScanNameResult
++QXmlStreamReaderPrivate::fastScanName(Value *val)
+ {
+     int n = 0;
+     uint c;
+     while ((c = getChar()) != StreamEOF) {
+         if (n >= 4096) {
+             // This is too long to be a sensible name, and
+-            // can exhaust memory
+-            return 0;
++            // can exhaust memory, or the range of decltype(*prefix)
++            raiseNamePrefixTooLongError();
++            return {};
+         }
+         switch (c) {
+         case '\n':
+@@ -1339,23 +1342,23 @@ inline int QXmlStreamReaderPrivate::fast
+         case '+':
+         case '*':
+             putChar(c);
+-            if (prefix && *prefix == n+1) {
+-                *prefix = 0;
++            if (val && val->prefix == n + 1) {
++                val->prefix = 0;
+                 putChar(':');
+                 --n;
+             }
+-            return n;
++            return FastScanNameResult(n);
+         case ':':
+-            if (prefix) {
+-                if (*prefix == 0) {
+-                    *prefix = n+2;
++            if (val) {
++                if (val->prefix == 0) {
++                    val->prefix = n + 2;
+                 } else { // only one colon allowed according to the namespace spec.
+                     putChar(c);
+-                    return n;
++                    return FastScanNameResult(n);
+                 }
+             } else {
+                 putChar(c);
+-                return n;
++                return FastScanNameResult(n);
+             }
+             Q_FALLTHROUGH();
+         default:
+@@ -1364,12 +1367,12 @@ inline int QXmlStreamReaderPrivate::fast
+         }
+     }
+ 
+-    if (prefix)
+-        *prefix = 0;
++    if (val)
++        val->prefix = 0;
+     int pos = textBuffer.size() - n;
+     putString(textBuffer, pos);
+     textBuffer.resize(pos);
+-    return 0;
++    return FastScanNameResult(0);
+ }
+ 
+ enum NameChar { NameBeginning, NameNotBeginning, NotName };
+@@ -1878,6 +1881,14 @@ void QXmlStreamReaderPrivate::raiseWellF
+     raiseError(QXmlStreamReader::NotWellFormedError, message);
+ }
+ 
++void QXmlStreamReaderPrivate::raiseNamePrefixTooLongError()
++{
++    // TODO: add a ImplementationLimitsExceededError and use it instead
++    raiseError(QXmlStreamReader::NotWellFormedError,
++               QXmlStream::tr("Length of XML attribute name exceeds implementation limits (4KiB "
++                              "characters)."));
++}
++
+ void QXmlStreamReaderPrivate::parseError()
+ {
+ 
+--- a/src/corelib/serialization/qxmlstream.g
++++ b/src/corelib/serialization/qxmlstream.g
+@@ -516,7 +516,16 @@ public:
+     int fastScanLiteralContent();
+     int fastScanSpace();
+     int fastScanContentCharList();
+-    int fastScanName(int *prefix = nullptr);
++
++    struct FastScanNameResult {
++        FastScanNameResult() : ok(false) {}
++        explicit FastScanNameResult(int len) : addToLen(len), ok(true) { }
++        operator bool() { return ok; }
++        int operator*() { Q_ASSERT(ok); return addToLen; }
++        int addToLen;
++        bool ok;
++    };
++    FastScanNameResult fastScanName(Value *val = nullptr);
+     inline int fastScanNMTOKEN();
+ 
+ 
+@@ -525,6 +534,7 @@ public:
+ 
+     void raiseError(QXmlStreamReader::Error error, const QString& message = QString());
+     void raiseWellFormedError(const QString &message);
++    void raiseNamePrefixTooLongError();
+ 
+     QXmlStreamEntityResolver *entityResolver;
+ 
+@@ -1809,7 +1819,12 @@ space_opt ::= space;
+ qname ::= LETTER;
+ /.
+         case $rule_number: {
+-            sym(1).len += fastScanName(&sym(1).prefix);
++            Value &val = sym(1);
++            if (auto res = fastScanName(&val))
++                val.len += *res;
++            else
++                return false;
++
+             if (atEnd) {
+                 resume($rule_number);
+                 return false;
+@@ -1820,7 +1835,11 @@ qname ::= LETTER;
+ name ::= LETTER;
+ /.
+         case $rule_number:
+-            sym(1).len += fastScanName();
++            if (auto res = fastScanName())
++                sym(1).len += *res;
++            else
++                return false;
++
+             if (atEnd) {
+                 resume($rule_number);
+                 return false;
+--- a/src/corelib/serialization/qxmlstream_p.h
++++ b/src/corelib/serialization/qxmlstream_p.h
+@@ -1005,7 +1005,16 @@ public:
+     int fastScanLiteralContent();
+     int fastScanSpace();
+     int fastScanContentCharList();
+-    int fastScanName(int *prefix = nullptr);
++
++    struct FastScanNameResult {
++        FastScanNameResult() : ok(false) {}
++        explicit FastScanNameResult(int len) : addToLen(len), ok(true) { }
++        operator bool() { return ok; }
++        int operator*() { Q_ASSERT(ok); return addToLen; }
++        int addToLen;
++        bool ok;
++    };
++    FastScanNameResult fastScanName(Value *val = nullptr);
+     inline int fastScanNMTOKEN();
+ 
+ 
+@@ -1014,6 +1023,7 @@ public:
+ 
+     void raiseError(QXmlStreamReader::Error error, const QString& message = QString());
+     void raiseWellFormedError(const QString &message);
++    void raiseNamePrefixTooLongError();
+ 
+     QXmlStreamEntityResolver *entityResolver;
+ 
+@@ -1937,7 +1947,12 @@ bool QXmlStreamReaderPrivate::parse()
+         break;
+ 
+         case 262: {
+-            sym(1).len += fastScanName(&sym(1).prefix);
++            Value &val = sym(1);
++            if (auto res = fastScanName(&val))
++                val.len += *res;
++            else
++                return false;
++
+             if (atEnd) {
+                 resume(262);
+                 return false;
+@@ -1945,7 +1960,11 @@ bool QXmlStreamReaderPrivate::parse()
+         } break;
+ 
+         case 263:
+-            sym(1).len += fastScanName();
++            if (auto res = fastScanName())
++                sym(1).len += *res;
++            else
++                return false;
++
+             if (atEnd) {
+                 resume(263);
+                 return false;
+--- a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp
++++ b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp
+@@ -39,6 +39,7 @@
+ 
+ #include "qc14n.h"
+ 
++Q_DECLARE_METATYPE(QXmlStreamReader::Error)
+ Q_DECLARE_METATYPE(QXmlStreamReader::ReadElementTextBehaviour)
+ 
+ static const char *const catalogFile = "XML-Test-Suite/xmlconf/finalCatalog.xml";
+@@ -580,6 +581,8 @@ private slots:
+     void readBack() const;
+     void roundTrip() const;
+     void roundTrip_data() const;
++    void test_fastScanName_data() const;
++    void test_fastScanName() const;
+ 
+     void entityExpansionLimit() const;
+ 
+@@ -1812,5 +1815,42 @@ void tst_QXmlStream::roundTrip() const
+     QCOMPARE(out, in);
+ }
+ 
++void tst_QXmlStream::test_fastScanName_data() const
++{
++    QTest::addColumn<QByteArray>("data");
++    QTest::addColumn<QXmlStreamReader::Error>("errorType");
++
++    // 4096 is the limit in QXmlStreamReaderPrivate::fastScanName()
++
++    QByteArray arr = "<a:" + QByteArray("b").repeated(4096 - 1);
++    QTest::newRow("data1") << arr << QXmlStreamReader::PrematureEndOfDocumentError;
++
++    arr = "<a:" + QByteArray("b").repeated(4096);
++    QTest::newRow("data2") << arr << QXmlStreamReader::NotWellFormedError;
++
++    arr = "<" + QByteArray("a").repeated(4000) + ":" + QByteArray("b").repeated(96);
++    QTest::newRow("data3") << arr << QXmlStreamReader::PrematureEndOfDocumentError;
++
++    arr = "<" + QByteArray("a").repeated(4000) + ":" + QByteArray("b").repeated(96 + 1);
++    QTest::newRow("data4") << arr << QXmlStreamReader::NotWellFormedError;
++
++    arr = "<" + QByteArray("a").repeated(4000 + 1) + ":" + QByteArray("b").repeated(96);
++    QTest::newRow("data5") << arr << QXmlStreamReader::NotWellFormedError;
++}
++
++void tst_QXmlStream::test_fastScanName() const
++{
++    QFETCH(QByteArray, data);
++    QFETCH(QXmlStreamReader::Error, errorType);
++
++    QXmlStreamReader reader(data);
++    QXmlStreamReader::TokenType tokenType;
++    while (!reader.atEnd())
++        tokenType = reader.readNext();
++
++    QCOMPARE(tokenType, QXmlStreamReader::Invalid);
++    QCOMPARE(reader.error(), errorType);
++}
++
+ #include "tst_qxmlstream.moc"
+ // vim: et:ts=4:sw=4:sts=4
diff --git a/debian/patches/CVE-2023-38197.diff b/debian/patches/CVE-2023-38197.diff
new file mode 100644
index 0000000..99acf0c
--- /dev/null
+++ b/debian/patches/CVE-2023-38197.diff
@@ -0,0 +1,364 @@
+Description: QXmlStreamReader: Raise error on unexpected tokens
+ QXmlStreamReader accepted multiple DOCTYPE elements, containing DTD
+ fragments in the XML prolog, and in the XML body.
+ Well-formed but invalid XML files - with multiple DTD fragments in
+ prolog and body, combined with recursive entity expansions - have
+ caused infinite loops in QXmlStreamReader.
+ .
+ This patch implements a token check in QXmlStreamReader.
+ A stream is allowed to start with an XML prolog. StartDocument
+ and DOCTYPE elements are only allowed in this prolog, which
+ may also contain ProcessingInstruction and Comment elements.
+ As soon as anything else is seen, the prolog ends.
+ After that, the prolog-specific elements are treated as unexpected.
+ Furthermore, the prolog can contain at most one DOCTYPE element.
+ .
+ Update the documentation to reflect the new behavior.
+ Add an autotest that checks the new error cases are correctly detected,
+ and no error is raised for legitimate input.
+ .
+ The original OSS-Fuzz files (see bug reports) are not included in this
+ patch for file size reasons. They have been tested manually. Each of
+ them has more than one DOCTYPE element, causing infinite loops in
+ recursive entity expansions. The newly implemented functionality
+ detects those invalid DTD fragments. By raising an error, it aborts
+ stream reading before an infinite loop occurs.
+ .
+ Thanks to OSS-Fuzz for finding this.
+Origin: upstream, https://download.qt.io/official_releases/qt/5.15/CVE-2023-38197-qtbase-5.15.diff
+Last-Update: 2023-07-15
+
+--- a/src/corelib/serialization/qxmlstream.cpp
++++ b/src/corelib/serialization/qxmlstream.cpp
+@@ -160,7 +160,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 +295,34 @@ QXmlStreamEntityResolver *QXmlStreamRead
+ 
+   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 +641,7 @@ QXmlStreamReader::TokenType QXmlStreamRe
+         d->token = -1;
+         return readNext();
+     }
++    d->checkToken();
+     return d->type;
+ }
+ 
+@@ -740,6 +762,14 @@ static const short QXmlStreamReader_toke
+ };
+ 
+ 
++static const char QXmlStreamReader_XmlContextString[] =
++    "Prolog\0"
++    "Body\0";
++
++static const short QXmlStreamReader_XmlContextString_indices[] = {
++    0, 7
++};
++
+ /*!
+     \property  QXmlStreamReader::namespaceProcessing
+     The namespace-processing flag of the stream reader
+@@ -775,6 +805,16 @@ QString QXmlStreamReader::tokenString()
+                          QXmlStreamReader_tokenTypeString_indices[d->type]);
+ }
+ 
++/*!
++   \internal
++   \return \param ctxt (Prolog/Body) as a string.
++ */
++QString contextString(QXmlStreamReaderPrivate::XmlContext ctxt)
++{
++    return QLatin1String(QXmlStreamReader_XmlContextString +
++                         QXmlStreamReader_XmlContextString_indices[static_cast<int>(ctxt)]);
++}
++
+ #endif // QT_NO_XMLSTREAMREADER
+ 
+ QXmlStreamPrivateTagStack::QXmlStreamPrivateTagStack()
+@@ -866,6 +906,8 @@ void QXmlStreamReaderPrivate::init()
+ 
+     type = QXmlStreamReader::NoToken;
+     error = QXmlStreamReader::NoError;
++    currentContext = XmlContext::Prolog;
++    foundDTD = false;
+ }
+ 
+ /*
+@@ -4061,6 +4103,92 @@ void QXmlStreamWriter::writeCurrentToken
+     }
+ }
+ 
++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;
++    default:
++        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,
++                   QLatin1String("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,
++                   QLatin1String("Found second DTD token in %1.").arg(contextString(context)));
++    } else {
++        foundDTD = true;
++    }
++}
++
+ /*!
+  \fn bool QXmlStreamAttributes::hasAttribute(const QString &qualifiedName) const
+  \since 4.5
+--- 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()
+      */
+--- /dev/null
++++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/dtdInBody.xml
+@@ -0,0 +1,20 @@
++<!DOCTYPE TEST [
++   <!ELEMENT TESTATTRIBUTE (CASE+)>
++   <!ELEMENT CASE (CLASS, FUNCTION)>
++   <!ELEMENT CLASS (#PCDATA)>
++
++   <!-- adding random ENTITY statement, as this is typical DTD content -->
++   <!ENTITY unite "&#x222a;">
++
++   <!ATTLIST CASE CLASS CDATA #REQUIRED>
++]>
++<TEST>
++  <CASE>
++    <CLASS>tst_QXmlStream</CLASS>
++  </CASE>
++  <!-- invalid DTD in XML body follows -->
++  <!DOCTYPE DTDTEST [
++    <!ELEMENT RESULT (CASE+)>
++    <!ATTLIST RESULT OUTPUT CDATA #REQUIRED>
++  ]>
++</TEST>
+--- /dev/null
++++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/multipleDtd.xml
+@@ -0,0 +1,20 @@
++<!DOCTYPE TEST [
++   <!ELEMENT TESTATTRIBUTE (CASE+)>
++   <!ELEMENT CASE (CLASS, FUNCTION, DATASET, COMMENTS)>
++   <!ELEMENT CLASS (#PCDATA)>
++
++   <!-- adding random ENTITY statements, as this is typical DTD content -->
++   <!ENTITY iff "&hArr;">
++
++   <!ATTLIST CASE CLASS CDATA #REQUIRED>
++]>
++<!-- invalid second DTD follows -->
++<!DOCTYPE SECOND [
++   <!ELEMENT SECONDATTRIBUTE (#PCDATA)>
++   <!ENTITY on "&#8728;">
++]>
++<TEST>
++  <CASE>
++    <CLASS>tst_QXmlStream</CLASS>
++  </CASE>
++</TEST>
+--- /dev/null
++++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/wellFormed.xml
+@@ -0,0 +1,15 @@
++<!DOCTYPE TEST [
++   <!ELEMENT TESTATTRIBUTE (CASE+)>
++   <!ELEMENT CASE (CLASS, FUNCTION, DATASET, COMMENTS)>
++   <!ELEMENT CLASS (#PCDATA)>
++
++   <!-- adding random ENTITY statements, as this is typical DTD content -->
++   <!ENTITY unite "&#x222a;">
++
++   <!ATTLIST CASE CLASS CDATA #REQUIRED>
++]>
++<TEST>
++  <CASE>
++    <CLASS>tst_QXmlStream</CLASS>
++  </CASE>
++</TEST>
+--- a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp
++++ b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp
+@@ -586,6 +586,9 @@ private slots:
+ 
+     void entityExpansionLimit() const;
+ 
++    void tokenErrorHandling_data() const;
++    void tokenErrorHandling() const;
++
+ private:
+     static QByteArray readFile(const QString &filename);
+ 
+@@ -1852,5 +1855,42 @@ void tst_QXmlStream::test_fastScanName()
+     QCOMPARE(reader.error(), errorType);
+ }
+ 
++void tst_QXmlStream::tokenErrorHandling_data() const
++{
++    qRegisterMetaType<QXmlStreamReader::Error>();
++    QTest::addColumn<QString>("fileName");
++    QTest::addColumn<QXmlStreamReader::Error>("expectedError");
++    QTest::addColumn<QString>("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
diff --git a/debian/patches/series b/debian/patches/series
index b3c675f..52659ab 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -18,6 +18,11 @@ qshapedpixmapwindow_no_tooltip.diff
 CVE-2023-32763.diff
 CVE-2023-32762.diff
 CVE-2023-33285.diff
+sql_odbc_more_unicode_checks.diff
+sql_odbc_fix_unicode_check.diff
+CVE-2023-34410.diff
+CVE-2023-37369.diff
+CVE-2023-38197.diff
 
 # Debian specific.
 gnukfreebsd.diff
diff --git a/debian/patches/sql_odbc_fix_unicode_check.diff b/debian/patches/sql_odbc_fix_unicode_check.diff
new file mode 100644
index 0000000..dd27b32
--- /dev/null
+++ b/debian/patches/sql_odbc_fix_unicode_check.diff
@@ -0,0 +1,32 @@
+Description: QSQL/ODBC: fix regression (trailing NUL)
+ When we fixed the callers of toSQLTCHAR() to use the result's size()
+ instead of the input's (which differ, if sizeof(SQLTCHAR) != 2), we
+ exposed callers to the append(0), which changes the size() of the
+ result QVLA. Callers that don't rely on NUL-termination (all?) now saw
+ an additional training NUL.
+ .
+ Fix by not NUL-terminating, and changing the only user of SQL_NTS to
+ use an explicit length.
+Origin: upstream, https://code.qt.io/cgit/qt/qtbase.git/commit/?id=9020034b3b6a3a81
+Last-Update: 2023-06-30
+
+--- a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
++++ b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
+@@ -125,7 +125,6 @@ inline static QVarLengthArray<SQLTCHAR>
+ {
+     QVarLengthArray<SQLTCHAR> result;
+     toSQLTCHARImpl(result, input);
+-    result.append(0); // make sure it's null terminated, doesn't matter if it already is, it does if it isn't.
+     return result;
+ }
+ 
+@@ -2119,7 +2118,8 @@ void QODBCDriverPrivate::checkUnicode()
+         QLatin1String("select 'test' from dual"),
+     };
+     for (const auto &statement : statements) {
+-        r = SQLExecDirect(hStmt, toSQLTCHAR(statement).data(), SQL_NTS);
++        auto encoded = toSQLTCHAR(statement);
++        r = SQLExecDirect(hStmt, encoded.data(), SQLINTEGER(encoded.size()));
+         if (r == SQL_SUCCESS)
+             break;
+     }
diff --git a/debian/patches/sql_odbc_more_unicode_checks.diff b/debian/patches/sql_odbc_more_unicode_checks.diff
new file mode 100644
index 0000000..de7d2d9
--- /dev/null
+++ b/debian/patches/sql_odbc_more_unicode_checks.diff
@@ -0,0 +1,35 @@
+Description: SQL/ODBC: add another check to detect unicode availability in driver
+ Since ODBC does not have a direct way finding out if unicode is
+ supported by the underlying driver the ODBC plugin does some checks. As
+ a last resort a sql statement is executed which returns a string. But
+ even this may fail because the select statement has no FROM part which
+ is rejected by at least Oracle does not allow. Therefore add another
+ query which is correct for Oracle & DB2 as a workaround. The question
+ why the first three statements to check for unicode availability fail
+ is still open but can't be checked since I've no access to an oracle
+ database.
+Origin: upstream, https://code.qt.io/cgit/qt/qtbase.git/commit/?id=f19320748d282b1e
+Last-Update: 2023-06-30
+
+--- a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
++++ b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
+@@ -2111,7 +2111,18 @@ void QODBCDriverPrivate::checkUnicode()
+                                   hDbc,
+                                   &hStmt);
+ 
+-    r = SQLExecDirect(hStmt, toSQLTCHAR(QLatin1String("select 'test'")).data(), SQL_NTS);
++    // for databases which do not return something useful in SQLGetInfo and are picky about a
++    // 'SELECT' statement without 'FROM' but support VALUE(foo) statement like e.g. DB2 or Oracle
++    const auto statements = {
++        QLatin1String("select 'test'"),
++        QLatin1String("values('test')"),
++        QLatin1String("select 'test' from dual"),
++    };
++    for (const auto &statement : statements) {
++        r = SQLExecDirect(hStmt, toSQLTCHAR(statement).data(), SQL_NTS);
++        if (r == SQL_SUCCESS)
++            break;
++    }
+     if(r == SQL_SUCCESS) {
+         r = SQLFetch(hStmt);
+         if(r == SQL_SUCCESS) {

Attachment: signature.asc
Description: PGP signature


--- End Message ---
--- Begin Message ---
Version: 12.6

The upload requested in this bug has been released as part of 12.6.

--- End Message ---

Reply via email to