Rejects WINDOW_UPDATE frames that would cause a window overflow, as per Section 6.9.1 of RFC 9113.

PiperOrigin-RevId: 484541291
diff --git a/quiche/http2/adapter/oghttp2_adapter_test.cc b/quiche/http2/adapter/oghttp2_adapter_test.cc
index 2ccde27..70daec0 100644
--- a/quiche/http2/adapter/oghttp2_adapter_test.cc
+++ b/quiche/http2/adapter/oghttp2_adapter_test.cc
@@ -3894,6 +3894,72 @@
   adapter->Send();
 }
 
+TEST(OgHttp2AdapterTest, WindowUpdateCausesWindowOverflow) {
+  DataSavingVisitor visitor;
+  OgHttp2Adapter::Options options;
+  options.perspective = Perspective::kServer;
+  auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+  const std::string data_chunk(kDefaultFramePayloadSizeLimit, 'a');
+  const std::string request =
+      TestFrameSequence()
+          .ClientPreface()
+          .Headers(1,
+                   {{":method", "GET"},
+                    {":scheme", "https"},
+                    {":authority", "example.com"},
+                    {":path", "/"}},
+                   /*fin=*/false)
+          .WindowUpdate(1, std::numeric_limits<int>::max())
+          .Data(1, "Subsequent frames on stream 1 are not delivered.")
+          .Serialize();
+  // Client preface (empty SETTINGS)
+  EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+  EXPECT_CALL(visitor, OnSettingsStart());
+  EXPECT_CALL(visitor, OnSettingsEnd());
+  // Stream 1
+  EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+  EXPECT_CALL(visitor, OnHeaderForStream).Times(4);
+  EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+
+  EXPECT_CALL(visitor, OnFrameHeader(1, 4, WINDOW_UPDATE, 0));
+
+  adapter->ProcessBytes(request);
+
+  EXPECT_TRUE(adapter->want_write());
+  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0));
+  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0));
+
+  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1));
+  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0));
+
+  EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, _, 0x0));
+  EXPECT_CALL(
+      visitor,
+      OnFrameSent(RST_STREAM, 1, _, 0x0,
+                  static_cast<int>(Http2ErrorCode::FLOW_CONTROL_ERROR)));
+  EXPECT_CALL(visitor, OnCloseStream(1, _));
+
+  adapter->Send();
+
+  const std::string window_update =
+      TestFrameSequence()
+          .WindowUpdate(0, std::numeric_limits<int>::max())
+          .Serialize();
+
+  EXPECT_CALL(visitor, OnFrameHeader(0, 4, WINDOW_UPDATE, 0));
+  EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kFlowControlError));
+  adapter->ProcessBytes(window_update);
+
+  EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
+  EXPECT_CALL(
+      visitor,
+      OnFrameSent(GOAWAY, 0, _, 0x0,
+                  static_cast<int>(Http2ErrorCode::FLOW_CONTROL_ERROR)));
+  adapter->Send();
+}
+
 TEST(OgHttp2AdapterTest, WindowUpdateRaisesFlowControlWindowLimit) {
   DataSavingVisitor visitor;
   OgHttp2Adapter::Options options;
diff --git a/quiche/http2/adapter/oghttp2_session.cc b/quiche/http2/adapter/oghttp2_session.cc
index bcec1cd..52c0a12 100644
--- a/quiche/http2/adapter/oghttp2_session.cc
+++ b/quiche/http2/adapter/oghttp2_session.cc
@@ -1429,6 +1429,7 @@
 
 void OgHttp2Session::OnWindowUpdate(spdy::SpdyStreamId stream_id,
                                     int delta_window_size) {
+  constexpr int kMaxWindowValue = 2147483647;  // (1 << 31) - 1
   if (stream_id == 0) {
     if (delta_window_size == 0) {
       // A PROTOCOL_ERROR, according to RFC 9113 Section 6.9.
@@ -1436,6 +1437,13 @@
                           ConnectionError::kFlowControlError);
       return;
     }
+    if (connection_send_window_ > 0 &&
+        delta_window_size > (kMaxWindowValue - connection_send_window_)) {
+      // Window overflow is a FLOW_CONTROL_ERROR.
+      LatchErrorAndNotify(Http2ErrorCode::FLOW_CONTROL_ERROR,
+                          ConnectionError::kFlowControlError);
+      return;
+    }
     connection_send_window_ += delta_window_size;
   } else {
     if (delta_window_size == 0) {
@@ -1459,6 +1467,13 @@
       if (streams_reset_.contains(stream_id)) {
         return;
       }
+      if (it->second.send_window > 0 &&
+          delta_window_size > (kMaxWindowValue - it->second.send_window)) {
+        // Window overflow is a FLOW_CONTROL_ERROR.
+        EnqueueFrame(std::make_unique<spdy::SpdyRstStreamIR>(
+            stream_id, spdy::ERROR_CODE_FLOW_CONTROL_ERROR));
+        return;
+      }
       const bool was_blocked = (it->second.send_window <= 0);
       it->second.send_window += delta_window_size;
       if (was_blocked && it->second.send_window > 0) {