From 1ddd3ea76be50a0cf2193e7afae729a118111071 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 17 Apr 2024 19:10:44 +0200 Subject: [PATCH] [network] Keep track of and check HTTP/2 header sizes. --- netwerk/protocol/http/Http2Session.cpp | 37 +++++++++++++++++++++++--- netwerk/protocol/http/Http2Session.h | 3 +++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp index 0bd9440034..d61e3c6e2f 100644 --- a/netwerk/protocol/http/Http2Session.cpp +++ b/netwerk/protocol/http/Http2Session.cpp @@ -90,6 +90,7 @@ Http2Session::Http2Session(nsISocketTransport *aSocketTransport, uint32_t versio , mClosed(false) , mCleanShutdown(false) , mTLSProfileConfirmed(false) + , mAggregatedHeaderSize(0) , mGoAwayReason(NO_HTTP_ERROR) , mClientGoAwayReason(UNASSIGNED) , mPeerGoAwayReason(UNASSIGNED) @@ -1262,6 +1263,13 @@ Http2Session::RecvHeaders(Http2Session *self) RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); } + uint32_t frameSize = self->mInputFrameDataSize - paddingControlBytes - + priorityLen - paddingLength; + if (self->mAggregatedHeaderSize + frameSize > + gHttpHandler->MaxHttpResponseHeaderSize()) { + LOG(("Http2Session %p header exceeds the limit\n", self)); + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); + } if (!self->mInputFrameDataStream) { // Cannot find stream. We can continue the session, but we need to // uncompress the header block to maintain the correct compression context @@ -1274,7 +1282,7 @@ Http2Session::RecvHeaders(Http2Session *self) self->GenerateRstStream(PROTOCOL_ERROR, self->mInputFrameID); self->mDecompressBuffer.Append(&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes + priorityLen], - self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength); + frameSize); if (self->mInputFrameFlags & kFlag_END_HEADERS) { rv = self->UncompressAndDiscard(false); @@ -1301,11 +1309,17 @@ Http2Session::RecvHeaders(Http2Session *self) // queue up any compression bytes self->mDecompressBuffer.Append(&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes + priorityLen], - self->mInputFrameDataSize - paddingControlBytes - priorityLen - paddingLength); + frameSize); self->mInputFrameDataStream->UpdateTransportReadEvents(self->mInputFrameDataSize); self->mLastDataReadEpoch = self->mLastReadEpoch; + if (!isContinuation) { + self->mAggregatedHeaderSize = frameSize; + } else { + self->mAggregatedHeaderSize += frameSize; + } + if (!endHeadersFlag) { // more are coming - don't process yet self->ResetDownstreamState(); return NS_OK; @@ -1613,6 +1627,15 @@ Http2Session::RecvPushPromise(Http2Session *self) self->mInputFrameDataSize)); RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); } + + uint32_t frameSize = self->mInputFrameDataSize - paddingControlBytes - + promiseLen - paddingLength; + + if (self->mAggregatedHeaderSize + frameSize > + gHttpHandler->MaxHttpResponseHeaderSize()) { + LOG(("Http2Session:RecvPushPromise %p header exceeds the limit\n", self)); + RETURN_SESSION_ERROR(self, PROTOCOL_ERROR); + } LOG3(("Http2Session::RecvPushPromise %p ID 0x%X assoc ID 0x%X " "paddingLength %d padded %d\n", @@ -1683,7 +1706,7 @@ Http2Session::RecvPushPromise(Http2Session *self) // Need to decompress the headers even though we aren't using them yet in // order to keep the compression context consistent for other frames self->mDecompressBuffer.Append(&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes + promiseLen], - self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength); + frameSize); if (self->mInputFrameFlags & kFlag_END_PUSH_PROMISE) { rv = self->UncompressAndDiscard(true); if (NS_FAILED(rv)) { @@ -1697,7 +1720,13 @@ Http2Session::RecvPushPromise(Http2Session *self) } self->mDecompressBuffer.Append(&self->mInputFrameBuffer[kFrameHeaderBytes + paddingControlBytes + promiseLen], - self->mInputFrameDataSize - paddingControlBytes - promiseLen - paddingLength); + frameSize); + + if (self->mInputFrameType != FRAME_TYPE_CONTINUATION) { + self->mAggregatedHeaderSize = frameSize; + } else { + self->mAggregatedHeaderSize += frameSize; + } if (!(self->mInputFrameFlags & kFlag_END_PUSH_PROMISE)) { LOG3(("Http2Session::RecvPushPromise not finishing processing for multi-frame push\n")); diff --git a/netwerk/protocol/http/Http2Session.h b/netwerk/protocol/http/Http2Session.h index 9d6b8c8611..355813f59e 100644 --- a/netwerk/protocol/http/Http2Session.h +++ b/netwerk/protocol/http/Http2Session.h @@ -483,6 +483,9 @@ private: // to make sure streams aren't shared across sessions. uint64_t mSerial; + // Aggregate size of continued headers (pushed and pulled) + uint32_t mAggregatedHeaderSize; + // If push is disabled, we want to be able to send PROTOCOL_ERRORs if we // receive a PUSH_PROMISE, but we have to wait for the SETTINGS ACK before // we can actually tell the other end to go away. These help us keep track