Validates the value of SETTINGS_MAX_FRAME_SIZE in oghttp2.

PiperOrigin-RevId: 453247197
diff --git a/quiche/http2/adapter/http2_protocol.h b/quiche/http2/adapter/http2_protocol.h
index 817d6be..02a423a 100644
--- a/quiche/http2/adapter/http2_protocol.h
+++ b/quiche/http2/adapter/http2_protocol.h
@@ -51,7 +51,11 @@
 
 // The default value for the size of the largest frame payload, according to RFC
 // 7540 Section 6.5.2 (SETTINGS_MAX_FRAME_SIZE).
-const int kDefaultFramePayloadSizeLimit = 16 * 1024;
+const uint32_t kDefaultFramePayloadSizeLimit = 16u * 1024u;
+
+// The maximum value for the size of the largest frame payload, according to RFC
+// 7540 Section 6.5.2 (SETTINGS_MAX_FRAME_SIZE).
+const uint32_t kMaximumFramePayloadSizeLimit = 16777215u;
 
 // The default value for the initial stream and connection flow control window
 // size, according to RFC 7540 Section 6.9.2.
diff --git a/quiche/http2/adapter/http2_util.cc b/quiche/http2/adapter/http2_util.cc
index 963d264..707fa00 100644
--- a/quiche/http2/adapter/http2_util.cc
+++ b/quiche/http2/adapter/http2_util.cc
@@ -101,6 +101,8 @@
       return "FlowControlError";
     case ConnectionError::kInvalidGoAwayLastStreamId:
       return "InvalidGoAwayLastStreamId";
+    case ConnectionError::kInvalidSetting:
+      return "InvalidSetting";
   }
   return "UnknownConnectionError";
 }
diff --git a/quiche/http2/adapter/http2_visitor_interface.h b/quiche/http2/adapter/http2_visitor_interface.h
index 2ec68c5..4e6bcb8 100644
--- a/quiche/http2/adapter/http2_visitor_interface.h
+++ b/quiche/http2/adapter/http2_visitor_interface.h
@@ -82,6 +82,8 @@
     kFlowControlError,
     // The peer sent a GOAWAY with an invalid last-stream-ID field.
     kInvalidGoAwayLastStreamId,
+    // The peer sent an invalid SETTINGS value.
+    kInvalidSetting,
   };
   virtual void OnConnectionError(ConnectionError error) = 0;
 
diff --git a/quiche/http2/adapter/nghttp2_adapter_test.cc b/quiche/http2/adapter/nghttp2_adapter_test.cc
index 22c8f83..8647b02 100644
--- a/quiche/http2/adapter/nghttp2_adapter_test.cc
+++ b/quiche/http2/adapter/nghttp2_adapter_test.cc
@@ -5883,6 +5883,35 @@
   EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS}));
 }
 
+TEST(NgHttp2AdapterTest, InvalidMaxFrameSize) {
+  DataSavingVisitor visitor;
+  auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
+
+  const std::string frames =
+      TestFrameSequence().ClientPreface({{MAX_FRAME_SIZE, 3}}).Serialize();
+  testing::InSequence s;
+
+  // Client preface
+  EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0));
+  EXPECT_CALL(
+      visitor,
+      OnInvalidFrame(0, Http2VisitorInterface::InvalidFrameError::kProtocol));
+
+  const int64_t read_result = adapter->ProcessBytes(frames);
+  EXPECT_EQ(static_cast<size_t>(read_result), frames.size());
+
+  EXPECT_TRUE(adapter->want_write());
+
+  EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
+  EXPECT_CALL(visitor,
+              OnFrameSent(GOAWAY, 0, _, 0x0,
+                          static_cast<int>(Http2ErrorCode::PROTOCOL_ERROR)));
+
+  int send_result = adapter->Send();
+  EXPECT_EQ(0, send_result);
+  EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY}));
+}
+
 TEST(NgHttp2AdapterTest, ServerForbidsProtocolPseudoheaderBeforeAck) {
   DataSavingVisitor visitor;
   auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
diff --git a/quiche/http2/adapter/oghttp2_adapter_test.cc b/quiche/http2/adapter/oghttp2_adapter_test.cc
index f09867c..ba195bc 100644
--- a/quiche/http2/adapter/oghttp2_adapter_test.cc
+++ b/quiche/http2/adapter/oghttp2_adapter_test.cc
@@ -347,6 +347,42 @@
               EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS}));
 }
 
+TEST(OgHttp2AdapterTest, InvalidMaxFrameSize) {
+  DataSavingVisitor visitor;
+  OgHttp2Adapter::Options options;
+  options.perspective = Perspective::kServer;
+  auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+  const std::string frames =
+      TestFrameSequence().ClientPreface({{MAX_FRAME_SIZE, 3}}).Serialize();
+  testing::InSequence s;
+
+  // Client preface
+  EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0));
+  EXPECT_CALL(visitor, OnSettingsStart());
+  EXPECT_CALL(
+      visitor,
+      OnInvalidFrame(0, Http2VisitorInterface::InvalidFrameError::kProtocol));
+  EXPECT_CALL(visitor, OnConnectionError(ConnectionError::kInvalidSetting));
+
+  const int64_t read_result = adapter->ProcessBytes(frames);
+  EXPECT_EQ(static_cast<size_t>(read_result), frames.size());
+
+  EXPECT_TRUE(adapter->want_write());
+
+  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+  EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0));
+  EXPECT_CALL(visitor,
+              OnFrameSent(GOAWAY, 0, _, 0x0,
+                          static_cast<int>(Http2ErrorCode::PROTOCOL_ERROR)));
+
+  int send_result = adapter->Send();
+  EXPECT_EQ(0, send_result);
+  EXPECT_THAT(visitor.data(),
+              EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::GOAWAY}));
+}
+
 TEST(OgHttp2AdapterTest, ClientHandles100Headers) {
   DataSavingVisitor visitor;
   OgHttp2Adapter::Options options;
diff --git a/quiche/http2/adapter/oghttp2_session.cc b/quiche/http2/adapter/oghttp2_session.cc
index 0f0d6bd..8be0fe8 100644
--- a/quiche/http2/adapter/oghttp2_session.cc
+++ b/quiche/http2/adapter/oghttp2_session.cc
@@ -1219,6 +1219,16 @@
 void OgHttp2Session::OnSetting(spdy::SpdySettingsId id, uint32_t value) {
   switch (id) {
     case MAX_FRAME_SIZE:
+      if (value < kDefaultFramePayloadSizeLimit ||
+          value > kMaximumFramePayloadSizeLimit) {
+        visitor_.OnInvalidFrame(
+            0, Http2VisitorInterface::InvalidFrameError::kProtocol);
+        // The specification says this is a connection-level protocol error.
+        LatchErrorAndNotify(
+            Http2ErrorCode::PROTOCOL_ERROR,
+            Http2VisitorInterface::ConnectionError::kInvalidSetting);
+        return;
+      }
       max_frame_payload_ = value;
       break;
     case MAX_CONCURRENT_STREAMS: