Use next_connection_id_sequence_number to validate retired CID number as retiring an active CID would not guarantee that a new CID is issued due to CID collision in dispatcher. Default enabled as a bug fix. Protected by FLAGS_quic_reloadable_flag_quic_check_retire_cid_with_next_cid_sequence_number. PiperOrigin-RevId: 487508836
diff --git a/quiche/quic/core/quic_connection_id_manager.cc b/quiche/quic/core/quic_connection_id_manager.cc index a2a3fbf..4776545 100644 --- a/quiche/quic/core/quic_connection_id_manager.cc +++ b/quiche/quic/core/quic_connection_id_manager.cc
@@ -340,9 +340,19 @@ const QuicRetireConnectionIdFrame& frame, QuicTime::Delta pto_delay, std::string* error_detail) { QUICHE_DCHECK(!active_connection_ids_.empty()); - if (frame.sequence_number > active_connection_ids_.back().second) { - *error_detail = "To be retired connecton ID is never issued."; - return IETF_QUIC_PROTOCOL_VIOLATION; + if (GetQuicReloadableFlag( + quic_check_retire_cid_with_next_cid_sequence_number)) { + QUIC_RELOADABLE_FLAG_COUNT( + quic_check_retire_cid_with_next_cid_sequence_number); + if (frame.sequence_number >= next_connection_id_sequence_number_) { + *error_detail = "To be retired connecton ID is never issued."; + return IETF_QUIC_PROTOCOL_VIOLATION; + } + } else { + if (frame.sequence_number > active_connection_ids_.back().second) { + *error_detail = "To be retired connecton ID is never issued."; + return IETF_QUIC_PROTOCOL_VIOLATION; + } } auto it =
diff --git a/quiche/quic/core/quic_connection_id_manager_test.cc b/quiche/quic/core/quic_connection_id_manager_test.cc index 9df262c..2dd35bf 100644 --- a/quiche/quic/core/quic_connection_id_manager_test.cc +++ b/quiche/quic/core/quic_connection_id_manager_test.cc
@@ -6,8 +6,10 @@ #include <cstddef> +#include "quiche/quic/core/frames/quic_retire_connection_id_frame.h" #include "quiche/quic/core/quic_connection_id.h" #include "quiche/quic/core/quic_error_codes.h" +#include "quiche/quic/platform/api/quic_flags.h" #include "quiche/quic/platform/api/quic_test.h" #include "quiche/quic/test_tools/mock_clock.h" #include "quiche/quic/test_tools/mock_connection_id_generator.h" @@ -1028,5 +1030,45 @@ cid_manager_.MaybeSendNewConnectionIds(); } +// Regression test for b/258450534 +TEST_F(QuicSelfIssuedConnectionIdManagerTest, + RetireConnectionIdAfterConnectionIdCollisionIsFine) { + SetQuicReloadableFlag(quic_check_cid_collision_when_issue_new_cid, true); + QuicConnectionId cid0 = initial_connection_id_; + QuicConnectionId cid1 = CheckGenerate(cid0); + EXPECT_CALL(cid_manager_visitor_, MaybeReserveConnectionId(cid1)) + .WillOnce(Return(true)); + EXPECT_CALL(cid_manager_visitor_, SendNewConnectionId(_)) + .WillOnce(Return(true)); + cid_manager_.MaybeSendNewConnectionIds(); + + QuicRetireConnectionIdFrame retire_cid_frame(/*control_frame_id=*/0, + /*sequence_number=*/1); + QuicConnectionId cid2 = CheckGenerate(cid1); + // This happens when cid2 is aleady present in the dispatcher map. + EXPECT_CALL(cid_manager_visitor_, MaybeReserveConnectionId(cid2)) + .WillOnce(Return(false)); + std::string error_details; + EXPECT_EQ( + cid_manager_.OnRetireConnectionIdFrame( + retire_cid_frame, QuicTime::Delta::FromSeconds(1), &error_details), + QUIC_NO_ERROR) + << error_details; + + if (GetQuicReloadableFlag( + quic_check_retire_cid_with_next_cid_sequence_number)) { + EXPECT_EQ( + cid_manager_.OnRetireConnectionIdFrame( + retire_cid_frame, QuicTime::Delta::FromSeconds(1), &error_details), + QUIC_NO_ERROR) + << error_details; + } else { + EXPECT_EQ( + cid_manager_.OnRetireConnectionIdFrame( + retire_cid_frame, QuicTime::Delta::FromSeconds(1), &error_details), + IETF_QUIC_PROTOCOL_VIOLATION); + } +} + } // namespace } // namespace quic::test
diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h index 88cd4a0..7f855cb 100644 --- a/quiche/quic/core/quic_flags_list.h +++ b/quiche/quic/core/quic_flags_list.h
@@ -85,6 +85,8 @@ QUIC_FLAG(quic_reloadable_flag_quic_use_ping_manager2, true) // If true, use new connection ID in connection migration. QUIC_FLAG(quic_reloadable_flag_quic_connection_migration_use_new_cid_v2, true) +// If true, use next_connection_id_sequence_number to validate retired cid number. +QUIC_FLAG(quic_reloadable_flag_quic_check_retire_cid_with_next_cid_sequence_number, true) // If true, uses conservative cwnd gain and pacing gain when cwnd gets bootstrapped. QUIC_FLAG(quic_reloadable_flag_quic_conservative_cwnd_and_pacing_gains, false) // Instead of assuming an incoming connection ID length for short headers, ask each time.