Version in base suite: 5.15.8+dfsg-11+deb12u2 Base version: qtbase-opensource-src_5.15.8+dfsg-11+deb12u2 Target version: qtbase-opensource-src_5.15.8+dfsg-11+deb12u3 Base file: /srv/ftp-master.debian.org/ftp/pool/main/q/qtbase-opensource-src/qtbase-opensource-src_5.15.8+dfsg-11+deb12u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/q/qtbase-opensource-src/qtbase-opensource-src_5.15.8+dfsg-11+deb12u3.dsc changelog | 9 ++ patches/CVE-2024-39936.diff | 153 ++++++++++++++++++++++++++++++++++++++++++ patches/a11y_null_checks.diff | 76 ++++++++++++++++++++ patches/series | 3 4 files changed, 240 insertions(+), 1 deletion(-) 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 2024-04-28 18:48:02.000000000 +0000 +++ qtbase-opensource-src-5.15.8+dfsg/debian/changelog 2025-03-27 11:24:29.000000000 +0000 @@ -1,3 +1,12 @@ +qtbase-opensource-src (5.15.8+dfsg-11+deb12u3) bookworm; urgency=medium + + * Backport upstream patch to add null checks in table iface methods in + linuxaccessibility/atspiadaptor.cpp (closes: #1081682). + * Backport upstream patch to delay any communication until encrypted() can + be responded to (CVE-2024-39936, closes: #1076293). + + -- Dmitry Shachnev Thu, 27 Mar 2025 14:24:29 +0300 + qtbase-opensource-src (5.15.8+dfsg-11+deb12u2) bookworm; urgency=medium * Non-maintainer upload by the LTS Team. diff -Nru qtbase-opensource-src-5.15.8+dfsg/debian/patches/CVE-2024-39936.diff qtbase-opensource-src-5.15.8+dfsg/debian/patches/CVE-2024-39936.diff --- qtbase-opensource-src-5.15.8+dfsg/debian/patches/CVE-2024-39936.diff 1970-01-01 00:00:00.000000000 +0000 +++ qtbase-opensource-src-5.15.8+dfsg/debian/patches/CVE-2024-39936.diff 2025-03-27 11:24:29.000000000 +0000 @@ -0,0 +1,153 @@ +Description: HTTP2: delay any communication until encrypted() can be responded to + We have the encrypted() signal that lets users do extra checks on the + established connection. It is emitted as BlockingQueued, so the HTTP + thread stalls until it is done emitting. Users can potentially call + abort() on the QNetworkReply at that point, which is passed as a Queued + call back to the HTTP thread. That means that any currently queued + signal emission will be processed before the abort() call is processed. + . + In the case of HTTP2 it is a little special since it is multiplexed and + the code is built to start requests as they are available. This means + that, while the code worked fine for HTTP1, since one connection only + has one request, it is not working for HTTP2, since we try to send more + requests in-between the encrypted() signal and the abort() call. + . + This patch changes the code to delay any communication until the + encrypted() signal has been emitted and processed, for HTTP2 only. + It's done by adding a few booleans, both to know that we have to return + early and so we can keep track of what events arose and what we need to + resume once enough time has passed that any abort() call must have been + processed. +Origin: upstream, https://code.qt.io/cgit/qt/qtbase.git/commit/?id=b1e75376cc3adfc7 +Last-Update: 2025-03-25 + +--- a/src/network/access/qhttp2protocolhandler.cpp ++++ b/src/network/access/qhttp2protocolhandler.cpp +@@ -371,12 +371,12 @@ bool QHttp2ProtocolHandler::sendRequest( + } + } + +- if (!prefaceSent && !sendClientPreface()) +- return false; +- + if (!requests.size()) + return true; + ++ if (!prefaceSent && !sendClientPreface()) ++ return false; ++ + m_channel->state = QHttpNetworkConnectionChannel::WritingState; + // Check what was promised/pushed, maybe we do not have to send a request + // and have a response already? +--- a/src/network/access/qhttpnetworkconnectionchannel.cpp ++++ b/src/network/access/qhttpnetworkconnectionchannel.cpp +@@ -255,6 +255,10 @@ void QHttpNetworkConnectionChannel::abor + bool QHttpNetworkConnectionChannel::sendRequest() + { + Q_ASSERT(!protocolHandler.isNull()); ++ if (waitingForPotentialAbort) { ++ needInvokeSendRequest = true; ++ return false; // this return value is unused ++ } + return protocolHandler->sendRequest(); + } + +@@ -267,21 +271,28 @@ bool QHttpNetworkConnectionChannel::send + void QHttpNetworkConnectionChannel::sendRequestDelayed() + { + QMetaObject::invokeMethod(this, [this] { +- Q_ASSERT(!protocolHandler.isNull()); + if (reply) +- protocolHandler->sendRequest(); ++ sendRequest(); + }, Qt::ConnectionType::QueuedConnection); + } + + void QHttpNetworkConnectionChannel::_q_receiveReply() + { + Q_ASSERT(!protocolHandler.isNull()); ++ if (waitingForPotentialAbort) { ++ needInvokeReceiveReply = true; ++ return; ++ } + protocolHandler->_q_receiveReply(); + } + + void QHttpNetworkConnectionChannel::_q_readyRead() + { + Q_ASSERT(!protocolHandler.isNull()); ++ if (waitingForPotentialAbort) { ++ needInvokeReadyRead = true; ++ return; ++ } + protocolHandler->_q_readyRead(); + } + +@@ -1289,7 +1300,18 @@ void QHttpNetworkConnectionChannel::_q_e + // Similar to HTTP/1.1 counterpart below: + const auto &pairs = spdyRequestsToSend.values(); // (request, reply) + const auto &pair = pairs.first(); ++ waitingForPotentialAbort = true; + emit pair.second->encrypted(); ++ ++ // We don't send or handle any received data until any effects from ++ // emitting encrypted() have been processed. This is necessary ++ // because the user may have called abort(). We may also abort the ++ // whole connection if the request has been aborted and there is ++ // no more requests to send. ++ QMetaObject::invokeMethod(this, ++ &QHttpNetworkConnectionChannel::checkAndResumeCommunication, ++ Qt::QueuedConnection); ++ + // In case our peer has sent us its settings (window size, max concurrent streams etc.) + // let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection). + QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection); +@@ -1307,6 +1329,26 @@ void QHttpNetworkConnectionChannel::_q_e + } + } + ++void QHttpNetworkConnectionChannel::checkAndResumeCommunication() ++{ ++ Q_ASSERT(connection->connectionType() > QHttpNetworkConnection::ConnectionTypeHTTP); ++ ++ // Because HTTP/2 requires that we send a SETTINGS frame as the first thing we do, and respond ++ // to a SETTINGS frame with an ACK, we need to delay any handling until we can ensure that any ++ // effects from emitting encrypted() have been processed. ++ // This function is called after encrypted() was emitted, so check for changes. ++ ++ if (!reply && spdyRequestsToSend.isEmpty()) ++ abort(); ++ waitingForPotentialAbort = false; ++ if (needInvokeReadyRead) ++ _q_readyRead(); ++ if (needInvokeReceiveReply) ++ _q_receiveReply(); ++ if (needInvokeSendRequest) ++ sendRequest(); ++} ++ + void QHttpNetworkConnectionChannel::requeueSpdyRequests() + { + QList spdyPairs = spdyRequestsToSend.values(); +--- a/src/network/access/qhttpnetworkconnectionchannel_p.h ++++ b/src/network/access/qhttpnetworkconnectionchannel_p.h +@@ -107,6 +107,10 @@ public: + QAbstractSocket *socket; + bool ssl; + bool isInitialized; ++ bool waitingForPotentialAbort = false; ++ bool needInvokeReceiveReply = false; ++ bool needInvokeReadyRead = false; ++ bool needInvokeSendRequest = false; + ChannelState state; + QHttpNetworkRequest request; // current request, only used for HTTP + QHttpNetworkReply *reply; // current reply for this request, only used for HTTP +@@ -187,6 +191,8 @@ public: + void closeAndResendCurrentRequest(); + void resendCurrentRequest(); + ++ void checkAndResumeCommunication(); ++ + bool isSocketBusy() const; + bool isSocketWriting() const; + bool isSocketWaiting() const; diff -Nru qtbase-opensource-src-5.15.8+dfsg/debian/patches/a11y_null_checks.diff qtbase-opensource-src-5.15.8+dfsg/debian/patches/a11y_null_checks.diff --- qtbase-opensource-src-5.15.8+dfsg/debian/patches/a11y_null_checks.diff 1970-01-01 00:00:00.000000000 +0000 +++ qtbase-opensource-src-5.15.8+dfsg/debian/patches/a11y_null_checks.diff 2025-03-27 11:24:29.000000000 +0000 @@ -0,0 +1,76 @@ +Description: a11y atspi: add null checks in table iface methods + Add null checks to cover the cases where QAccessibleTableInterface::cellAt + returns nullptr (which happens e.g. when called with invalid indices via + AT-SPI) or where the cell object doesn't implement the + QAccessibleTableCellInterface, which would previously result in crashes. + . + Cherry-picked into 5.15 as it fixes a crash in popular accessibility client + software. Conflict resolution: remove C++17'isms (`if` with initializer). +Origin: upstream, https://invent.kde.org/qt/qt/qtbase/-/commit/076da096464a5d3f +Last-Update: 2025-03-24 +Bug: https://bugs.debian.org/1081682 + +--- a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp ++++ b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp +@@ -2393,13 +2393,14 @@ bool AtSpiAdaptor::tableInterface(QAcces + if (cols > 0) { + row = index / cols; + col = index % cols; +- QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, col)->tableCellInterface(); +- if (cell) { +- row = cell->rowIndex(); +- col = cell->columnIndex(); +- rowExtents = cell->rowExtent(); +- colExtents = cell->columnExtent(); +- isSelected = cell->isSelected(); ++ QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, col); ++ QAccessibleTableCellInterface *cellIface = cell ? cell->tableCellInterface() : nullptr; ++ if (cellIface) { ++ row = cellIface->rowIndex(); ++ col = cellIface->columnIndex(); ++ rowExtents = cellIface->rowExtent(); ++ colExtents = cellIface->columnExtent(); ++ isSelected = cellIface->isSelected(); + success = true; + } + } +@@ -2410,12 +2411,22 @@ bool AtSpiAdaptor::tableInterface(QAcces + } else if (function == QLatin1String("GetColumnExtentAt")) { + int row = message.arguments().at(0).toInt(); + int column = message.arguments().at(1).toInt(); +- connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->columnExtent())); ++ int columnExtent = 0; ++ QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column); ++ QAccessibleTableCellInterface *cellIface = cell ? cell->tableCellInterface() : nullptr; ++ if (cellIface) ++ columnExtent = cellIface->columnExtent(); ++ connection.send(message.createReply(columnExtent)); + + } else if (function == QLatin1String("GetRowExtentAt")) { + int row = message.arguments().at(0).toInt(); + int column = message.arguments().at(1).toInt(); +- connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->rowExtent())); ++ int rowExtent = 0; ++ QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column); ++ QAccessibleTableCellInterface *cellIface = cell ? cell->tableCellInterface() : nullptr; ++ if (cellIface) ++ rowExtent = cellIface->rowExtent(); ++ connection.send(message.createReply(rowExtent)); + + } else if (function == QLatin1String("GetColumnHeader")) { + int column = message.arguments().at(0).toInt(); +@@ -2455,8 +2466,12 @@ bool AtSpiAdaptor::tableInterface(QAcces + } else if (function == QLatin1String("IsSelected")) { + int row = message.arguments().at(0).toInt(); + int column = message.arguments().at(1).toInt(); +- QAccessibleTableCellInterface* cell = interface->tableInterface()->cellAt(row, column)->tableCellInterface(); +- connection.send(message.createReply(cell->isSelected())); ++ bool isSelected = false; ++ QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column); ++ QAccessibleTableCellInterface *cellIface = cell ? cell->tableCellInterface() : nullptr; ++ if (cellIface) ++ isSelected = cellIface->isSelected(); ++ connection.send(message.createReply(isSelected)); + } else if (function == QLatin1String("AddColumnSelection")) { + int column = message.arguments().at(0).toInt(); + connection.send(message.createReply(interface->tableInterface()->selectColumn(column))); 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 2024-04-28 18:48:02.000000000 +0000 +++ qtbase-opensource-src-5.15.8+dfsg/debian/patches/series 2025-03-27 11:24:29.000000000 +0000 @@ -23,9 +23,10 @@ CVE-2023-34410.diff CVE-2023-37369.diff CVE-2023-38197.diff - CVE-2023-51714.diff CVE-2024-25580.diff +a11y_null_checks.diff +CVE-2024-39936.diff # Debian specific. gnukfreebsd.diff