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 {