Fix coalesced packet processing
Make sure we correctly process all coalesced packets without modifying the collection while we're iterating on it.
This issue was found by clusterfuzz:
https://bugs.chromium.org/p/chromium/issues/detail?id=990001
I've confirmed that the new test fails with the old code and passes with the fix, and that the fuzzer no longer crashes.
gfe-relnote: fix coalesced packet processing, protected by disabled v99 flag
PiperOrigin-RevId: 261433275
Change-Id: Iea1edf70fc84873fc7fe2f05c749759b2c5a6c9b
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 0b648e2..0c33604 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -2620,15 +2620,12 @@
void QuicConnection::MaybeProcessCoalescedPackets() {
bool processed = false;
- for (const auto& packet : coalesced_packets_) {
- if (!connected_) {
- return;
- }
+ while (connected_ && !coalesced_packets_.empty()) {
+ std::unique_ptr<QuicEncryptedPacket> packet =
+ std::move(coalesced_packets_.front());
+ coalesced_packets_.pop_front();
- // }
- // while (connected_ && !coalesced_packets_.empty()) {
QUIC_DVLOG(1) << ENDPOINT << "Processing coalesced packet";
- // QuicEncryptedPacket* packet = coalesced_packets_.front().get();
if (framer_.ProcessPacket(*packet)) {
processed = true;
} else {
@@ -2644,9 +2641,7 @@
}
}
}
- // coalesced_packets_.pop_front();
}
- coalesced_packets_.clear();
if (processed) {
MaybeProcessUndecryptablePackets();
}
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index a3ab06f..773db66 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -8562,6 +8562,53 @@
EXPECT_FALSE(ack_alarm->IsSet());
}
+// Verify that a packet containing three coalesced packets is parsed correctly.
+TEST_P(QuicConnectionTest, CoalescedPacket) {
+ if (!QuicVersionHasLongHeaderLengths(connection_.transport_version())) {
+ // Coalesced packets can only be encoded using long header lengths.
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_TRUE(connection_.connected());
+ if (QuicVersionUsesCryptoFrames(connection_.transport_version())) {
+ EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(3);
+ } else {
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(3);
+ }
+
+ uint64_t packet_numbers[3] = {1, 2, 3};
+ EncryptionLevel encryption_levels[3] = {
+ ENCRYPTION_INITIAL, ENCRYPTION_INITIAL, ENCRYPTION_FORWARD_SECURE};
+ char buffer[kMaxOutgoingPacketSize] = {};
+ size_t total_encrypted_length = 0;
+ for (int i = 0; i < 3; i++) {
+ QuicPacketHeader header =
+ ConstructPacketHeader(packet_numbers[i], encryption_levels[i]);
+ QuicFrames frames;
+ if (QuicVersionUsesCryptoFrames(connection_.transport_version())) {
+ frames.push_back(QuicFrame(&crypto_frame_));
+ } else {
+ frames.push_back(QuicFrame(frame1_));
+ }
+ std::unique_ptr<QuicPacket> packet = ConstructPacket(header, frames);
+ peer_creator_.set_encryption_level(encryption_levels[i]);
+ size_t encrypted_length = peer_framer_.EncryptPayload(
+ encryption_levels[i], QuicPacketNumber(packet_numbers[i]), *packet,
+ buffer + total_encrypted_length,
+ sizeof(buffer) - total_encrypted_length);
+ EXPECT_GT(encrypted_length, 0u);
+ total_encrypted_length += encrypted_length;
+ }
+ connection_.ProcessUdpPacket(
+ kSelfAddress, kPeerAddress,
+ QuicReceivedPacket(buffer, total_encrypted_length, clock_.Now(), false));
+ if (connection_.GetSendAlarm()->IsSet()) {
+ connection_.GetSendAlarm()->Fire();
+ }
+
+ EXPECT_TRUE(connection_.connected());
+}
+
} // namespace
} // namespace test
} // namespace quic