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