gfe-relnote: Fix a bug in QuicSpdyClientStreamBase. Protected by --gfe2_reloadable_flag_quic_eliminate_static_stream_map_3

When eliminate_static_stream_map is true, GetSpdyDataStream() will be called with a stream id that may not be a spdy stream. This results in an invalid cast which blows up in some Chrome tests. I've added a DCHECK in GetSpdyDataStream() to make sure we never do this again, and have added and IsStaticStream() method which can be used for checks in this case. Renames gfe2_reloadable_flag_quic_eliminate_static_stream_map_2 to gfe2_reloadable_flag_quic_eliminate_static_stream_map_3.

PiperOrigin-RevId: 248762017
Change-Id: Iab361cc46408cd57e38848f172a7f826f346b28d
diff --git a/quic/core/http/quic_spdy_client_session_base.cc b/quic/core/http/quic_spdy_client_session_base.cc
index 2db5b84..93f6880 100644
--- a/quic/core/http/quic_spdy_client_session_base.cc
+++ b/quic/core/http/quic_spdy_client_session_base.cc
@@ -64,7 +64,7 @@
     QuicStreamId promised_stream_id,
     size_t frame_len,
     const QuicHeaderList& header_list) {
-  if (QuicContainsKey(static_streams(), stream_id)) {
+  if (IsStaticStream(stream_id)) {
     connection()->CloseConnection(
         QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
@@ -95,12 +95,6 @@
     // It's quite possible to receive headers after a stream has been reset.
     return;
   }
-  if (eliminate_static_stream_map() && stream->is_static()) {
-    connection()->CloseConnection(
-        QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
-    return;
-  }
   stream->OnPromiseHeaderList(promised_stream_id, frame_len, header_list);
 }
 
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 5eec97d..bdaa433 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -363,7 +363,7 @@
         QuicUtils::GetHeadersStreamId(connection()->transport_version()),
         headers_stream_.get());
   } else {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 7, 17);
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 7, 17);
     unowned_headers_stream_ = headers_stream_.get();
     RegisterStaticStreamNew(std::move(headers_stream_));
   }
@@ -416,7 +416,7 @@
                                          bool fin,
                                          size_t frame_len,
                                          const QuicHeaderList& header_list) {
-  if (QuicContainsKey(static_streams(), stream_id)) {
+  if (IsStaticStream(stream_id)) {
     connection()->CloseConnection(
         QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
@@ -447,13 +447,6 @@
     // It's quite possible to receive headers after a stream has been reset.
     return;
   }
-  if (eliminate_static_stream_map() && stream->is_static()) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 8, 17);
-    connection()->CloseConnection(
-        QUIC_INVALID_HEADERS_STREAM_DATA, "stream is static",
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
-    return;
-  }
   stream->OnStreamHeaderList(fin, frame_len, header_list);
 }
 
@@ -542,7 +535,9 @@
 
 QuicSpdyStream* QuicSpdySession::GetSpdyDataStream(
     const QuicStreamId stream_id) {
-  return static_cast<QuicSpdyStream*>(GetOrCreateDynamicStream(stream_id));
+  QuicStream* stream = GetOrCreateDynamicStream(stream_id);
+  DCHECK(!stream || !stream->is_static());
+  return static_cast<QuicSpdyStream*>(stream);
 }
 
 void QuicSpdySession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
@@ -716,7 +711,7 @@
   // In the case where session is destructed by calling
   // dynamic_streams().clear(), we will have incorrect accounting here.
   // TODO(renjietang): Modify destructors and make this a DCHECK.
-  QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 9, 17);
+  QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 9, 17);
   if (static_cast<size_t>(dynamic_streams().size()) >
       num_incoming_static_streams() + num_outgoing_static_streams()) {
     return dynamic_streams().size() - num_incoming_static_streams() -
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 8217cd7..0e2fcf8 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -926,7 +926,7 @@
     EXPECT_CALL(*crypto_stream, OnCanWrite());
   }
   TestHeadersStream* headers_stream;
