Http2: fix handling incoming frames on locally reset stream
After some of the RST stream handling was updated to more closely follow the RFC it was accidentally not updating the handleHEADERS function, and the handleDATA function was handled incorrectly leading to a potential nullptr dereference. Amends d17d260948e16549d82f1fdd4dec98d246b0622e. Pick-to: 6.10 6.9 Change-Id: I345448efd7da92f4f74033b03a5c040b5db9d271 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
parent
904aec2f37
commit
8d5db24c14
@ -1384,13 +1384,16 @@ void QHttp2Connection::handleDATA()
|
||||
if (isInvalidStream(streamID))
|
||||
return connectionError(ENHANCE_YOUR_CALM, "DATA on invalid stream");
|
||||
|
||||
// RFC9113, 6.1: If a DATA frame is received whose stream is not in the "open" or
|
||||
// "half-closed (local)" state, the recipient MUST respond with a stream error.
|
||||
auto stream = getStream(streamID);
|
||||
if (stream->state() == QHttp2Stream::State::HalfClosedRemote
|
||||
|| stream->state() == QHttp2Stream::State::Closed) {
|
||||
return stream->streamError(Http2Error::STREAM_CLOSED,
|
||||
QLatin1String("Data on closed stream"));
|
||||
QHttp2Stream *stream = nullptr;
|
||||
if (!streamWasResetLocally(streamID)) {
|
||||
stream = getStream(streamID);
|
||||
// RFC9113, 6.1: If a DATA frame is received whose stream is not in the "open" or
|
||||
// "half-closed (local)" state, the recipient MUST respond with a stream error.
|
||||
if (stream->state() == QHttp2Stream::State::HalfClosedRemote
|
||||
|| stream->state() == QHttp2Stream::State::Closed) {
|
||||
return stream->streamError(Http2Error::STREAM_CLOSED,
|
||||
QLatin1String("Data on closed stream"));
|
||||
}
|
||||
}
|
||||
|
||||
if (qint32(inboundFrame.payloadSize()) > sessionReceiveWindowSize) {
|
||||
@ -1403,9 +1406,8 @@ void QHttp2Connection::handleDATA()
|
||||
|
||||
sessionReceiveWindowSize -= inboundFrame.payloadSize();
|
||||
|
||||
auto it = m_streams.constFind(streamID);
|
||||
if (it != m_streams.cend() && it.value())
|
||||
it.value()->handleDATA(inboundFrame);
|
||||
if (stream)
|
||||
stream->handleDATA(inboundFrame);
|
||||
|
||||
if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM))
|
||||
emit receivedEND_STREAM(streamID);
|
||||
@ -1451,6 +1453,11 @@ void QHttp2Connection::handleHEADERS()
|
||||
|
||||
qCDebug(qHttp2ConnectionLog, "[%p] Created new incoming stream %d", this, streamID);
|
||||
emit newIncomingStream(newStream);
|
||||
} else if (streamWasResetLocally(streamID)) {
|
||||
qCDebug(qHttp2ConnectionLog,
|
||||
"[%p] Received HEADERS on previously locally reset stream %d (must process but ignore)",
|
||||
this, streamID);
|
||||
// nop
|
||||
} else if (auto it = m_streams.constFind(streamID); it == m_streams.cend()) {
|
||||
// RFC 9113, 6.2: HEADERS frames MUST be associated with a stream.
|
||||
// A connection error is not required but it seems to be the right thing to do.
|
||||
|
@ -311,7 +311,8 @@ void QHttp2ProtocolHandler::handleHeadersReceived(const HPack::HttpHeader &heade
|
||||
auto &requestPair = requestReplyPairs[stream];
|
||||
auto *httpReply = requestPair.second;
|
||||
auto &httpRequest = requestPair.first;
|
||||
Q_ASSERT(httpReply || stream->state() == QHttp2Stream::State::ReservedRemote);
|
||||
if (!httpReply && (!stream || stream->state() != QHttp2Stream::State::ReservedRemote))
|
||||
return;
|
||||
|
||||
auto *httpReplyPrivate = httpReply->d_func();
|
||||
|
||||
@ -394,6 +395,8 @@ void QHttp2ProtocolHandler::handleDataReceived(const QByteArray &data, bool endS
|
||||
QHttp2Stream *stream = qobject_cast<QHttp2Stream *>(sender());
|
||||
auto &httpPair = requestReplyPairs[stream];
|
||||
auto *httpReply = httpPair.second;
|
||||
if (!httpReply)
|
||||
return;
|
||||
Q_ASSERT(!stream->isPromisedStream());
|
||||
|
||||
if (!data.isEmpty() && !httpPair.first.d->needResendWithCredentials) {
|
||||
|
@ -30,6 +30,7 @@ private slots:
|
||||
void testBadFrameSize();
|
||||
void testDataFrameAfterRSTIncoming();
|
||||
void testDataFrameAfterRSTOutgoing();
|
||||
void headerFrameAfterRSTOutgoing_data();
|
||||
void headerFrameAfterRSTOutgoing();
|
||||
void connectToServer();
|
||||
void WINDOW_UPDATE();
|
||||
@ -728,8 +729,16 @@ void tst_QHttp2Connection::testDataFrameAfterRSTOutgoing()
|
||||
QVERIFY(closedServerSpy.wait());
|
||||
}
|
||||
|
||||
void tst_QHttp2Connection::headerFrameAfterRSTOutgoing_data()
|
||||
{
|
||||
QTest::addColumn<bool>("deleteStream");
|
||||
QTest::addRow("retain-stream") << false;
|
||||
QTest::addRow("delete-stream") << true;
|
||||
}
|
||||
|
||||
void tst_QHttp2Connection::headerFrameAfterRSTOutgoing()
|
||||
{
|
||||
QFETCH(const bool, deleteStream);
|
||||
auto [client, server] = makeFakeConnectedSockets();
|
||||
auto *connection = makeHttp2Connection(client.get(), {}, Client);
|
||||
auto *serverConnection = makeHttp2Connection(server.get(), {}, Server);
|
||||
@ -753,6 +762,8 @@ void tst_QHttp2Connection::headerFrameAfterRSTOutgoing()
|
||||
|
||||
// Send an RST frame from the client, but we don't process it yet
|
||||
clientStream->sendRST_STREAM(Http2::CANCEL);
|
||||
if (deleteStream)
|
||||
delete std::exchange(clientStream, nullptr);
|
||||
|
||||
// The server sends a reply, not knowing about the inbound RST frame
|
||||
const HPack::HttpHeader StandardReply{ { ":status", "200" }, { "x-whatever", "some info" } };
|
||||
|
Loading…
x
Reference in New Issue
Block a user