Close connection when stream receives wrong data regarding close offset.
gfe-relnote: protected by gfe2_reloadable_flag_quic_close_connection_on_wrong_offset.
PiperOrigin-RevId: 276352453
Change-Id: Ia4174e54d01a2d8c2aa10182940c1093c1a16ad8
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index a603bc1..ae17f09 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -163,6 +163,8 @@
RETURN_STRING_LITERAL(QUIC_QPACK_DECOMPRESSION_FAILED);
RETURN_STRING_LITERAL(QUIC_QPACK_ENCODER_STREAM_ERROR);
RETURN_STRING_LITERAL(QUIC_QPACK_DECODER_STREAM_ERROR);
+ RETURN_STRING_LITERAL(QUIC_STREAM_DATA_BEYOND_CLOSE_OFFSET);
+ RETURN_STRING_LITERAL(QUIC_STREAM_MULTIPLE_OFFSET);
RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
// Intentionally have no default case, so we'll break the build
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index 6008ab6..b6c2d50 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -347,8 +347,14 @@
QUIC_QPACK_ENCODER_STREAM_ERROR = 127,
QUIC_QPACK_DECODER_STREAM_ERROR = 128,
+ // Received stream data beyond close offset.
+ QUIC_STREAM_DATA_BEYOND_CLOSE_OFFSET = 129,
+
+ // Received multiple close offset.
+ QUIC_STREAM_MULTIPLE_OFFSET = 130,
+
// No error. Used as bound while iterating.
- QUIC_LAST_ERROR = 129,
+ QUIC_LAST_ERROR = 131,
};
// QuicErrorCodes is encoded as four octets on-the-wire when doing Google QUIC,
// or a varint62 when doing IETF QUIC. Ensure that its value does not exceed
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index d128f0d..845277b 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -2746,11 +2746,15 @@
session_.OnStreamFrame(frame);
QuicStreamFrame frame1(stream->id(), false, 1, ",");
- EXPECT_CALL(*connection_, SendControlFrame(_));
- EXPECT_CALL(*connection_,
- OnStreamReset(stream->id(), QUIC_DATA_AFTER_CLOSE_OFFSET));
+ if (!GetQuicReloadableFlag(quic_close_connection_on_wrong_offset)) {
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ EXPECT_CALL(*connection_,
+ OnStreamReset(stream->id(), QUIC_DATA_AFTER_CLOSE_OFFSET));
+ } else {
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_STREAM_DATA_BEYOND_CLOSE_OFFSET, _, _));
+ }
session_.OnStreamFrame(frame1);
- EXPECT_TRUE(connection_->connected());
}
// A client test class that can be used when the automatic configuration is not
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index 4b70bd4..aced2b9 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -9,6 +9,7 @@
#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_types.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
@@ -172,7 +173,12 @@
}
if (frame.offset + frame.data_length > sequencer_.close_offset()) {
- Reset(QUIC_DATA_AFTER_CLOSE_OFFSET);
+ CloseConnectionWithDetails(
+ QUIC_STREAM_DATA_BEYOND_CLOSE_OFFSET,
+ QuicStrCat(
+ "Stream ", id_,
+ " received data with offset: ", frame.offset + frame.data_length,
+ ", which is beyond close offset: ", sequencer()->close_offset()));
return;
}
@@ -211,6 +217,20 @@
"Reset frame stream offset overflow.");
return;
}
+
+ const QuicStreamOffset kMaxOffset =
+ std::numeric_limits<QuicStreamOffset>::max();
+ if (sequencer()->close_offset() != kMaxOffset &&
+ frame.byte_offset != sequencer()->close_offset()) {
+ CloseConnectionWithDetails(
+ QUIC_STREAM_MULTIPLE_OFFSET,
+ QuicStrCat("Stream ", id_,
+ " received new final offset: ", frame.byte_offset,
+ ", which is different from close offset: ",
+ sequencer()->close_offset()));
+ return;
+ }
+
MaybeIncreaseHighestReceivedOffset(frame.byte_offset);
if (flow_controller_.FlowControlViolation() ||
connection_flow_controller_->FlowControlViolation()) {
@@ -402,7 +422,17 @@
}
if (frame.offset + frame.data_length > sequencer_.close_offset()) {
- Reset(QUIC_DATA_AFTER_CLOSE_OFFSET);
+ if (!GetQuicReloadableFlag(quic_close_connection_on_wrong_offset)) {
+ Reset(QUIC_DATA_AFTER_CLOSE_OFFSET);
+ return;
+ }
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_close_connection_on_wrong_offset, 1, 2);
+ CloseConnectionWithDetails(
+ QUIC_STREAM_DATA_BEYOND_CLOSE_OFFSET,
+ QuicStrCat(
+ "Stream ", id_,
+ " received data with offset: ", frame.offset + frame.data_length,
+ ", which is beyond close offset: ", sequencer_.close_offset()));
return;
}
@@ -459,6 +489,23 @@
"Reset frame stream offset overflow.");
return;
}
+
+ if (GetQuicReloadableFlag(quic_close_connection_on_wrong_offset)) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_close_connection_on_wrong_offset, 2, 2);
+ const QuicStreamOffset kMaxOffset =
+ std::numeric_limits<QuicStreamOffset>::max();
+ if (sequencer()->close_offset() != kMaxOffset &&
+ frame.byte_offset != sequencer()->close_offset()) {
+ CloseConnectionWithDetails(
+ QUIC_STREAM_MULTIPLE_OFFSET,
+ QuicStrCat("Stream ", id_,
+ " received new final offset: ", frame.byte_offset,
+ ", which is different from close offset: ",
+ sequencer_.close_offset()));
+ return;
+ }
+ }
+
MaybeIncreaseHighestReceivedOffset(frame.byte_offset);
if (flow_controller_->FlowControlViolation() ||
connection_flow_controller_->FlowControlViolation()) {
diff --git a/quic/core/quic_stream_test.cc b/quic/core/quic_stream_test.cc
index eaeb630..68be6be 100644
--- a/quic/core/quic_stream_test.cc
+++ b/quic/core/quic_stream_test.cc
@@ -8,8 +8,10 @@
#include <string>
#include <utility>
+#include "net/third_party/quiche/src/quic/core/frames/quic_rst_stream_frame.h"
#include "net/third_party/quiche/src/quic/core/quic_connection.h"
#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
#include "net/third_party/quiche/src/quic/core/quic_types.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
#include "net/third_party/quiche/src/quic/core/quic_versions.h"
@@ -1631,6 +1633,19 @@
stream.OnWindowUpdateFrame(window_update_frame);
}
+TEST_P(QuicStreamTest, RstStreamFrameChangesCloseOffset) {
+ SetQuicReloadableFlag(quic_close_connection_on_wrong_offset, true);
+ Initialize();
+
+ QuicStreamFrame stream_frame(stream_->id(), true, 0, "abc");
+ stream_->OnStreamFrame(stream_frame);
+ QuicRstStreamFrame rst(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, 0u);
+
+ EXPECT_CALL(*connection_, CloseConnection(QUIC_STREAM_MULTIPLE_OFFSET, _, _));
+ stream_->OnStreamReset(rst);
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/quic_types.cc b/quic/core/quic_types.cc
index e6f95cd..c584714 100644
--- a/quic/core/quic_types.cc
+++ b/quic/core/quic_types.cc
@@ -6,6 +6,7 @@
#include <cstdint>
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
namespace quic {
@@ -425,6 +426,11 @@
return {
false,
{static_cast<uint64_t>(IETF_QUIC_HTTP_QPACK_DECODER_STREAM_ERROR)}};
+ case QUIC_STREAM_DATA_BEYOND_CLOSE_OFFSET:
+ return {true,
+ {static_cast<uint64_t>(QUIC_STREAM_DATA_BEYOND_CLOSE_OFFSET)}};
+ case QUIC_STREAM_MULTIPLE_OFFSET:
+ return {true, {static_cast<uint64_t>(QUIC_STREAM_MULTIPLE_OFFSET)}};
case QUIC_LAST_ERROR:
return {false, {static_cast<uint64_t>(QUIC_LAST_ERROR)}};
}