-  if (!GetQuicReloadableFlag(quic_eliminate_static_stream_map_2) &&
+  if (!GetQuicReloadableFlag(quic_eliminate_static_stream_map_3) &&
       !QuicVersionUsesCryptoFrames(connection_->transport_version())) {
     QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr);
     headers_stream = new TestHeadersStream(&session_);
@@ -1669,7 +1669,7 @@
 
 TEST_P(QuicSpdySessionTestClient, WritePriority) {
   TestHeadersStream* headers_stream;
-  if (!GetQuicReloadableFlag(quic_eliminate_static_stream_map_2) &&
+  if (!GetQuicReloadableFlag(quic_eliminate_static_stream_map_3) &&
       !QuicVersionUsesCryptoFrames(connection_->transport_version())) {
     QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr);
     headers_stream = new TestHeadersStream(&session_);
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index abc6331..6d526ab 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -89,7 +89,7 @@
       closed_streams_clean_up_alarm_(nullptr),
       supported_versions_(supported_versions),
       eliminate_static_stream_map_(
-          GetQuicReloadableFlag(quic_eliminate_static_stream_map_2) ||
+          GetQuicReloadableFlag(quic_eliminate_static_stream_map_3) ||
           QuicVersionUsesCryptoFrames(connection->transport_version())) {
   closed_streams_clean_up_alarm_ =
       QuicWrapUnique<QuicAlarm>(connection_->alarm_factory()->CreateAlarm(
@@ -113,7 +113,7 @@
         QuicUtils::GetCryptoStreamId(connection_->transport_version()),
         GetMutableCryptoStream());
   } else {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 10, 17);
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 10, 17);
     QuicStreamId id =
         QuicUtils::GetCryptoStreamId(connection_->transport_version());
     largest_static_stream_id_ = std::max(id, largest_static_stream_id_);
@@ -216,7 +216,7 @@
   }
   if (eliminate_static_stream_map_ && frame.fin &&
       handler.stream->is_static()) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 1, 17);
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 1, 17);
     connection()->CloseConnection(
         QUIC_INVALID_STREAM_ID, "Attempt to close a static stream",
         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
@@ -304,7 +304,7 @@
   }
 
   if (eliminate_static_stream_map_ && stream->is_static()) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 2, 17);
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 2, 17);
     QUIC_DVLOG(1) << ENDPOINT
                   << "Received STOP_SENDING for a static stream, id: "
                   << stream_id << " Closing connection";
@@ -380,7 +380,7 @@
     return;  // Errors are handled by GetOrCreateStream.
   }
   if (eliminate_static_stream_map_ && handler.stream->is_static()) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 3, 17);
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 3, 17);
     connection()->CloseConnection(
         QUIC_INVALID_STREAM_ID, "Attempt to reset a static stream",
         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
@@ -439,7 +439,7 @@
       }
     }
   } else {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 4, 17);
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 4, 17);
     // Copy all non static streams in a new map for the ease of deleting.
     QuicSmallMap<QuicStreamId, QuicStream*, 10> non_static_streams;
     for (const auto& it : dynamic_stream_map_) {
@@ -770,7 +770,7 @@
   DynamicStreamMap::iterator it = dynamic_stream_map_.find(id);
   if (it != dynamic_stream_map_.end()) {
     if (eliminate_static_stream_map_ && it->second->is_static()) {
-      QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 5, 17);
+      QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 5, 17);
       QUIC_DVLOG(1) << ENDPOINT
                     << "Try to send rst for a static stream, id: " << id
                     << " Closing connection";
@@ -845,7 +845,7 @@
   }
   QuicStream* stream = it->second.get();
   if (eliminate_static_stream_map_ && stream->is_static()) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 6, 17);
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 6, 17);
     QUIC_DVLOG(1) << ENDPOINT
                   << "Try to close a static stream, id: " << stream_id
                   << " Closing connection";
@@ -1093,7 +1093,7 @@
   }
   if (eliminate_static_stream_map_ &&
       !QuicVersionUsesCryptoFrames(connection_->transport_version())) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 11, 17);
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 11, 17);
     GetMutableCryptoStream()->flow_controller()->UpdateReceiveWindowSize(
         stream_window);
   }
@@ -1144,7 +1144,7 @@
   }
   if (eliminate_static_stream_map_ &&
       !QuicVersionUsesCryptoFrames(connection_->transport_version())) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 12, 17);
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 12, 17);
     GetMutableCryptoStream()->UpdateSendWindowOffset(new_window);
   }
 }
