Clear QuicPacketCreator::packet_.has_scone_packet. This completes the fix for b/548012868 by covering the case where the server INITIAL is lost in flight. Not clearing this bit causes a state mismatch when serializing a coalesced packet; an INITIAL is serialized without SCONE at first and then reserialized with SCONE, causing one of several QUIC_BUGs that generally involve failed serialization. has_scone_packet is always false in production, so forcing it false here has no production effect. PiperOrigin-RevId: 970555472
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc index 6e3b1c2..6cf05b7 100644 --- a/quiche/quic/core/http/end_to_end_test.cc +++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -5059,6 +5059,46 @@ EXPECT_NE(client_session->received_bandwidth(), QuicBandwidth::Zero()); } +// Repro for the other part of b/548012868. Without a fix to the +// QuicPacketCreator logic, this test will crash when transmitting an INITIAL +// that had SCONE. +TEST_P(EndToEndTest, SconeProtocolServerToClientAsynchronousWithPacketLoss) { + if (!version_.IsIetfQuic() || override_server_connection_id_length_ != 16) { + // Because the server in this test suite uses + // DeterministicConnectionIdGenerator, an 8-byte connection ID is unchanged, + // meaning it will not be rejected by the client. + ASSERT_TRUE(Initialize()); + return; + } + client_config_.set_parse_scone_packets(true); + server_config_.set_scone_packet_interval(QuicTime::Delta::FromSeconds(20)); + + // Build a cert chain with 8 certs, so that the HANDSHAKE messages fill + // several packets. + num_certs_in_chain_ = 8; + AsyncCryptoStreamFactory async_crypto_stream_factory(&server_thread_); + async_crypto_stream_factory_ = &async_crypto_stream_factory; + + delete server_writer_; + server_writer_ = new SconePacketWriter(); + delete client_writer_; + client_writer_ = new SconePacketWriter(); + // Drop the first server packet. + absl::down_cast<SconePacketWriter*>(server_writer_) + ->set_fake_drop_first_n_packets(1); + + ASSERT_TRUE(Initialize()); + EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable()); + QuicTestClientSession* client_session = + absl::down_cast<QuicTestClientSession*>(client_->client()->session()); + ASSERT_NE(client_session, nullptr); + + EXPECT_TRUE( + absl::down_cast<SconePacketWriter*>(client_writer_)->SawSconeIndicator()); + // There should be no bandwidth report, because the SCONE packet was lost. + EXPECT_EQ(client_session->received_bandwidth(), QuicBandwidth::Zero()); +} + TEST_P(EndToEndTest, VersionNegotiationDowngradeAttackIsDetected) { ResetClientWriterForVersionNegotiationTest(); ParsedQuicVersion target_version = server_supported_versions_.back();
diff --git a/quiche/quic/core/quic_packet_creator.cc b/quiche/quic/core/quic_packet_creator.cc index 7553054..ec31f43 100644 --- a/quiche/quic/core/quic_packet_creator.cc +++ b/quiche/quic/core/quic_packet_creator.cc
@@ -509,6 +509,7 @@ needs_full_padding_ = false; packet_.bytes_not_retransmitted.reset(); packet_.initial_header.reset(); + packet_.has_scone_packet = false; } size_t QuicPacketCreator::ReserializeInitialPacketInCoalescedPacket(
diff --git a/quiche/quic/core/quic_packet_creator_test.cc b/quiche/quic/core/quic_packet_creator_test.cc index 8338cf0..59e842a 100644 --- a/quiche/quic/core/quic_packet_creator_test.cc +++ b/quiche/quic/core/quic_packet_creator_test.cc
@@ -1903,6 +1903,18 @@ SerializeAllFrames(frames_); } +// Regression test for b/548012868. +TEST_P(QuicPacketCreatorTest, FlushClearsScone) { + creator_.PrependSconePacket(); + creator_.AddFrame(QuicFrame(QuicPingFrame()), NOT_RETRANSMISSION); + EXPECT_CALL(delegate_, OnSerializedPacket) + .WillOnce([](SerializedPacket packet) { + EXPECT_TRUE(packet.has_scone_packet); + }); + creator_.FlushCurrentPacket(); + EXPECT_FALSE(QuicPacketCreatorPeer::HasSconePacket(&creator_)); +} + TEST_P(QuicPacketCreatorTest, AddDatagramFrame) { if (client_framer_.version().IsIetfQuic()) { creator_.SetMaxDatagramFrameSize(kMaxAcceptedDatagramFrameSize);
diff --git a/quiche/quic/test_tools/quic_packet_creator_peer.cc b/quiche/quic/test_tools/quic_packet_creator_peer.cc index 40599b1..699d6db 100644 --- a/quiche/quic/test_tools/quic_packet_creator_peer.cc +++ b/quiche/quic/test_tools/quic_packet_creator_peer.cc
@@ -160,5 +160,10 @@ return creator->RemoveSoftMaxPacketLength(); } +// static +bool QuicPacketCreatorPeer::HasSconePacket(QuicPacketCreator* creator) { + return creator->packet_.has_scone_packet; +} + } // namespace test } // namespace quic
diff --git a/quiche/quic/test_tools/quic_packet_creator_peer.h b/quiche/quic/test_tools/quic_packet_creator_peer.h index 8fa9226..c2de665 100644 --- a/quiche/quic/test_tools/quic_packet_creator_peer.h +++ b/quiche/quic/test_tools/quic_packet_creator_peer.h
@@ -57,6 +57,7 @@ static void SetRandom(QuicPacketCreator* creator, QuicRandom* random); static bool WillAttachSconeIndicator(const QuicPacketCreator& creator); static bool RemoveSoftMaxPacketLength(QuicPacketCreator* creator); + static bool HasSconePacket(QuicPacketCreator* creator); }; } // namespace test