--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bullseye
User: release.debian....@packages.debian.org
Usertags: pu
The attached debdiff for qtbase-opensource-src fixes several CVEs in
Bullseye. All CVEs are marked as no-dsa by the security team.
Thorsten
diff -Nru qtbase-opensource-src-5.15.2+dfsg/debian/changelog
qtbase-opensource-src-5.15.2+dfsg/debian/changelog
--- qtbase-opensource-src-5.15.2+dfsg/debian/changelog 2021-07-02
17:58:04.000000000 +0200
+++ qtbase-opensource-src-5.15.2+dfsg/debian/changelog 2024-04-28
22:48:02.000000000 +0200
@@ -1,3 +1,33 @@
+qtbase-opensource-src (5.15.2+dfsg-9+deb11u1) bullseye; urgency=medium
+
+ * Non-maintainer upload by the LTS Team.
+ * CVE-2024-25580 (Closes: #1064053)
+ fix buffer overflow due to crafted KTX image file
+ * CVE-2023-32763 (Closes: #1036702)
+ fix QTextLayout buffer overflow due to crafted SVG file
+ * CVE-2022-25255
+ prevent QProcess from execution of a binary from the current working
+ directory when not found in the PATH
+ * CVE-2023-24607 (Closes: #1031872)
+ fix denial of service via a crafted string when the SQL ODBC driver
+ plugin is used
+ * fix regression caused by patch for CVE-2023-24607
+ * CVE-2023-32762
+ prevent incorrect parsing of the strict-transport-security (HSTS) header
+ * CVE-2023-51714 (Closes: #1060694)
+ fix incorrect HPack integer overflow check.
+ * CVE-2023-38197 (Closes: #1041105)
+ fix infinite loop in recursive entity expansion
+ * CVE-2023-37369 (Closes: #1059302)
+ fix crash of application in QXmlStreamReader due to crafted XML string
+ * CVE-2023-34410 (Closes: #1037210)
+ fix checking during TLS whether root of the chain really is a
+ configured CA certificate
+ * CVE-2023-33285 (Closes: #1036848)
+ fix buffer overflow in QDnsLookup
+
+ -- Thorsten Alteholz <deb...@alteholz.de> Sun, 28 Apr 2024 22:48:02 +0200
+
qtbase-opensource-src (5.15.2+dfsg-9) unstable; urgency=medium
* Revert adding fix-misplacement-of-placeholder-text-in-QLineEdit.diff.
diff -Nru qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2022-25255.diff
qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2022-25255.diff
--- qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2022-25255.diff
1970-01-01 01:00:00.000000000 +0100
+++ qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2022-25255.diff
2024-03-05 13:22:01.000000000 +0100
@@ -0,0 +1,96 @@
+Description: QProcess: ensure we don't accidentally execute something from CWD
+ Unless "." (or the empty string) is in $PATH, we're not supposed to find
+ executables in the current directory. This is how the Unix shells behave
+ and we match their behavior. It's also the behavior Qt had prior to 5.9
+ (commit 28666d167aa8e602c0bea25ebc4d51b55005db13). On Windows, searching
+ the current directory is the norm, so we keep that behavior.
+ .
+ This commit does not add an explicit check for an empty return from
+ QStandardPaths::findExecutable(). Instead, we allow that empty string to
+ go all the way to execve(2), which will fail with ENOENT. We could catch
+ it early, before fork(2), but why add code for the error case?
+ .
+ See https://kde.org/info/security/advisory-20220131-1.txt
+Origin: upstream,
https://download.qt.io/official_releases/qt/5.15/CVE-2022-25255-qprocess5-15.diff
+Last-Update: 2022-02-21
+
+Index: qtbase-opensource-src-5.15.2+dfsg/src/corelib/io/qprocess_unix.cpp
+===================================================================
+--- qtbase-opensource-src-5.15.2+dfsg.orig/src/corelib/io/qprocess_unix.cpp
2024-03-05 13:21:06.432881985 +0100
++++ qtbase-opensource-src-5.15.2+dfsg/src/corelib/io/qprocess_unix.cpp
2024-03-05 13:21:06.428881981 +0100
+@@ -1,7 +1,7 @@
+ /****************************************************************************
+ **
+ ** Copyright (C) 2016 The Qt Company Ltd.
+-** Copyright (C) 2016 Intel Corporation.
++** Copyright (C) 2022 Intel Corporation.
+ ** Contact: https://www.qt.io/licensing/
+ **
+ ** This file is part of the QtCore module of the Qt Toolkit.
+@@ -422,14 +422,15 @@
+ // Add the program name to the argument list.
+ argv[0] = nullptr;
+ if (!program.contains(QLatin1Char('/'))) {
++ // findExecutable() returns its argument if it's an absolute path,
++ // otherwise it searches $PATH; returns empty if not found (we handle
++ // that case much later)
+ const QString &exeFilePath = QStandardPaths::findExecutable(program);
+- if (!exeFilePath.isEmpty()) {
+- const QByteArray &tmp = QFile::encodeName(exeFilePath);
+- argv[0] = ::strdup(tmp.constData());
+- }
+- }
+- if (!argv[0])
++ const QByteArray &tmp = QFile::encodeName(exeFilePath);
++ argv[0] = ::strdup(tmp.constData());
++ } else {
+ argv[0] = ::strdup(encodedProgramName.constData());
++ }
+
+ // Add every argument to the list
+ for (int i = 0; i < arguments.count(); ++i)
+@@ -983,15 +984,16 @@
+ envp = _q_dupEnvironment(environment.d.constData()->vars,
&envc);
+ }
+
+- QByteArray tmp;
+ if (!program.contains(QLatin1Char('/'))) {
++ // findExecutable() returns its argument if it's an absolute
path,
++ // otherwise it searches $PATH; returns empty if not found
(we handle
++ // that case much later)
+ const QString &exeFilePath =
QStandardPaths::findExecutable(program);
+- if (!exeFilePath.isEmpty())
+- tmp = QFile::encodeName(exeFilePath);
++ const QByteArray &tmp = QFile::encodeName(exeFilePath);
++ argv[0] = ::strdup(tmp.constData());
++ } else {
++ argv[0] = ::strdup(QFile::encodeName(program));
+ }
+- if (tmp.isEmpty())
+- tmp = QFile::encodeName(program);
+- argv[0] = tmp.data();
+
+ if (envp)
+ qt_safe_execve(argv[0], argv, envp);
+Index:
qtbase-opensource-src-5.15.2+dfsg/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
+===================================================================
+---
qtbase-opensource-src-5.15.2+dfsg.orig/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
2024-03-05 13:21:06.432881985 +0100
++++
qtbase-opensource-src-5.15.2+dfsg/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp
2024-03-05 13:21:57.744931492 +0100
+@@ -1449,7 +1449,7 @@
+ {
+ #if QT_CONFIG(process)
+ QProcess testProcess;
+- testProcess.start("desktopsettingsaware_helper");
++ testProcess.start("./desktopsettingsaware_helper");
+ QVERIFY2(testProcess.waitForStarted(),
+ qPrintable(QString::fromLatin1("Cannot start
'desktopsettingsaware_helper': %1").arg(testProcess.errorString())));
+ QVERIFY(testProcess.waitForFinished(10000));
+@@ -2365,7 +2365,7 @@
+ #if QT_CONFIG(process)
+ QProcess testProcess;
+ QStringList arguments;
+- testProcess.start("modal_helper", arguments);
++ testProcess.start("./modal_helper", arguments);
+ QVERIFY2(testProcess.waitForStarted(),
+ qPrintable(QString::fromLatin1("Cannot start 'modal_helper':
%1").arg(testProcess.errorString())));
+ QVERIFY(testProcess.waitForFinished(20000));
diff -Nru qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-24607.diff
qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-24607.diff
--- qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-24607.diff
1970-01-01 01:00:00.000000000 +0100
+++ qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-24607.diff
2024-03-05 13:24:50.000000000 +0100
@@ -0,0 +1,332 @@
+Description: Fix denial-of-service in Qt SQL ODBC driver plugin
+Origin: upstream,
https://download.qt.io/official_releases/qt/5.15/CVE-2023-24607-qtbase-5.15.diff
+Last-Update: 2023-02-26
+
+Index:
qtbase-opensource-src-5.15.2+dfsg/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
+===================================================================
+---
qtbase-opensource-src-5.15.2+dfsg.orig/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
2024-03-05 13:24:44.661090169 +0100
++++
qtbase-opensource-src-5.15.2+dfsg/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
2024-03-05 13:24:44.657090165 +0100
+@@ -92,23 +92,39 @@
+ return result;
+ }
+
++template <size_t SizeOfChar = sizeof(SQLTCHAR)>
++void toSQLTCHARImpl(QVarLengthArray<SQLTCHAR> &result, const QString &input);
// primary template undefined
++
++template <typename Container>
++void do_append(QVarLengthArray<SQLTCHAR> &result, const Container &c)
++{
++ result.append(reinterpret_cast<const SQLTCHAR *>(c.data()), c.size());
++}
++
++template <>
++void toSQLTCHARImpl<1>(QVarLengthArray<SQLTCHAR> &result, const QString
&input)
++{
++ const auto u8 = input.toUtf8();
++ do_append(result, u8);
++}
++
++template <>
++void toSQLTCHARImpl<2>(QVarLengthArray<SQLTCHAR> &result, const QString
&input)
++{
++ do_append(result, input);
++}
++
++template <>
++void toSQLTCHARImpl<4>(QVarLengthArray<SQLTCHAR> &result, const QString
&input)
++{
++ const auto u32 = input.toUcs4();
++ do_append(result, u32);
++}
++
+ inline static QVarLengthArray<SQLTCHAR> toSQLTCHAR(const QString &input)
+ {
+ QVarLengthArray<SQLTCHAR> result;
+- result.resize(input.size());
+- switch(sizeof(SQLTCHAR)) {
+- case 1:
+- memcpy(result.data(), input.toUtf8().data(), input.size());
+- break;
+- case 2:
+- memcpy(result.data(), input.unicode(), input.size() * 2);
+- break;
+- case 4:
+- memcpy(result.data(), input.toUcs4().data(), input.size() * 4);
+- break;
+- default:
+- qCritical("sizeof(SQLTCHAR) is %d. Don't know how to handle
this.", int(sizeof(SQLTCHAR)));
+- }
++ 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;
+ }
+@@ -763,6 +779,14 @@
+ return quote;
+ }
+
++static SQLRETURN qt_string_SQLSetConnectAttr(SQLHDBC handle, SQLINTEGER attr,
const QString &val)
++{
++ auto encoded = toSQLTCHAR(val);
++ return SQLSetConnectAttr(handle, attr,
++ encoded.data(),
++ SQLINTEGER(encoded.size() * sizeof(SQLTCHAR)));
// size in bytes
++}
++
+
+ bool QODBCDriverPrivate::setConnectionOptions(const QString& connOpts)
+ {
+@@ -798,10 +822,7 @@
+ v = val.toUInt();
+ r = SQLSetConnectAttr(hDbc, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER)
size_t(v), 0);
+ } else if (opt.toUpper() ==
QLatin1String("SQL_ATTR_CURRENT_CATALOG")) {
+- val.utf16(); // 0 terminate
+- r = SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG,
+- toSQLTCHAR(val).data(),
+- val.length()*sizeof(SQLTCHAR));
++ r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_CURRENT_CATALOG,
val);
+ } else if (opt.toUpper() == QLatin1String("SQL_ATTR_METADATA_ID")) {
+ if (val.toUpper() == QLatin1String("SQL_TRUE")) {
+ v = SQL_TRUE;
+@@ -816,10 +837,7 @@
+ v = val.toUInt();
+ r = SQLSetConnectAttr(hDbc, SQL_ATTR_PACKET_SIZE, (SQLPOINTER)
size_t(v), 0);
+ } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACEFILE")) {
+- val.utf16(); // 0 terminate
+- r = SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE,
+- toSQLTCHAR(val).data(),
+- val.length()*sizeof(SQLTCHAR));
++ r = qt_string_SQLSetConnectAttr(hDbc, SQL_ATTR_TRACEFILE, val);
+ } else if (opt.toUpper() == QLatin1String("SQL_ATTR_TRACE")) {
+ if (val.toUpper() == QLatin1String("SQL_OPT_TRACE_OFF")) {
+ v = SQL_OPT_TRACE_OFF;
+@@ -1022,9 +1040,12 @@
+ return false;
+ }
+
+- r = SQLExecDirect(d->hStmt,
+- toSQLTCHAR(query).data(),
+- (SQLINTEGER) query.length());
++ {
++ auto encoded = toSQLTCHAR(query);
++ r = SQLExecDirect(d->hStmt,
++ encoded.data(),
++ SQLINTEGER(encoded.size()));
++ }
+ if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO && r!= SQL_NO_DATA) {
+ setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
+ "Unable to execute statement"),
QSqlError::StatementError, d));
+@@ -1370,9 +1391,12 @@
+ return false;
+ }
+
+- r = SQLPrepare(d->hStmt,
+- toSQLTCHAR(query).data(),
+- (SQLINTEGER) query.length());
++ {
++ auto encoded = toSQLTCHAR(query);
++ r = SQLPrepare(d->hStmt,
++ encoded.data(),
++ SQLINTEGER(encoded.size()));
++ }
+
+ if (r != SQL_SUCCESS) {
+ setLastError(qMakeError(QCoreApplication::translate("QODBCResult",
+@@ -1400,7 +1424,7 @@
+ SQLCloseCursor(d->hStmt);
+
+ QVector<QVariant>& values = boundValues();
+- QVector<QByteArray> tmpStorage(values.count(), QByteArray()); // holds
temporary buffers
++ QVector<QByteArray> tmpStorage(values.count(), QByteArray()); // targets
for SQLBindParameter()
+ QVarLengthArray<SQLLEN, 32> indicators(values.count());
+ memset(indicators.data(), 0, indicators.size() * sizeof(SQLLEN));
+
+@@ -1579,35 +1603,36 @@
+ case QVariant::String:
+ if (d->unicode) {
+ QByteArray &ba = tmpStorage[i];
+- QString str = val.toString();
++ {
++ const auto encoded = toSQLTCHAR(val.toString());
++ ba = QByteArray(reinterpret_cast<const char
*>(encoded.data()),
++ encoded.size() * sizeof(SQLTCHAR));
++ }
++
+ if (*ind != SQL_NULL_DATA)
+- *ind = str.length() * sizeof(SQLTCHAR);
+- int strSize = str.length() * sizeof(SQLTCHAR);
++ *ind = ba.size();
+
+ if (bindValueType(i) & QSql::Out) {
+- const QVarLengthArray<SQLTCHAR> a(toSQLTCHAR(str));
+- ba = QByteArray((const char *)a.constData(), a.size()
* sizeof(SQLTCHAR));
+ r = SQLBindParameter(d->hStmt,
+ i + 1,
+ qParamType[bindValueType(i) &
QSql::InOut],
+ SQL_C_TCHAR,
+- strSize > 254 ? SQL_WLONGVARCHAR
: SQL_WVARCHAR,
++ ba.size() > 254 ?
SQL_WLONGVARCHAR : SQL_WVARCHAR,
+ 0, // god knows... don't change
this!
+ 0,
+- ba.data(),
++ const_cast<char
*>(ba.constData()), // don't detach
+ ba.size(),
+ ind);
+ break;
+ }
+- ba = QByteArray ((const char
*)toSQLTCHAR(str).constData(), str.size()*sizeof(SQLTCHAR));
+ r = SQLBindParameter(d->hStmt,
+ i + 1,
+ qParamType[bindValueType(i) &
QSql::InOut],
+ SQL_C_TCHAR,
+- strSize > 254 ? SQL_WLONGVARCHAR :
SQL_WVARCHAR,
+- strSize,
++ ba.size() > 254 ? SQL_WLONGVARCHAR
: SQL_WVARCHAR,
++ ba.size(),
+ 0,
+- const_cast<char *>(ba.constData()),
++ const_cast<char *>(ba.constData()),
// don't detach
+ ba.size(),
+ ind);
+ break;
+@@ -1715,10 +1740,11 @@
+ case QVariant::String:
+ if (d->unicode) {
+ if (bindValueType(i) & QSql::Out) {
+- const QByteArray &first = tmpStorage.at(i);
+- QVarLengthArray<SQLTCHAR> array;
+- array.append((const SQLTCHAR *)first.constData(),
first.size());
+- values[i] = fromSQLTCHAR(array,
first.size()/sizeof(SQLTCHAR));
++ const QByteArray &bytes = tmpStorage.at(i);
++ const auto strSize = bytes.size() /
int(sizeof(SQLTCHAR));
++ QVarLengthArray<SQLTCHAR> string(strSize);
++ memcpy(string.data(), bytes.data(), strSize *
sizeof(SQLTCHAR));
++ values[i] = fromSQLTCHAR(string);
+ }
+ break;
+ }
+@@ -1965,14 +1991,16 @@
+ SQLSMALLINT cb;
+ QVarLengthArray<SQLTCHAR> connOut(1024);
+ memset(connOut.data(), 0, connOut.size() * sizeof(SQLTCHAR));
+- r = SQLDriverConnect(d->hDbc,
+- NULL,
+- toSQLTCHAR(connQStr).data(),
+- (SQLSMALLINT)connQStr.length(),
+- connOut.data(),
+- 1024,
+- &cb,
+- /*SQL_DRIVER_NOPROMPT*/0);
++ {
++ auto encoded = toSQLTCHAR(connQStr);
++ r = SQLDriverConnect(d->hDbc,
++ nullptr,
++ encoded.data(), SQLSMALLINT(encoded.size()),
++ connOut.data(),
++ 1024,
++ &cb,
++ /*SQL_DRIVER_NOPROMPT*/0);
++ }
+
+ if (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO) {
+ setLastError(qMakeError(tr("Unable to connect"),
QSqlError::ConnectionError, d));
+@@ -2351,17 +2379,15 @@
+ if (tableType.isEmpty())
+ return tl;
+
+- QString joinedTableTypeString = tableType.join(QLatin1Char(','));
++ {
++ auto joinedTableTypeString = toSQLTCHAR(tableType.join(u','));
+
+- r = SQLTables(hStmt,
+- NULL,
+- 0,
+- NULL,
+- 0,
+- NULL,
+- 0,
+- toSQLTCHAR(joinedTableTypeString).data(),
+- joinedTableTypeString.length() /* characters, not bytes
*/);
++ r = SQLTables(hStmt,
++ nullptr, 0,
++ nullptr, 0,
++ nullptr, 0,
++ joinedTableTypeString.data(),
joinedTableTypeString.size());
++ }
+
+ if (r != SQL_SUCCESS)
+ qSqlWarning(QLatin1String("QODBCDriver::tables Unable to execute
table list"), d);
+@@ -2435,28 +2461,30 @@
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER);
+- r = SQLPrimaryKeys(hStmt,
+- catalog.length() == 0 ? NULL :
toSQLTCHAR(catalog).data(),
+- catalog.length(),
+- schema.length() == 0 ? NULL :
toSQLTCHAR(schema).data(),
+- schema.length(),
+- toSQLTCHAR(table).data(),
+- table.length() /* in characters, not in bytes */);
++ {
++ auto c = toSQLTCHAR(catalog);
++ auto s = toSQLTCHAR(schema);
++ auto t = toSQLTCHAR(table);
++ r = SQLPrimaryKeys(hStmt,
++ catalog.isEmpty() ? nullptr : c.data(), c.size(),
++ schema.isEmpty() ? nullptr : s.data(), s.size(),
++ t.data(), t.size());
++ }
+
+ // if the SQLPrimaryKeys() call does not succeed (e.g the driver
+ // does not support it) - try an alternative method to get hold of
+ // the primary index (e.g MS Access and FoxPro)
+ if (r != SQL_SUCCESS) {
+- r = SQLSpecialColumns(hStmt,
+- SQL_BEST_ROWID,
+- catalog.length() == 0 ? NULL :
toSQLTCHAR(catalog).data(),
+- catalog.length(),
+- schema.length() == 0 ? NULL :
toSQLTCHAR(schema).data(),
+- schema.length(),
+- toSQLTCHAR(table).data(),
+- table.length(),
+- SQL_SCOPE_CURROW,
+- SQL_NULLABLE);
++ auto c = toSQLTCHAR(catalog);
++ auto s = toSQLTCHAR(schema);
++ auto t = toSQLTCHAR(table);
++ r = SQLSpecialColumns(hStmt,
++ SQL_BEST_ROWID,
++ catalog.isEmpty() ? nullptr : c.data(),
c.size(),
++ schema.isEmpty() ? nullptr : s.data(),
s.size(),
++ t.data(), t.size(),
++ SQL_SCOPE_CURROW,
++ SQL_NULLABLE);
+
+ if (r != SQL_SUCCESS) {
+ qSqlWarning(QLatin1String("QODBCDriver::primaryIndex: Unable
to execute primary key list"), d);
+@@ -2537,15 +2565,17 @@
+ SQL_ATTR_CURSOR_TYPE,
+ (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY,
+ SQL_IS_UINTEGER);
+- r = SQLColumns(hStmt,
+- catalog.length() == 0 ? NULL :
toSQLTCHAR(catalog).data(),
+- catalog.length(),
+- schema.length() == 0 ? NULL : toSQLTCHAR(schema).data(),
+- schema.length(),
+- toSQLTCHAR(table).data(),
+- table.length(),
+- NULL,
+- 0);
++ {
++ auto c = toSQLTCHAR(catalog);
++ auto s = toSQLTCHAR(schema);
++ auto t = toSQLTCHAR(table);
++ r = SQLColumns(hStmt,
++ catalog.isEmpty() ? nullptr : c.data(), c.size(),
++ schema.isEmpty() ? nullptr : s.data(), s.size(),
++ t.data(), t.size(),
++ nullptr,
++ 0);
++ }
+ if (r != SQL_SUCCESS)
+ qSqlWarning(QLatin1String("QODBCDriver::record: Unable to execute
column list"), d);
+
diff -Nru qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-32762.diff
qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-32762.diff
--- qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-32762.diff
1970-01-01 01:00:00.000000000 +0100
+++ qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-32762.diff
2024-03-05 13:48:39.000000000 +0100
@@ -0,0 +1,47 @@
+commit 1b736a815be0222f4b24289cf17575fc15707305
+Author: Mårten Nordheim <marten.nordh...@qt.io>
+Date: Fri May 5 11:07:26 2023 +0200
+
+ Hsts: match header names case insensitively
+
+ Header field names are always considered to be case-insensitive.
+
+ Pick-to: 6.5 6.5.1 6.2 5.15
+ Fixes: QTBUG-113392
+ Change-Id: Ifb4def4bb7f2ac070416cdc76581a769f1e52b43
+ Reviewed-by: Qt CI Bot <qt_ci_...@qt-project.org>
+ Reviewed-by: Edward Welbourne <edward.welbou...@qt.io>
+ Reviewed-by: Volker Hilsheimer <volker.hilshei...@qt.io>
+
+Index: qtbase-opensource-src-5.15.2+dfsg/src/network/access/qhsts.cpp
+===================================================================
+--- qtbase-opensource-src-5.15.2+dfsg.orig/src/network/access/qhsts.cpp
2024-03-05 13:48:37.054356050 +0100
++++ qtbase-opensource-src-5.15.2+dfsg/src/network/access/qhsts.cpp
2024-03-05 13:48:37.054356050 +0100
+@@ -364,8 +364,8 @@
+ bool QHstsHeaderParser::parse(const QList<QPair<QByteArray, QByteArray>>
&headers)
+ {
+ for (const auto &h : headers) {
+- // We use '==' since header name was already 'trimmed' for us:
+- if (h.first == "Strict-Transport-Security") {
++ // We compare directly because header name was already 'trimmed' for
us:
++ if (h.first.compare("Strict-Transport-Security", Qt::CaseInsensitive)
== 0) {
+ header = h.second;
+ // RFC6797, 8.1:
+ //
+Index:
qtbase-opensource-src-5.15.2+dfsg/tests/auto/network/access/hsts/tst_qhsts.cpp
+===================================================================
+---
qtbase-opensource-src-5.15.2+dfsg.orig/tests/auto/network/access/hsts/tst_qhsts.cpp
2024-03-05 13:48:37.054356050 +0100
++++
qtbase-opensource-src-5.15.2+dfsg/tests/auto/network/access/hsts/tst_qhsts.cpp
2024-03-05 13:48:37.054356050 +0100
+@@ -242,6 +242,12 @@
+ QVERIFY(parser.includeSubDomains());
+
+ list.pop_back();
++ list << Header("strict-transport-security",
"includeSubDomains;max-age=1000");
++ QVERIFY(parser.parse(list));
++ QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc());
++ QVERIFY(parser.includeSubDomains());
++
++ list.pop_back();
+ // Invalid (includeSubDomains twice):
+ list << Header("Strict-Transport-Security", "max-age = 1000 ;
includeSubDomains;includeSubDomains");
+ QVERIFY(!parser.parse(list));
diff -Nru qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-32763.diff
qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-32763.diff
--- qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-32763.diff
1970-01-01 01:00:00.000000000 +0100
+++ qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-32763.diff
2024-03-05 12:57:24.000000000 +0100
@@ -0,0 +1,54 @@
+Description: fix buffer overflow in Qt SVG
+ Adds qAddOverflow and qMulOverflow definitions to QFixed.
+Origin: upstream,
https://download.qt.io/official_releases/qt/5.15/CVE-2023-32763-qtbase-5.15.diff
+Last-Update: 2023-05-22
+
+Index: qtbase-opensource-src-5.15.2+dfsg/src/gui/painting/qfixed_p.h
+===================================================================
+--- qtbase-opensource-src-5.15.2+dfsg.orig/src/gui/painting/qfixed_p.h
2024-03-05 12:57:22.447571651 +0100
++++ qtbase-opensource-src-5.15.2+dfsg/src/gui/painting/qfixed_p.h
2024-03-05 12:57:22.443571651 +0100
+@@ -54,6 +54,7 @@
+ #include <QtGui/private/qtguiglobal_p.h>
+ #include "QtCore/qdebug.h"
+ #include "QtCore/qpoint.h"
++#include <QtCore/private/qnumeric_p.h>
+ #include "QtCore/qsize.h"
+
+ QT_BEGIN_NAMESPACE
+@@ -182,6 +183,14 @@
+ Q_DECL_CONSTEXPR inline bool operator>(const QFixed &f, int i) { return
f.value() > i * 64; }
+ Q_DECL_CONSTEXPR inline bool operator>(int i, const QFixed &f) { return i *
64 > f.value(); }
+
++inline bool qAddOverflow(QFixed v1, QFixed v2, QFixed *r)
++{
++ int val;
++ bool result = add_overflow(v1.value(), v2.value(), &val);
++ r->setValue(val);
++ return result;
++}
++
+ #ifndef QT_NO_DEBUG_STREAM
+ inline QDebug &operator<<(QDebug &dbg, const QFixed &f)
+ { return dbg << f.toReal(); }
+Index: qtbase-opensource-src-5.15.2+dfsg/src/gui/text/qtextlayout.cpp
+===================================================================
+--- qtbase-opensource-src-5.15.2+dfsg.orig/src/gui/text/qtextlayout.cpp
2024-03-05 12:57:22.447571651 +0100
++++ qtbase-opensource-src-5.15.2+dfsg/src/gui/text/qtextlayout.cpp
2024-03-05 12:57:22.443571651 +0100
+@@ -2138,11 +2138,14 @@
+ eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
+ } else {
+ eng->minWidth = qMax(eng->minWidth, lbh.minw);
+- eng->maxWidth += line.textWidth;
++ if (qAddOverflow(eng->maxWidth, line.textWidth, &eng->maxWidth))
++ eng->maxWidth = QFIXED_MAX;
+ }
+
+- if (line.textWidth > 0 && item < eng->layoutData->items.size())
+- eng->maxWidth += lbh.spaceData.textWidth;
++ if (line.textWidth > 0 && item < eng->layoutData->items.size()) {
++ if (qAddOverflow(eng->maxWidth, lbh.spaceData.textWidth,
&eng->maxWidth))
++ eng->maxWidth = QFIXED_MAX;
++ }
+
+ line.textWidth += trailingSpace;
+ if (lbh.spaceData.length) {
diff -Nru qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-33285.diff
qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-33285.diff
--- qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-33285.diff
1970-01-01 01:00:00.000000000 +0100
+++ qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-33285.diff
2023-05-25 12:45:05.000000000 +0200
@@ -0,0 +1,77 @@
+Description: QDnsLookup/Unix: make sure we don't overflow the buffer
+ The DNS Records are variable length and encode their size in 16 bits
+ before the Record Data (RDATA). Ensure that both the RDATA and the
+ Record header fields before it fall inside the buffer we have.
+ .
+ Additionally reject any replies containing more than one query records.
+Origin: upstream,
https://code.qt.io/cgit/qt/qtbase.git/commit/?id=7dba2c87619d558a
+Last-Update: 2023-05-25
+
+--- a/src/network/kernel/qdnslookup_unix.cpp
++++ b/src/network/kernel/qdnslookup_unix.cpp
+@@ -227,7 +227,6 @@ void QDnsLookupRunnable::query(const int
+ // responseLength in case of error, we still can extract the
+ // exact error code from the response.
+ HEADER *header = (HEADER*)response;
+- const int answerCount = ntohs(header->ancount);
+ switch (header->rcode) {
+ case NOERROR:
+ break;
+@@ -260,18 +259,31 @@ void QDnsLookupRunnable::query(const int
+ return;
+ }
+
+- // Skip the query host, type (2 bytes) and class (2 bytes).
+ char host[PACKETSZ], answer[PACKETSZ];
+ unsigned char *p = response + sizeof(HEADER);
+- int status = local_dn_expand(response, response + responseLength, p,
host, sizeof(host));
+- if (status < 0) {
++ int status;
++
++ if (ntohs(header->qdcount) == 1) {
++ // Skip the query host, type (2 bytes) and class (2 bytes).
++ status = local_dn_expand(response, response + responseLength, p,
host, sizeof(host));
++ if (status < 0) {
++ reply->error = QDnsLookup::InvalidReplyError;
++ reply->errorString = tr("Could not expand domain name");
++ return;
++ }
++ if ((p - response) + status + 4 >= responseLength)
++ header->qdcount = 0xffff; // invalid reply below
++ else
++ p += status + 4;
++ }
++ if (ntohs(header->qdcount) > 1) {
+ reply->error = QDnsLookup::InvalidReplyError;
+- reply->errorString = tr("Could not expand domain name");
++ reply->errorString = tr("Invalid reply received");
+ return;
+ }
+- p += status + 4;
+
+ // Extract results.
++ const int answerCount = ntohs(header->ancount);
+ int answerIndex = 0;
+ while ((p < response + responseLength) && (answerIndex < answerCount)) {
+ status = local_dn_expand(response, response + responseLength, p,
host, sizeof(host));
+@@ -283,6 +295,11 @@ void QDnsLookupRunnable::query(const int
+ const QString name = QUrl::fromAce(host);
+
+ p += status;
++
++ if ((p - response) + 10 > responseLength) {
++ // probably just a truncated reply, return what we have
++ return;
++ }
+ const quint16 type = (p[0] << 8) | p[1];
+ p += 2; // RR type
+ p += 2; // RR class
+@@ -290,6 +307,8 @@ void QDnsLookupRunnable::query(const int
+ p += 4;
+ const quint16 size = (p[0] << 8) | p[1];
+ p += 2;
++ if ((p - response) + size > responseLength)
++ return; // truncated
+
+ if (type == QDnsLookup::A) {
+ if (size != 4) {
diff -Nru qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-34410.diff
qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-34410.diff
--- qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-34410.diff
1970-01-01 01:00:00.000000000 +0100
+++ qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-34410.diff
2024-04-28 22:48:02.000000000 +0200
@@ -0,0 +1,56 @@
+Index:
qtbase-opensource-src-5.15.2+dfsg/src/network/ssl/qsslsocket_schannel.cpp
+===================================================================
+---
qtbase-opensource-src-5.15.2+dfsg.orig/src/network/ssl/qsslsocket_schannel.cpp
2024-04-25 14:19:26.756392964 +0200
++++ qtbase-opensource-src-5.15.2+dfsg/src/network/ssl/qsslsocket_schannel.cpp
2024-04-25 14:19:26.752392971 +0200
+@@ -1877,6 +1877,28 @@
+ if (configuration.peerVerifyDepth > 0 &&
DWORD(configuration.peerVerifyDepth) < verifyDepth)
+ verifyDepth = DWORD(configuration.peerVerifyDepth);
+
++ const auto &caCertificates = q->sslConfiguration().caCertificates();
++
++ if (!rootCertOnDemandLoadingAllowed()
++ && !(chain->TrustStatus.dwErrorStatus &
CERT_TRUST_IS_PARTIAL_CHAIN)
++ && (q->peerVerifyMode() == QSslSocket::VerifyPeer
++ || (isClient && q->peerVerifyMode() ==
QSslSocket::AutoVerifyPeer))) {
++ // When verifying a peer Windows "helpfully" builds a chain that
++ // may include roots from the system store. But we don't want that if
++ // the user has set their own CA certificates.
++ // Since Windows claims this is not a partial chain the root is
included
++ // and we have to check that it is one of our configured CAs.
++ CERT_CHAIN_ELEMENT *element = chain->rgpElement[chain->cElement - 1];
++ QSslCertificate certificate = getCertificateFromChainElement(element);
++ if (!caCertificates.contains(certificate)) {
++ auto error = QSslError(QSslError::CertificateUntrusted,
certificate);
++ sslErrors += error;
++ emit q->peerVerifyError(error);
++ if (q->state() != QAbstractSocket::ConnectedState)
++ return false;
++ }
++ }
++
+ for (DWORD i = 0; i < verifyDepth; i++) {
+ CERT_CHAIN_ELEMENT *element = chain->rgpElement[i];
+ QSslCertificate certificate = getCertificateFromChainElement(element);
+Index: qtbase-opensource-src-5.15.2+dfsg/src/network/ssl/qsslsocket.cpp
+===================================================================
+--- qtbase-opensource-src-5.15.2+dfsg.orig/src/network/ssl/qsslsocket.cpp
2024-04-25 14:19:26.756392964 +0200
++++ qtbase-opensource-src-5.15.2+dfsg/src/network/ssl/qsslsocket.cpp
2024-04-25 14:19:26.752392971 +0200
+@@ -2221,6 +2221,10 @@
+ , 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 @@
+ 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.2+dfsg/debian/patches/CVE-2023-37369.diff
qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-37369.diff
--- qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-37369.diff
1970-01-01 01:00:00.000000000 +0100
+++ qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-37369.diff
2024-04-28 22:48:02.000000000 +0200
@@ -0,0 +1,202 @@
+Index:
qtbase-opensource-src-5.15.2+dfsg/src/corelib/serialization/qxmlstream.cpp
+===================================================================
+---
qtbase-opensource-src-5.15.2+dfsg.orig/src/corelib/serialization/qxmlstream.cpp
2024-04-26 13:23:27.490939469 +0200
++++ qtbase-opensource-src-5.15.2+dfsg/src/corelib/serialization/qxmlstream.cpp
2024-04-26 13:24:24.074800856 +0200
+@@ -1302,11 +1302,19 @@
+ 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, or the range of decltype(*prefix)
++ raiseNamePrefixTooLongError();
++ return {};
++ }
+ switch (c) {
+ case '\n':
+ case ' ':
+@@ -1334,23 +1342,23 @@
+ 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:
+@@ -1359,12 +1367,12 @@
+ }
+ }
+
+- 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 };
+@@ -1873,6 +1881,14 @@
+ 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
implemnetation limits (4KiB "
++ "characters)."));
++}
++
+ void QXmlStreamReaderPrivate::parseError()
+ {
+
+Index: qtbase-opensource-src-5.15.2+dfsg/src/corelib/serialization/qxmlstream.g
+===================================================================
+---
qtbase-opensource-src-5.15.2+dfsg.orig/src/corelib/serialization/qxmlstream.g
2024-04-26 13:23:27.490939469 +0200
++++ qtbase-opensource-src-5.15.2+dfsg/src/corelib/serialization/qxmlstream.g
2024-04-26 13:23:27.486939479 +0200
+@@ -516,7 +516,16 @@
+ 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 @@
+
+ void raiseError(QXmlStreamReader::Error error, const QString& message =
QString());
+ void raiseWellFormedError(const QString &message);
++ void raiseNamePrefixTooLongError();
+
+ QXmlStreamEntityResolver *entityResolver;
+
+@@ -1809,7 +1819,12 @@
+ 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 @@
+ 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;
+Index:
qtbase-opensource-src-5.15.2+dfsg/src/corelib/serialization/qxmlstream_p.h
+===================================================================
+---
qtbase-opensource-src-5.15.2+dfsg.orig/src/corelib/serialization/qxmlstream_p.h
2024-04-26 13:23:27.490939469 +0200
++++ qtbase-opensource-src-5.15.2+dfsg/src/corelib/serialization/qxmlstream_p.h
2024-04-26 13:23:27.486939479 +0200
+@@ -1005,7 +1005,16 @@
+ 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 @@
+
+ void raiseError(QXmlStreamReader::Error error, const QString& message =
QString());
+ void raiseWellFormedError(const QString &message);
++ void raiseNamePrefixTooLongError();
+
+ QXmlStreamEntityResolver *entityResolver;
+
+@@ -1937,7 +1947,12 @@
+ 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 @@
+ } break;
+
+ case 263:
+- sym(1).len += fastScanName();
++ if (auto res = fastScanName())
++ sym(1).len += *res;
++ else
++ return false;
++
+ if (atEnd) {
+ resume(263);
+ return false;
diff -Nru qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-38197.diff
qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-38197.diff
--- qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-38197.diff
1970-01-01 01:00:00.000000000 +0100
+++ qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-38197.diff
2024-04-28 22:48:02.000000000 +0200
@@ -0,0 +1,219 @@
+Index:
qtbase-opensource-src-5.15.2+dfsg/src/corelib/serialization/qxmlstream.cpp
+===================================================================
+---
qtbase-opensource-src-5.15.2+dfsg.orig/src/corelib/serialization/qxmlstream.cpp
2024-04-25 08:25:46.389994553 +0200
++++ qtbase-opensource-src-5.15.2+dfsg/src/corelib/serialization/qxmlstream.cpp
2024-04-25 08:25:46.385994556 +0200
+@@ -160,7 +160,7 @@
+ 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 @@
+
+ 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 @@
+ d->token = -1;
+ return readNext();
+ }
++ d->checkToken();
+ return d->type;
+ }
+
+@@ -740,6 +762,14 @@
+ };
+
+
++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 @@
+ 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 @@
+
+ type = QXmlStreamReader::NoToken;
+ error = QXmlStreamReader::NoError;
++ currentContext = XmlContext::Prolog;
++ foundDTD = false;
+ }
+
+ /*
+@@ -4059,6 +4101,92 @@
+ }
+ }
+
++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
+Index:
qtbase-opensource-src-5.15.2+dfsg/src/corelib/serialization/qxmlstream_p.h
+===================================================================
+---
qtbase-opensource-src-5.15.2+dfsg.orig/src/corelib/serialization/qxmlstream_p.h
2024-04-25 08:25:46.389994553 +0200
++++ qtbase-opensource-src-5.15.2+dfsg/src/corelib/serialization/qxmlstream_p.h
2024-04-25 08:25:46.385994556 +0200
+@@ -804,6 +804,17 @@
+ #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 -Nru qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-51714.diff
qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-51714.diff
--- qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-51714.diff
1970-01-01 01:00:00.000000000 +0100
+++ qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2023-51714.diff
2024-04-28 22:48:02.000000000 +0200
@@ -0,0 +1,61 @@
+From 23c3fc483e8b6e21012a61f0bea884446f727776 Mon Sep 17 00:00:00 2001
+From: Marc Mutz <marc.m...@qt.io>
+Date: Tue, 12 Dec 2023 22:08:07 +0100
+Subject: [PATCH] HPack: fix incorrect integer overflow check
+
+This code never worked:
+
+For the comparison with max() - 32 to trigger, on 32-bit platforms (or
+Qt 5) signed interger overflow would have had to happen in the
+addition of the two sizes. The compiler can therefore remove the
+overflow check as dead code.
+
+On Qt 6 and 64-bit platforms, the signed integer addition would be
+very unlikely to overflow, but the following truncation to uint32
+would yield the correct result only in a narrow 32-value window just
+below UINT_MAX, if even that.
+
+Fix by using the proper tool, qAddOverflow.
+
+Manual conflict resolutions:
+ - qAddOverflow doesn't exist in Qt 5, use private add_overflow
+ predecessor API instead
+
+Change-Id: I7599f2e75ff7f488077b0c60b81022591005661c
+Reviewed-by: Allan Sandfeld Jensen <allan.jen...@qt.io>
+(cherry picked from commit ee5da1f2eaf8932aeca02ffea6e4c618585e29e3)
+Reviewed-by: Qt Cherry-pick Bot <cherrypick_...@qt-project.org>
+(cherry picked from commit debeb8878da2dc706ead04b6072ecbe7e5313860)
+Reviewed-by: Thiago Macieira <thiago.macie...@intel.com>
+Reviewed-by: Marc Mutz <marc.m...@qt.io>
+(cherry picked from commit 811b9eef6d08d929af8708adbf2a5effb0eb62d7)
+(cherry picked from commit f931facd077ce945f1e42eaa3bead208822d3e00)
+(cherry picked from commit 9ef4ca5ecfed771dab890856130e93ef5ceabef5)
+Reviewed-by: Mårten Nordheim <marten.nordh...@qt.io>
+---
+
+Index:
qtbase-opensource-src-5.15.2+dfsg/src/network/access/http2/hpacktable.cpp
+===================================================================
+---
qtbase-opensource-src-5.15.2+dfsg.orig/src/network/access/http2/hpacktable.cpp
2024-04-24 16:05:41.491907436 +0200
++++ qtbase-opensource-src-5.15.2+dfsg/src/network/access/http2/hpacktable.cpp
2024-04-24 16:07:20.779882496 +0200
+@@ -40,6 +40,7 @@
+ #include "hpacktable_p.h"
+
+ #include <QtCore/qdebug.h>
++#include <QtCore/private/qnumeric_p.h>
+
+ #include <algorithm>
+ #include <cstddef>
+@@ -62,8 +63,10 @@
+ // for counting the number of references to the name and value would have
+ // 32 octets of overhead."
+
+- const unsigned sum = unsigned(name.size() + value.size());
+- if (std::numeric_limits<unsigned>::max() - 32 < sum)
++ size_t sum;
++ if (add_overflow(size_t(name.size()), size_t(value.size()), &sum))
++ return HeaderSize();
++ if (sum > (std::numeric_limits<unsigned>::max() - 32))
+ return HeaderSize();
+ return HeaderSize(true, quint32(sum + 32));
+ }
diff -Nru qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2024-25580.diff
qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2024-25580.diff
--- qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2024-25580.diff
1970-01-01 01:00:00.000000000 +0100
+++ qtbase-opensource-src-5.15.2+dfsg/debian/patches/CVE-2024-25580.diff
2024-02-27 23:25:41.000000000 +0100
@@ -0,0 +1,197 @@
+diff --git a/src/gui/util/qktxhandler.cpp b/src/gui/util/qktxhandler.cpp
+index 0d98e97453..6a79e55109 100644
+--- a/src/gui/util/qktxhandler.cpp
++++ b/src/gui/util/qktxhandler.cpp
+@@ -73,7 +73,7 @@ struct KTXHeader {
+ quint32 bytesOfKeyValueData;
+ };
+
+-static const quint32 headerSize = sizeof(KTXHeader);
++static constexpr quint32 qktxh_headerSize = sizeof(KTXHeader);
+
+ // Currently unused, declared for future reference
+ struct KTXKeyValuePairItem {
+@@ -103,11 +103,36 @@ struct KTXMipmapLevel {
+ */
+ };
+
+-bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block)
++static bool qAddOverflow(quint32 v1, quint32 v2, quint32 *r) {
++ // unsigned additions are well-defined
++ *r = v1 + v2;
++ return v1 > quint32(v1 + v2);
++}
++
++// Returns the nearest multiple of 4 greater than or equal to 'value'
++static bool nearestMultipleOf4(quint32 value, quint32 *result)
++{
++ constexpr quint32 rounding = 4;
++ *result = 0;
++ if (qAddOverflow(value, rounding - 1, result))
++ return true;
++ *result &= ~(rounding - 1);
++ return false;
++}
++
++// Returns a slice with prechecked bounds
++static QByteArray safeSlice(const QByteArray& array, quint32 start, quint32
length)
+ {
+- Q_UNUSED(suffix)
++ quint32 end = 0;
++ if (qAddOverflow(start, length, &end) || end > quint32(array.length()))
++ return {};
++ return QByteArray(array.data() + start, length);
++}
+
+- return (qstrncmp(block.constData(), ktxIdentifier, KTX_IDENTIFIER_LENGTH)
== 0);
++bool QKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block)
++{
++ Q_UNUSED(suffix);
++ return block.startsWith(QByteArray::fromRawData(ktxIdentifier,
KTX_IDENTIFIER_LENGTH));
+ }
+
+ QTextureFileData QKtxHandler::read()
+@@ -115,42 +140,97 @@ QTextureFileData QKtxHandler::read()
+ if (!device())
+ return QTextureFileData();
+
+- QByteArray buf = device()->readAll();
+- const quint32 dataSize = quint32(buf.size());
+- if (dataSize < headerSize || !canRead(QByteArray(), buf)) {
+- qCDebug(lcQtGuiTextureIO, "Invalid KTX file %s",
logName().constData());
++ const QByteArray buf = device()->readAll();
++ if (size_t(buf.size()) > std::numeric_limits<quint32>::max()) {
++ qWarning(lcQtGuiTextureIO, "Too big KTX file %s",
logName().constData());
++ return QTextureFileData();
++ }
++
++ if (!canRead(QByteArray(), buf)) {
++ qWarning(lcQtGuiTextureIO, "Invalid KTX file %s",
logName().constData());
++ return QTextureFileData();
++ }
++
++ if (buf.size() < qsizetype(qktxh_headerSize)) {
++ qWarning(lcQtGuiTextureIO, "Invalid KTX header size in %s",
logName().constData());
+ return QTextureFileData();
+ }
+
+- const KTXHeader *header = reinterpret_cast<const KTXHeader
*>(buf.constData());
+- if (!checkHeader(*header)) {
+- qCDebug(lcQtGuiTextureIO, "Unsupported KTX file format in %s",
logName().constData());
++ KTXHeader header;
++ memcpy(&header, buf.data(), qktxh_headerSize);
++ if (!checkHeader(header)) {
++ qWarning(lcQtGuiTextureIO, "Unsupported KTX file format in %s",
logName().constData());
+ return QTextureFileData();
+ }
+
+ QTextureFileData texData;
+ texData.setData(buf);
+
+- texData.setSize(QSize(decode(header->pixelWidth),
decode(header->pixelHeight)));
+- texData.setGLFormat(decode(header->glFormat));
+- texData.setGLInternalFormat(decode(header->glInternalFormat));
+- texData.setGLBaseInternalFormat(decode(header->glBaseInternalFormat));
+-
+- texData.setNumLevels(decode(header->numberOfMipmapLevels));
+- quint32 offset = headerSize + decode(header->bytesOfKeyValueData);
+- const int maxLevels = qMin(texData.numLevels(), 32); // Cap
iterations in case of corrupt file.
+- for (int i = 0; i < maxLevels; i++) {
+- if (offset + sizeof(KTXMipmapLevel) > dataSize) //
Corrupt file; avoid oob read
+- break;
+- const KTXMipmapLevel *level = reinterpret_cast<const KTXMipmapLevel
*>(buf.constData() + offset);
+- quint32 levelLen = decode(level->imageSize);
+- texData.setDataOffset(offset + sizeof(KTXMipmapLevel::imageSize), i);
+- texData.setDataLength(levelLen, i);
+- offset += sizeof(KTXMipmapLevel::imageSize) + levelLen + (3 -
((levelLen + 3) % 4));
++ texData.setSize(QSize(decode(header.pixelWidth),
decode(header.pixelHeight)));
++ texData.setGLFormat(decode(header.glFormat));
++ texData.setGLInternalFormat(decode(header.glInternalFormat));
++ texData.setGLBaseInternalFormat(decode(header.glBaseInternalFormat));
++
++ texData.setNumLevels(decode(header.numberOfMipmapLevels));
++
++ const quint32 bytesOfKeyValueData = decode(header.bytesOfKeyValueData);
++ quint32 headerKeyValueSize;
++ if (qAddOverflow(qktxh_headerSize, bytesOfKeyValueData,
&headerKeyValueSize)) {
++ qWarning(lcQtGuiTextureIO, "Overflow in size of key value data in
header of KTX file %s",
++ logName().constData());
++ return QTextureFileData();
++ }
++
++ if (headerKeyValueSize >= quint32(buf.size())) {
++ qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s",
logName().constData());
++ return QTextureFileData();
++ }
++
++ // Technically, any number of levels is allowed but if the value is
bigger than
++ // what is possible in KTX V2 (and what makes sense) we return an error.
++ // maxLevels = log2(max(width, height, depth))
++ const int maxLevels = (sizeof(quint32) * 8)
++ - qCountLeadingZeroBits(std::max(
++ { header.pixelWidth, header.pixelHeight,
header.pixelDepth }));
++
++ if (texData.numLevels() > maxLevels) {
++ qWarning(lcQtGuiTextureIO, "Too many levels in KTX file %s",
logName().constData());
++ return QTextureFileData();
++ }
++
++ quint32 offset = headerKeyValueSize;
++ for (int level = 0; level < texData.numLevels(); level++) {
++ const auto imageSizeSlice = safeSlice(buf, offset, sizeof(quint32));
++ if (imageSizeSlice.isEmpty()) {
++ qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s",
logName().constData());
++ return QTextureFileData();
++ }
++
++ const quint32 imageSize =
decode(qFromUnaligned<quint32>(imageSizeSlice.data()));
++ offset += sizeof(quint32); // overflow checked indirectly above
++
++ texData.setDataOffset(offset, level);
++ texData.setDataLength(imageSize, level);
++
++ // Add image data and padding to offset
++ quint32 padded = 0;
++ if (nearestMultipleOf4(imageSize, &padded)) {
++ qWarning(lcQtGuiTextureIO, "Overflow in KTX file %s",
logName().constData());
++ return QTextureFileData();
++ }
++
++ quint32 offsetNext;
++ if (qAddOverflow(offset, padded, &offsetNext)) {
++ qWarning(lcQtGuiTextureIO, "OOB request in KTX file %s",
logName().constData());
++ return QTextureFileData();
++ }
++
++ offset = offsetNext;
+ }
+
+ if (!texData.isValid()) {
+- qCDebug(lcQtGuiTextureIO, "Invalid values in header of KTX file %s",
logName().constData());
++ qWarning(lcQtGuiTextureIO, "Invalid values in header of KTX file %s",
++ logName().constData());
+ return QTextureFileData();
+ }
+
+@@ -191,7 +271,7 @@ bool QKtxHandler::checkHeader(const KTXHeader &header)
+ (decode(header.numberOfFaces) == 1));
+ }
+
+-quint32 QKtxHandler::decode(quint32 val)
++quint32 QKtxHandler::decode(quint32 val) const
+ {
+ return inverseEndian ? qbswap<quint32>(val) : val;
+ }
+diff --git a/src/gui/util/qktxhandler_p.h b/src/gui/util/qktxhandler_p.h
+index f831e59d95..cdf1b2eaf8 100644
+--- a/src/gui/util/qktxhandler_p.h
++++ b/src/gui/util/qktxhandler_p.h
+@@ -68,7 +68,7 @@ public:
+
+ private:
+ bool checkHeader(const KTXHeader &header);
+- quint32 decode(quint32 val);
++ quint32 decode(quint32 val) const;
+
+ bool inverseEndian = false;
+ };
diff -Nru qtbase-opensource-src-5.15.2+dfsg/debian/patches/series
qtbase-opensource-src-5.15.2+dfsg/debian/patches/series
--- qtbase-opensource-src-5.15.2+dfsg/debian/patches/series 2021-07-02
17:58:04.000000000 +0200
+++ qtbase-opensource-src-5.15.2+dfsg/debian/patches/series 2024-04-28
22:48:02.000000000 +0200
@@ -6,6 +6,19 @@
mime_globs.diff
fix-invalid-pointer-return-with-QGridLayout.diff
+CVE-2024-25580.diff
+CVE-2023-32763.diff
+CVE-2022-25255.diff
+CVE-2023-24607.diff
+sql_odbc_fix_unicode_check.diff
+sql_odbc_more_unicode_checks.diff
+CVE-2023-32762.diff
+CVE-2023-51714.diff
+CVE-2023-37369.diff
+CVE-2023-38197.diff
+CVE-2023-34410.diff
+CVE-2023-33285.diff
+
# Debian specific.
gnukfreebsd.diff
no_htmlinfo_example.diff
diff -Nru
qtbase-opensource-src-5.15.2+dfsg/debian/patches/sql_odbc_fix_unicode_check.diff
qtbase-opensource-src-5.15.2+dfsg/debian/patches/sql_odbc_fix_unicode_check.diff
---
qtbase-opensource-src-5.15.2+dfsg/debian/patches/sql_odbc_fix_unicode_check.diff
1970-01-01 01:00:00.000000000 +0100
+++
qtbase-opensource-src-5.15.2+dfsg/debian/patches/sql_odbc_fix_unicode_check.diff
2024-04-28 22:48:02.000000000 +0200
@@ -0,0 +1,24 @@
+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
+
+Index:
qtbase-opensource-src-5.15.2+dfsg/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
+===================================================================
+---
qtbase-opensource-src-5.15.2+dfsg.orig/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
2024-04-28 17:51:58.976069584 +0200
++++
qtbase-opensource-src-5.15.2+dfsg/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
2024-04-28 17:51:58.972069583 +0200
+@@ -125,7 +125,6 @@
+ {
+ 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;
+ }
+
diff -Nru
qtbase-opensource-src-5.15.2+dfsg/debian/patches/sql_odbc_more_unicode_checks.diff
qtbase-opensource-src-5.15.2+dfsg/debian/patches/sql_odbc_more_unicode_checks.diff
---
qtbase-opensource-src-5.15.2+dfsg/debian/patches/sql_odbc_more_unicode_checks.diff
1970-01-01 01:00:00.000000000 +0100
+++
qtbase-opensource-src-5.15.2+dfsg/debian/patches/sql_odbc_more_unicode_checks.diff
2024-04-28 22:48:02.000000000 +0200
@@ -0,0 +1,37 @@
+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
+
+Index:
qtbase-opensource-src-5.15.2+dfsg/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
+===================================================================
+---
qtbase-opensource-src-5.15.2+dfsg.orig/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
2024-04-28 17:59:07.172222178 +0200
++++
qtbase-opensource-src-5.15.2+dfsg/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
2024-04-28 17:59:07.172222178 +0200
+@@ -2109,7 +2109,18 @@
+ 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) {
--- End Message ---