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