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