Signal error in HttpDecoder on empty PUSH_PROMISE frame.

Currently on an empty, invalid PUSH_PROMISE frame HttpDecoder transitions from
STATE_READING_FRAME_LENGTH directly to STATE_FINISH_PARSING, skipping
STATE_READING_FRAME_PAYLOAD, which results in calling
Visitor::OnPushPromiseFrameEnd() without calling
Visitor::OnPushPromiseFrameStart().  This is wrong and can cause QuicSpdyStream
to crash.

This was caught by ClusterFuzz at https://crbug.com/1001823.

Also add tests for other empty frames, and sanity DCHECKs in QuicSpdyStream.

gfe-relnote: n/a, change to QUIC v99-only code.  Protected by existing disabled
gfe2_reloadable_flag_quic_enable_version_99.
PiperOrigin-RevId: 270386637
Change-Id: I0c1944d1df300136d27367679e3128dd45e9bfd3
diff --git a/quic/core/http/http_decoder.cc b/quic/core/http/http_decoder.cc
index c9519fe..e1fa36e 100644
--- a/quic/core/http/http_decoder.cc
+++ b/quic/core/http/http_decoder.cc
@@ -153,6 +153,10 @@
       continue_processing = visitor_->OnSettingsFrameStart(header_length);
       break;
     case static_cast<uint64_t>(HttpFrameType::PUSH_PROMISE):
+      if (current_frame_length_ == 0) {
+        RaiseError(QUIC_INVALID_FRAME_DATA, "Corrupt PUSH_PROMISE frame.");
+        return false;
+      }
       break;
     case static_cast<uint64_t>(HttpFrameType::GOAWAY):
       break;
diff --git a/quic/core/http/http_decoder_test.cc b/quic/core/http/http_decoder_test.cc
index 6847ecb..3abc8be 100644
--- a/quic/core/http/http_decoder_test.cc
+++ b/quic/core/http/http_decoder_test.cc
@@ -919,7 +919,78 @@
       EXPECT_EQ(test_data.error_message, decoder.error_detail());
     }
   }
-}  // namespace test
+}
+
+TEST_F(HttpDecoderTest, EmptyCancelPushFrame) {
+  std::string input = QuicTextUtils::HexDecode(
+      "03"    // type (CANCEL_PUSH)
+      "00");  // frame length
+
+  EXPECT_CALL(visitor_, OnError(&decoder_));
+  EXPECT_EQ(input.size(), ProcessInput(input));
+  EXPECT_EQ(QUIC_INVALID_FRAME_DATA, decoder_.error());
+  EXPECT_EQ("Unable to read push_id", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, EmptySettingsFrame) {
+  std::string input = QuicTextUtils::HexDecode(
+      "04"    // type (SETTINGS)
+      "00");  // frame length
+
+  EXPECT_CALL(visitor_, OnSettingsFrameStart(2));
+
+  SettingsFrame empty_frame;
+  EXPECT_CALL(visitor_, OnSettingsFrame(empty_frame));
+
+  EXPECT_EQ(input.size(), ProcessInput(input));
+  EXPECT_EQ(QUIC_NO_ERROR, decoder_.error());
+  EXPECT_EQ("", decoder_.error_detail());
+}
+
+// Regression test for https://crbug.com/1001823.
+TEST_F(HttpDecoderTest, EmptyPushPromiseFrame) {
+  std::string input = QuicTextUtils::HexDecode(
+      "05"    // type (PUSH_PROMISE)
+      "00");  // frame length
+
+  EXPECT_CALL(visitor_, OnError(&decoder_));
+  EXPECT_EQ(input.size(), ProcessInput(input));
+  EXPECT_EQ(QUIC_INVALID_FRAME_DATA, decoder_.error());
+  EXPECT_EQ("Corrupt PUSH_PROMISE frame.", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, EmptyGoAwayFrame) {
+  std::string input = QuicTextUtils::HexDecode(
+      "07"    // type (GOAWAY)
+      "00");  // frame length
+
+  EXPECT_CALL(visitor_, OnError(&decoder_));
+  EXPECT_EQ(input.size(), ProcessInput(input));
+  EXPECT_EQ(QUIC_INVALID_FRAME_DATA, decoder_.error());
+  EXPECT_EQ("Unable to read GOAWAY stream_id", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, EmptyMaxPushIdFrame) {
+  std::string input = QuicTextUtils::HexDecode(
+      "0d"    // type (MAX_PUSH_ID)
+      "00");  // frame length
+
+  EXPECT_CALL(visitor_, OnError(&decoder_));
+  EXPECT_EQ(input.size(), ProcessInput(input));
+  EXPECT_EQ(QUIC_INVALID_FRAME_DATA, decoder_.error());
+  EXPECT_EQ("Unable to read push_id", decoder_.error_detail());
+}
+
+TEST_F(HttpDecoderTest, EmptyDuplicatePushFrame) {
+  std::string input = QuicTextUtils::HexDecode(
+      "0e"    // type (DUPLICATE_PUSH)
+      "00");  // frame length
+
+  EXPECT_CALL(visitor_, OnError(&decoder_));
+  EXPECT_EQ(input.size(), ProcessInput(input));
+  EXPECT_EQ(QUIC_INVALID_FRAME_DATA, decoder_.error());
+  EXPECT_EQ("Unable to read push_id", decoder_.error_detail());
+}
 
 }  // namespace test
 
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index a407d5c..a089345 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -127,7 +127,7 @@
   bool OnPushPromiseFrameStart(PushId push_id,
                                QuicByteCount header_length,
                                QuicByteCount push_id_length) override {
-    if (!VersionHasStreamType(stream_->transport_version())) {
+    if (!VersionUsesQpack(stream_->transport_version())) {
       CloseConnectionOnWrongFrame("Push Promise");
       return false;
     }
@@ -880,6 +880,7 @@
 
 bool QuicSpdyStream::OnHeadersFramePayload(QuicStringPiece payload) {
   DCHECK(VersionUsesQpack(transport_version()));
+  DCHECK(qpack_decoded_headers_accumulator_);
 
   if (headers_decompressed_) {
     trailers_payload_length_ += payload.length();
@@ -904,6 +905,7 @@
 
 bool QuicSpdyStream::OnHeadersFrameEnd() {
   DCHECK(VersionUsesQpack(transport_version()));
+  DCHECK(qpack_decoded_headers_accumulator_);
 
   auto result = qpack_decoded_headers_accumulator_->EndHeaderBlock();