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