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)}}; }
diff --git a/quic/test_tools/quic_test_client.cc b/quic/test_tools/quic_test_client.cc index 237d9f6..ca5e95a 100644 --- a/quic/test_tools/quic_test_client.cc +++ b/quic/test_tools/quic_test_client.cc
@@ -382,7 +382,9 @@ QuicStreamId stream_id = GetNthClientInitiatedBidirectionalStreamId( session->transport_version(), 0); - session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0); + QuicStream* stream = session->GetOrCreateStream(stream_id); + session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, + stream->stream_bytes_written()); return ret; }