Impose upper limit on the frame length of some HTTP/3 frames. This helps prevent attacks that send malformed data. gfe-relnote: n/a --unused code. PiperOrigin-RevId: 242941217 Change-Id: Ic1f97f091afb57a38dc05e98ee1a5c42f1caf95a
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc index 972cb37..0a2ea0d 100644 --- a/quic/core/http/http_decoder.cc +++ b/quic/core/http/http_decoder.cc
@@ -26,6 +26,10 @@ // Length of the type field of HTTP/3 frames. static const QuicByteCount kFrameTypeLength = 1; +// Length of the weight field of a priority frame. +static const size_t kPriorityWeightLength = 1; +// Length of a priority frame's first byte. +static const size_t kPriorityFirstByteLength = 1; } // namespace @@ -98,6 +102,12 @@ return; } + if (current_frame_length_ > MaxFrameLength(current_frame_type_)) { + RaiseError(QUIC_INTERNAL_ERROR, "Frame is too large"); + visitor_->OnError(this); + return; + } + // Calling the following two visitor methods does not require parsing of any // frame payload. if (current_frame_type_ == 0x0) { @@ -152,15 +162,10 @@ break; } case 0x3: { // CANCEL_PUSH - // TODO(rch): Handle partial delivery. BufferFramePayload(reader); break; } case 0x4: { // SETTINGS - // TODO(rch): Handle overly large SETTINGS frames. Either: - // 1. Impose a limit on SETTINGS frame size, and close the connection if - // exceeded - // 2. Implement a streaming parsing mode. BufferFramePayload(reader); break; } @@ -439,4 +444,26 @@ return true; } +QuicByteCount HttpDecoder::MaxFrameLength(uint8_t frame_type) { + switch (frame_type) { + case 0x2: // PRIORITY + return kPriorityFirstByteLength + VARIABLE_LENGTH_INTEGER_LENGTH_8 * 2 + + kPriorityWeightLength; + case 0x3: // CANCEL_PUSH + return sizeof(PushId); + case 0x4: // SETTINGS + // This limit is arbitrary. + return 1024 * 1024; + case 0x7: // GOAWAY + return sizeof(QuicStreamId); + case 0xD: // MAX_PUSH_ID + return sizeof(PushId); + case 0xE: // DUPLICATE_PUSH + return sizeof(PushId); + default: + // Other frames require no data buffering, so it's safe to have no limit. + return std::numeric_limits<QuicByteCount>::max(); + } +} + } // namespace quic
diff --git a/quic/core/http/http_decoder.h b/quic/core/http/http_decoder.h index 2318a03..225941d 100644 --- a/quic/core/http/http_decoder.h +++ b/quic/core/http/http_decoder.h
@@ -164,6 +164,9 @@ // Parses the payload of a SETTINGS frame from |reader| into |frame|. bool ParseSettingsFrame(QuicDataReader* reader, SettingsFrame* frame); + // Returns the max frame size of a given |frame_type|. + QuicByteCount MaxFrameLength(uint8_t frame_type); + // Visitor to invoke when messages are parsed. Visitor* visitor_; // Unowned. // Current state of the parsing.
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc index a4202e1..0f7b60c 100644 --- a/quic/core/http/http_decoder_test.cc +++ b/quic/core/http/http_decoder_test.cc
@@ -3,7 +3,9 @@ // found in the LICENSE file. #include "net/third_party/quiche/src/quic/core/http/http_decoder.h" + #include "net/third_party/quiche/src/quic/core/http/http_encoder.h" +#include "net/third_party/quiche/src/quic/core/quic_data_writer.h" #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" @@ -479,4 +481,30 @@ EXPECT_EQ("", decoder_.error_detail()); } +TEST_F(HttpDecoderTest, MalformedFrameWithOverlyLargePayload) { + char input[] = {0x10, // length + 0x03, // type (CANCEL_PUSH) + 0x15}; // malformed payload + // Process the full frame. + EXPECT_CALL(visitor_, OnError(&decoder_)); + EXPECT_EQ(0, decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))); + EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error()); + EXPECT_EQ("Frame is too large", decoder_.error_detail()); +} + +TEST_F(HttpDecoderTest, MalformedSettingsFrame) { + char input[30]; + QuicDataWriter writer(30, input); + // Write length. + writer.WriteVarInt62(2048 * 1024); + // Write type SETTINGS. + writer.WriteUInt8(0x04); + + writer.WriteStringPiece("Malformed payload"); + EXPECT_CALL(visitor_, OnError(&decoder_)); + EXPECT_EQ(0, decoder_.ProcessInput(input, QUIC_ARRAYSIZE(input))); + EXPECT_EQ(QUIC_INTERNAL_ERROR, decoder_.error()); + EXPECT_EQ("Frame is too large", decoder_.error_detail()); +} + } // namespace quic