Add a regression test of b/168294218, also verified gfe2_reloadable_flag_quic_fix_missing_initial_keys fixes the issue. PiperOrigin-RevId: 333599100 Change-Id: Ib5e9b08ff2db5a396f519ad075bb1527146693d7
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc index 4500a5b..c26cdc8 100644 --- a/quic/core/quic_connection_test.cc +++ b/quic/core/quic_connection_test.cc
@@ -1349,6 +1349,70 @@ return encrypted_length; } + struct PacketInfo { + PacketInfo(uint64_t packet_number, QuicFrames frames, EncryptionLevel level) + : packet_number(packet_number), frames(frames), level(level) {} + + uint64_t packet_number; + QuicFrames frames; + EncryptionLevel level; + }; + + size_t ProcessCoalescedPacket(std::vector<PacketInfo> packets) { + char coalesced_buffer[kMaxOutgoingPacketSize]; + size_t coalesced_size = 0; + bool contains_initial = false; + for (const auto& packet : packets) { + QuicPacketHeader header = + ConstructPacketHeader(packet.packet_number, packet.level); + // Set the correct encryption level and encrypter on peer_creator and + // peer_framer, respectively. + peer_creator_.set_encryption_level(packet.level); + if (packet.level == ENCRYPTION_INITIAL) { + contains_initial = true; + } + if (QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_) > + ENCRYPTION_INITIAL) { + peer_framer_.SetEncrypter( + QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), + std::make_unique<TaggingEncrypter>(0x01)); + // Set the corresponding decrypter. + if (connection_.version().KnowsWhichDecrypterToUse()) { + connection_.InstallDecrypter( + QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), + std::make_unique<StrictTaggingDecrypter>(0x01)); + } else { + connection_.SetDecrypter( + QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), + std::make_unique<StrictTaggingDecrypter>(0x01)); + } + } + std::unique_ptr<QuicPacket> constructed_packet( + ConstructPacket(header, packet.frames)); + + char buffer[kMaxOutgoingPacketSize]; + size_t encrypted_length = peer_framer_.EncryptPayload( + packet.level, QuicPacketNumber(packet.packet_number), + *constructed_packet, buffer, kMaxOutgoingPacketSize); + DCHECK_LE(coalesced_size + encrypted_length, kMaxOutgoingPacketSize); + memcpy(coalesced_buffer + coalesced_size, buffer, encrypted_length); + coalesced_size += encrypted_length; + } + if (contains_initial) { + // Padded coalesced packet to full if it contains initial packet. + memset(coalesced_buffer + coalesced_size, '0', + kMaxOutgoingPacketSize - coalesced_size); + } + connection_.ProcessUdpPacket( + kSelfAddress, kPeerAddress, + QuicReceivedPacket(coalesced_buffer, coalesced_size, clock_.Now(), + false)); + if (connection_.GetSendAlarm()->IsSet()) { + connection_.GetSendAlarm()->Fire(); + } + return coalesced_size; + } + size_t ProcessDataPacket(uint64_t number) { return ProcessDataPacketAtLevel(number, false, ENCRYPTION_FORWARD_SECURE); } @@ -12045,6 +12109,71 @@ EXPECT_TRUE(connection_.connected()); } +// Regresstion test for b/168294218 +TEST_P(QuicConnectionTest, ZeroRttRejectionAndMissingInitialKeys) { + if (!connection_.SupportsMultiplePacketNumberSpaces() || + !GetQuicReloadableFlag(quic_fix_missing_initial_keys)) { + return; + } + // Not defer send in response to packet. + connection_.set_defer_send_in_response_to_packets(false); + EXPECT_CALL(visitor_, OnHandshakePacketSent()).WillOnce(Invoke([this]() { + connection_.RemoveEncrypter(ENCRYPTION_INITIAL); + connection_.NeuterUnencryptedPackets(); + })); + EXPECT_CALL(visitor_, OnCryptoFrame(_)) + .WillRepeatedly(Invoke([=](const QuicCryptoFrame& frame) { + if (frame.level == ENCRYPTION_HANDSHAKE) { + // 0-RTT gets rejected. + connection_.MarkZeroRttPacketsForRetransmission(0); + // Send Crypto data. + connection_.SetEncrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<TaggingEncrypter>(0x03)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); + connection_.SendCryptoStreamData(); + connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<TaggingEncrypter>(0x04)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + // Retransmit rejected 0-RTT packets. + connection_.OnCanWrite(); + // Advance INITIAL ack delay to trigger initial ACK to be sent AFTER + // the retransmission of rejected 0-RTT packets while the HANDSHAKE + // packet is still in the coalescer, such that the INITIAL key gets + // dropped between SendAllPendingAcks and actually send the ack frame, + // bummer. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1)); + } + })); + use_tagging_decrypter(); + connection_.SetEncrypter(ENCRYPTION_INITIAL, + std::make_unique<TaggingEncrypter>(0x01)); + connection_.SendCryptoStreamData(); + // Send 0-RTT packet. + connection_.SetEncrypter(ENCRYPTION_ZERO_RTT, + std::make_unique<TaggingEncrypter>(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + connection_.SendStreamDataWithString(2, "foo", 0, NO_FIN); + + QuicAckFrame frame1 = InitAckFrame(1); + // Received ACK for packet 1. + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); + ProcessFramePacketAtLevel(1, QuicFrame(&frame1), ENCRYPTION_INITIAL); + EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); + + // Fire retransmission alarm. + EXPECT_CALL(visitor_, SendPing()).WillOnce(Invoke([this]() { SendPing(); })); + connection_.GetRetransmissionAlarm()->Fire(); + + QuicFrames frames1; + frames1.push_back(QuicFrame(&crypto_frame_)); + QuicFrames frames2; + QuicCryptoFrame crypto_frame(ENCRYPTION_HANDSHAKE, 0, + quiche::QuicheStringPiece(data1)); + frames2.push_back(QuicFrame(&crypto_frame)); + ProcessCoalescedPacket( + {{2, frames1, ENCRYPTION_INITIAL}, {3, frames2, ENCRYPTION_HANDSHAKE}}); +} + } // namespace } // namespace test } // namespace quic