Version in base suite: 5.15.8+dfsg-11 Base version: qtbase-opensource-src_5.15.8+dfsg-11 Target version: qtbase-opensource-src_5.15.8+dfsg-11+deb12u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/q/qtbase-opensource-src/qtbase-opensource-src_5.15.8+dfsg-11.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/q/qtbase-opensource-src/qtbase-opensource-src_5.15.8+dfsg-11+deb12u1.dsc changelog | 16 + patches/CVE-2023-34410.diff | 34 ++ patches/CVE-2023-37369.diff | 289 +++++++++++++++++++++++ patches/CVE-2023-38197.diff | 364 ++++++++++++++++++++++++++++++ patches/series | 5 patches/sql_odbc_fix_unicode_check.diff | 32 ++ patches/sql_odbc_more_unicode_checks.diff | 35 ++ 7 files changed, 775 insertions(+) diff -Nru qtbase-opensource-src-5.15.8+dfsg/debian/changelog qtbase-opensource-src-5.15.8+dfsg/debian/changelog --- qtbase-opensource-src-5.15.8+dfsg/debian/changelog 2023-05-25 10:45:05.000000000 +0000 +++ qtbase-opensource-src-5.15.8+dfsg/debian/changelog 2024-04-07 09:45:51.000000000 +0000 @@ -1,3 +1,19 @@ +qtbase-opensource-src (5.15.8+dfsg-11+deb12u1) bookworm; 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). + + -- Dmitry Shachnev Sun, 07 Apr 2024 12:45:51 +0300 + qtbase-opensource-src (5.15.8+dfsg-11) unstable; urgency=medium * Rename the patches for consistency and add DEP-3 headers. diff -Nru qtbase-opensource-src-5.15.8+dfsg/debian/patches/CVE-2023-34410.diff qtbase-opensource-src-5.15.8+dfsg/debian/patches/CVE-2023-34410.diff --- qtbase-opensource-src-5.15.8+dfsg/debian/patches/CVE-2023-34410.diff 1970-01-01 00:00:00.000000000 +0000 +++ qtbase-opensource-src-5.15.8+dfsg/debian/patches/CVE-2023-34410.diff 2024-04-07 09:45:51.000000000 +0000 @@ -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 -Nru qtbase-opensource-src-5.15.8+dfsg/debian/patches/CVE-2023-37369.diff qtbase-opensource-src-5.15.8+dfsg/debian/patches/CVE-2023-37369.diff --- qtbase-opensource-src-5.15.8+dfsg/debian/patches/CVE-2023-37369.diff 1970-01-01 00:00:00.000000000 +0000 +++ qtbase-opensource-src-5.15.8+dfsg/debian/patches/CVE-2023-37369.diff 2024-04-07 09:45:51.000000000 +0000 @@ -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("data"); ++ QTest::addColumn("errorType"); ++ ++ // 4096 is the limit in QXmlStreamReaderPrivate::fastScanName() ++ ++ QByteArray arr = "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(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 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++]> ++ ++ ++ tst_QXmlStream ++ ++ ++ ++ ++ ]> ++ +--- /dev/null ++++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/multipleDtd.xml +@@ -0,0 +1,20 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++]> ++ ++ ++ ++]> ++ ++ ++ tst_QXmlStream ++ ++ +--- /dev/null ++++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/wellFormed.xml +@@ -0,0 +1,15 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++]> ++ ++ ++ tst_QXmlStream ++ ++ +--- 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(); ++ 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 diff -Nru qtbase-opensource-src-5.15.8+dfsg/debian/patches/series qtbase-opensource-src-5.15.8+dfsg/debian/patches/series --- qtbase-opensource-src-5.15.8+dfsg/debian/patches/series 2023-05-25 10:45:05.000000000 +0000 +++ qtbase-opensource-src-5.15.8+dfsg/debian/patches/series 2024-04-07 09:45:51.000000000 +0000 @@ -18,6 +18,11 @@ 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 -Nru qtbase-opensource-src-5.15.8+dfsg/debian/patches/sql_odbc_fix_unicode_check.diff qtbase-opensource-src-5.15.8+dfsg/debian/patches/sql_odbc_fix_unicode_check.diff --- qtbase-opensource-src-5.15.8+dfsg/debian/patches/sql_odbc_fix_unicode_check.diff 1970-01-01 00:00:00.000000000 +0000 +++ qtbase-opensource-src-5.15.8+dfsg/debian/patches/sql_odbc_fix_unicode_check.diff 2024-04-07 09:45:51.000000000 +0000 @@ -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 + { + QVarLengthArray 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 -Nru qtbase-opensource-src-5.15.8+dfsg/debian/patches/sql_odbc_more_unicode_checks.diff qtbase-opensource-src-5.15.8+dfsg/debian/patches/sql_odbc_more_unicode_checks.diff --- qtbase-opensource-src-5.15.8+dfsg/debian/patches/sql_odbc_more_unicode_checks.diff 1970-01-01 00:00:00.000000000 +0000 +++ qtbase-opensource-src-5.15.8+dfsg/debian/patches/sql_odbc_more_unicode_checks.diff 2024-04-07 09:45:51.000000000 +0000 @@ -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) {