Disallow invalid MAX_PUSH_ID frame

This frame only contains a variable length integer, so its payload length is not allowed to exceed  8 bytes. Note that gfe2_reloadable_flag_quic_reject_large_max_push_id is default-enabled.

Protected by FLAGS_quic_reloadable_flag_quic_reject_large_max_push_id.

PiperOrigin-RevId: 385247216
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc
index 92a2e38..b20789b 100644
--- a/quic/core/http/http_decoder.cc
+++ b/quic/core/http/http_decoder.cc
@@ -669,6 +669,13 @@
       return 1024 * 1024;
     case static_cast<uint64_t>(HttpFrameType::GOAWAY):
       return VARIABLE_LENGTH_INTEGER_LENGTH_8;
+    case static_cast<uint64_t>(HttpFrameType::MAX_PUSH_ID):
+      if (GetQuicReloadableFlag(quic_reject_large_max_push_id)) {
+        QUIC_RELOADABLE_FLAG_COUNT(quic_reject_large_max_push_id);
+        return VARIABLE_LENGTH_INTEGER_LENGTH_8;
+      } else {
+        return std::numeric_limits<QuicByteCount>::max();
+      }
     case static_cast<uint64_t>(HttpFrameType::PRIORITY_UPDATE_REQUEST_STREAM):
       // This limit is arbitrary.
       return 1024 * 1024;
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc
index 36bc2d1..7fa09ba 100644
--- a/quic/core/http/http_decoder_test.cc
+++ b/quic/core/http/http_decoder_test.cc
@@ -603,7 +603,7 @@
   EXPECT_EQ("", decoder_.error_detail());
 }
 
-TEST_F(HttpDecoderTest, MalformedFrameWithOverlyLargePayload) {
+TEST_F(HttpDecoderTest, GoawayWithOverlyLargePayload) {
   std::string input = absl::HexStringToBytes(
       "07"    // type (GOAWAY)
       "10");  // length exceeding the maximum possible length for GOAWAY frame
@@ -614,6 +614,24 @@
   EXPECT_EQ("Frame is too large.", decoder_.error_detail());
 }
 
+TEST_F(HttpDecoderTest, MaxPushIdWithOverlyLargePayload) {
+  std::string input = absl::HexStringToBytes(
+      "0d"    // type (MAX_PUSH_ID)
+      "10");  // length exceeding the maximum possible length for MAX_PUSH_ID
+              // frame
+  // Process all data at once.
+  if (GetQuicReloadableFlag(quic_reject_large_max_push_id)) {
+    EXPECT_CALL(visitor_, OnError(&decoder_));
+    EXPECT_EQ(2u, ProcessInput(input));
+    EXPECT_THAT(decoder_.error(), IsError(QUIC_HTTP_FRAME_TOO_LARGE));
+    EXPECT_EQ("Frame is too large.", decoder_.error_detail());
+  } else {
+    EXPECT_EQ(2u, ProcessInput(input));
+    EXPECT_THAT(decoder_.error(), IsQuicNoError());
+    EXPECT_EQ("", decoder_.error_detail());
+  }
+}
+
 TEST_F(HttpDecoderTest, MalformedSettingsFrame) {
   char input[30];
   QuicDataWriter writer(30, input);
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 08922c7..002dd3b 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -35,6 +35,8 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allow_client_enabled_bbr_v2, false)
 // If true, allow ticket open to be ignored in TlsServerHandshaker. Also fixes TlsServerHandshaker::ResumptionAttempted when handshake hints is used.
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_allow_ignore_ticket_open, true)
+// If true, close connection if a MAX_PUSH_ID header with length field exceeding maximum valid payload length of 8 bytes is received.
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_reject_large_max_push_id, true)
 // If true, close read side but not write side in QuicSpdyStream::OnStreamReset().
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_on_stream_reset, true)
 // If true, default on PTO which unifies TLP + RTO loss recovery.