gfe-relnote: Close QUIC connection is there are too many (> 1000) buffered control frames in control frame manager. Protected by gfe2_reloadable_flag_quic_add_upper_limit_of_buffered_control_frames.

PiperOrigin-RevId: 261002263
Change-Id: I4dd92d5c1ea159d05cf35719f1a7ccc36f7b3fd3
diff --git a/quic/core/quic_control_frame_manager.cc b/quic/core/quic_control_frame_manager.cc
index 511ef2b..ac4488b 100644
--- a/quic/core/quic_control_frame_manager.cc
+++ b/quic/core/quic_control_frame_manager.cc
@@ -14,11 +14,25 @@
 
 namespace quic {
 
+namespace {
+
+// The maximum number of buffered control frames which are waiting to be ACKed
+// or sent for the first time.
+const size_t kMaxNumControlFrames = 1000;
+
+}  // namespace
+
 QuicControlFrameManager::QuicControlFrameManager(QuicSession* session)
     : last_control_frame_id_(kInvalidControlFrameId),
       least_unacked_(1),
       least_unsent_(1),
-      session_(session) {}
+      session_(session),
+      add_upper_limit_(GetQuicReloadableFlag(
+          quic_add_upper_limit_of_buffered_control_frames)) {
+  if (add_upper_limit_) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_add_upper_limit_of_buffered_control_frames);
+  }
+}
 
 QuicControlFrameManager::~QuicControlFrameManager() {
   while (!control_frames_.empty()) {
@@ -30,6 +44,15 @@
 void QuicControlFrameManager::WriteOrBufferQuicFrame(QuicFrame frame) {
   const bool had_buffered_frames = HasBufferedFrames();
   control_frames_.emplace_back(frame);
+  if (add_upper_limit_ && control_frames_.size() > kMaxNumControlFrames) {
+    session_->connection()->CloseConnection(
+        QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES,
+        QuicStrCat("More than ", kMaxNumControlFrames,
+                   "buffered control frames, least_unacked: ", least_unacked_,
+                   ", least_unsent_: ", least_unsent_),
+        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    return;
+  }
   if (had_buffered_frames) {
     return;
   }
@@ -101,6 +124,15 @@
   }
   control_frames_.emplace_back(
       QuicFrame(QuicPingFrame(++last_control_frame_id_)));
+  if (add_upper_limit_ && control_frames_.size() > kMaxNumControlFrames) {
+    session_->connection()->CloseConnection(
+        QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES,
+        QuicStrCat("More than ", kMaxNumControlFrames,
+                   "buffered control frames, least_unacked: ", least_unacked_,
+                   ", least_unsent_: ", least_unsent_),
+        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    return;
+  }
   WriteBufferedFrames();
 }
 
@@ -172,6 +204,9 @@
   }
   if (!QuicContainsKey(pending_retransmissions_, id)) {
     pending_retransmissions_[id] = true;
+    QUIC_BUG_IF(pending_retransmissions_.size() > control_frames_.size())
+        << "least_unacked_: " << least_unacked_
+        << ", least_unsent_: " << least_unsent_;
   }
 }
 
diff --git a/quic/core/quic_control_frame_manager.h b/quic/core/quic_control_frame_manager.h
index a4c2678..54ca479 100644
--- a/quic/core/quic_control_frame_manager.h
+++ b/quic/core/quic_control_frame_manager.h
@@ -146,6 +146,9 @@
 
   // Last sent window update frame for each stream.
   QuicSmallMap<QuicStreamId, QuicControlFrameId, 10> window_update_frames_;
+
+  // Latched value of quic_add_upper_limit_of_buffered_control_frames.
+  const bool add_upper_limit_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_control_frame_manager_test.cc b/quic/core/quic_control_frame_manager_test.cc
index 216ba74..8059ff6 100644
--- a/quic/core/quic_control_frame_manager_test.cc
+++ b/quic/core/quic_control_frame_manager_test.cc
@@ -4,6 +4,7 @@
 
 #include "net/third_party/quiche/src/quic/core/quic_control_frame_manager.h"
 
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
@@ -288,6 +289,27 @@
   EXPECT_FALSE(manager_->WillingToWrite());
 }
 
+TEST_F(QuicControlFrameManagerTest, TooManyBufferedControlFrames) {
+  SetQuicReloadableFlag(quic_add_upper_limit_of_buffered_control_frames, true);
+  Initialize();
+  EXPECT_CALL(*connection_, SendControlFrame(_))
+      .Times(5)
+      .WillRepeatedly(Invoke(&ClearControlFrame));
+  // Flush buffered frames.
+  manager_->OnCanWrite();
+  // Write 995 control frames.
+  EXPECT_CALL(*connection_, SendControlFrame(_)).WillOnce(Return(false));
+  for (size_t i = 0; i < 995; ++i) {
+    manager_->WriteOrBufferRstStream(kTestStreamId, QUIC_STREAM_CANCELLED, 0);
+  }
+  // Verify write one more control frame causes connection close.
+  EXPECT_CALL(
+      *connection_,
+      CloseConnection(QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES, _,
+                      ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET));
+  manager_->WriteOrBufferRstStream(kTestStreamId, QUIC_STREAM_CANCELLED, 0);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index f13eec4..1be1786 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -157,6 +157,7 @@
     RETURN_STRING_LITERAL(QUIC_IETF_GQUIC_ERROR_MISSING);
     RETURN_STRING_LITERAL(
         QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM);
+    RETURN_STRING_LITERAL(QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES);
 
     RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
     // Intentionally have no default case, so we'll break the build
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index abc666d..534d0b5 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -334,8 +334,11 @@
   // Received WindowUpdate on a READ_UNIDIRECTIONAL stream.
   QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM = 123,
 
+  // There are too many buffered control frames in control frame manager.
+  QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES = 124,
+
   // No error. Used as bound while iterating.
-  QUIC_LAST_ERROR = 124,
+  QUIC_LAST_ERROR = 125,
 };
 // QuicErrorCodes is encoded as a single octet on-the-wire.
 static_assert(static_cast<int>(QUIC_LAST_ERROR) <=