@@ -1276,7 +1276,7 @@
   if (eliminate_static_stream_map_ &&
       QuicUtils::IsCryptoStreamId(connection_->transport_version(),
                                   stream_id)) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 13, 17);
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 13, 17);
     return StreamHandler(GetMutableCryptoStream());
   }
   StaticStreamMap::iterator it = static_stream_map_.find(stream_id);
@@ -1423,6 +1423,18 @@
   return false;
 }
 
+bool QuicSession::IsStaticStream(QuicStreamId id) const {
+  if (eliminate_static_stream_map()) {
+    auto it = dynamic_stream_map_.find(id);
+    if (it == dynamic_stream_map_.end()) {
+      return false;
+    }
+    return it->second->is_static();
+  }
+
+  return QuicContainsKey(static_streams(), id);
+}
+
 size_t QuicSession::GetNumOpenIncomingStreams() const {
   return num_dynamic_incoming_streams_ - num_draining_incoming_streams_ +
          num_locally_closed_incoming_streams_highest_offset_;
@@ -1511,7 +1523,7 @@
   if (eliminate_static_stream_map_ &&
       !QuicVersionUsesCryptoFrames(connection_->transport_version()) &&
       GetMutableCryptoStream()->flow_controller()->IsBlocked()) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 14, 17);
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 14, 17);
     return true;
   }
   return false;
@@ -1572,7 +1584,7 @@
 
   if (eliminate_static_stream_map_ &&
       QuicUtils::IsCryptoStreamId(connection_->transport_version(), id)) {
-    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_2, 15, 17);
+    QUIC_RELOADABLE_FLAG_COUNT_N(quic_eliminate_static_stream_map_3, 15, 17);
     return const_cast<QuicCryptoStream*>(GetCryptoStream());
   }
 
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 7da8ec3..d8f40c9 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -525,6 +525,9 @@
   // Returns true if the stream is still active.
   bool IsOpenStream(QuicStreamId id);
 
+  // Returns true if the stream is a static stream.
+  bool IsStaticStream(QuicStreamId id) const;
+
   // Close connection when receive a frame for a locally-created nonexistant
   // stream.
   // Prerequisite: IsClosedStream(stream_id) == false
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 5af49fa..5b86bdd 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -1268,7 +1268,7 @@
       QuicUtils::GetHeadersStreamId(connection_->transport_version());
   std::unique_ptr<TestStream> fake_headers_stream = QuicMakeUnique<TestStream>(
       headers_stream_id, &session_, /*is_static*/ true, BIDIRECTIONAL);
-  if (GetQuicReloadableFlag(quic_eliminate_static_stream_map_2)) {
+  if (GetQuicReloadableFlag(quic_eliminate_static_stream_map_3)) {
     QuicSessionPeer::RegisterStaticStreamNew(&session_,
                                              std::move(fake_headers_stream));
   } else {
@@ -1289,7 +1289,7 @@
       QuicUtils::GetHeadersStreamId(connection_->transport_version());
   std::unique_ptr<TestStream> fake_headers_stream = QuicMakeUnique<TestStream>(
       headers_stream_id, &session_, /*is_static*/ true, BIDIRECTIONAL);
-  if (GetQuicReloadableFlag(quic_eliminate_static_stream_map_2)) {
+  if (GetQuicReloadableFlag(quic_eliminate_static_stream_map_3)) {
     QuicSessionPeer::RegisterStaticStreamNew(&session_,
                                              std::move(fake_headers_stream));
   } else {
@@ -2428,7 +2428,7 @@
   QuicStreamId stream_id = 0;
   std::unique_ptr<TestStream> fake_static_stream = QuicMakeUnique<TestStream>(
       stream_id, &session_, /*is_static*/ true, BIDIRECTIONAL);
-  if (GetQuicReloadableFlag(quic_eliminate_static_stream_map_2)) {
+  if (GetQuicReloadableFlag(quic_eliminate_static_stream_map_3)) {
     QuicSessionPeer::RegisterStaticStreamNew(&session_,
                                              std::move(fake_static_stream));
   } else {