Reset stream if stream frame contains data that goes beyond stream's close offset.
This fixes http://crbug/1002119
gfe-relnote: protected by gfe2_reloadable_flag_quic_rst_if_stream_frame_beyond_close_offset.
PiperOrigin-RevId: 268732204
Change-Id: I66f845e320de89e8634b1bfb0725f599fc43dfd6
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index 1be1786..e69cd60 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -29,6 +29,7 @@
RETURN_STRING_LITERAL(QUIC_INVALID_PROMISE_METHOD);
RETURN_STRING_LITERAL(QUIC_PUSH_STREAM_TIMED_OUT);
RETURN_STRING_LITERAL(QUIC_HEADERS_TOO_LARGE);
+ RETURN_STRING_LITERAL(QUIC_DATA_AFTER_CLOSE_OFFSET);
RETURN_STRING_LITERAL(QUIC_STREAM_TTL_EXPIRED);
}
// Return a default value so that we return this when |error| doesn't match
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index e07094c..298029f 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -54,6 +54,8 @@
QUIC_HEADERS_TOO_LARGE,
// The data is not likely arrive in time.
QUIC_STREAM_TTL_EXPIRED,
+ // The stream received data that goes beyond its close offset.
+ QUIC_DATA_AFTER_CLOSE_OFFSET,
// No error. Used as bound while iterating.
QUIC_STREAM_LAST_ERROR,
};
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index a75d948..90c7e83 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -2717,6 +2717,25 @@
EXPECT_FALSE(session_.WillingAndAbleToWrite());
}
+// Regression test for
+// https://bugs.chromium.org/p/chromium/issues/detail?id=1002119
+TEST_P(QuicSessionTestServer, StreamFrameReceivedAfterFin) {
+ TestStream* stream = session_.CreateOutgoingBidirectionalStream();
+ QuicStreamFrame frame(stream->id(), true, 0, ",");
+ session_.OnStreamFrame(frame);
+
+ QuicStreamFrame frame1(stream->id(), false, 1, ",");
+ if (GetQuicReloadableFlag(quic_rst_if_stream_frame_beyond_close_offset)) {
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream->id(), QUIC_DATA_AFTER_CLOSE_OFFSET));
+ session_.OnStreamFrame(frame1);
+ EXPECT_TRUE(connection_->connected());
+ } else {
+ EXPECT_DEBUG_DEATH(session_.OnStreamFrame(frame1), "Check failed");
+ }
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index 43b8db8..ae302b9 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -6,6 +6,7 @@
#include <string>
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
#include "net/third_party/quiche/src/quic/core/quic_flow_controller.h"
#include "net/third_party/quiche/src/quic/core/quic_session.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
@@ -163,6 +164,12 @@
return;
}
+ if (GetQuicReloadableFlag(quic_rst_if_stream_frame_beyond_close_offset) &&
+ frame.offset + frame.data_length > sequencer_.close_offset()) {
+ Reset(QUIC_DATA_AFTER_CLOSE_OFFSET);
+ return;
+ }
+
if (frame.fin) {
fin_received_ = true;
}
@@ -387,6 +394,15 @@
frame.data_length, ". ", sequencer_.DebugString()));
return;
}
+
+ if (GetQuicReloadableFlag(quic_rst_if_stream_frame_beyond_close_offset)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_rst_if_stream_frame_beyond_close_offset);
+ if (frame.offset + frame.data_length > sequencer_.close_offset()) {
+ Reset(QUIC_DATA_AFTER_CLOSE_OFFSET);
+ return;
+ }
+ }
+
if (frame.fin) {
fin_received_ = true;
if (fin_sent_) {
diff --git a/quic/core/quic_stream_sequencer.cc b/quic/core/quic_stream_sequencer.cc
index dc7b0cd..8f84fb9 100644
--- a/quic/core/quic_stream_sequencer.cc
+++ b/quic/core/quic_stream_sequencer.cc
@@ -38,6 +38,7 @@
QuicStreamSequencer::~QuicStreamSequencer() {}
void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
+ DCHECK_LE(frame.offset + frame.data_length, close_offset_);
++num_frames_received_;
const QuicStreamOffset byte_offset = frame.offset;
const size_t data_len = frame.data_length;
diff --git a/quic/core/quic_stream_sequencer_test.cc b/quic/core/quic_stream_sequencer_test.cc
index b57fa6e..8ad845b 100644
--- a/quic/core/quic_stream_sequencer_test.cc
+++ b/quic/core/quic_stream_sequencer_test.cc
@@ -370,15 +370,11 @@
EXPECT_TRUE(sequencer_->IsClosed());
}
-TEST_F(QuicStreamSequencerTest, MutipleOffsets) {
+TEST_F(QuicStreamSequencerTest, MultipleOffsets) {
OnFinFrame(3, "");
EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS));
- OnFinFrame(5, "");
- EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
-
- EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS));
OnFinFrame(1, "");
EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
@@ -761,10 +757,9 @@
// Regression test for https://crbug.com/992486.
TEST_F(QuicStreamSequencerTest, CorruptFinFrames) {
SetQuicReloadableFlag(quic_no_stream_data_after_reset, true);
- EXPECT_CALL(stream_, OnDataAvailable()).Times(1);
EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS));
- OnFinFrame(0u, "");
+ OnFinFrame(2u, "");
OnFinFrame(0u, "a");
EXPECT_FALSE(sequencer_->HasBytesToRead());
}