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.