Package: qtbase-opensource-src / 5.15.8+dfsg-11+deb12u3

CVE-2024-39936.diff Patch series | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
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<HttpMessagePair> 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;