Determine stateless reset packet length by received packet length.

Protected by FLAGS_quic_reloadable_flag_quic_fix_stateless_reset.

PiperOrigin-RevId: 363022883
Change-Id: I37c39acab6d13ea9b4c8045ab58adb15e37476a4
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index de43714..936aa9f 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -3273,8 +3273,8 @@
                     Perspective::IS_SERVER, kQuicDefaultConnectionIdLength);
   std::unique_ptr<QuicEncryptedPacket> packet;
   if (version_.HasIetfInvariantHeader()) {
-    packet = framer.BuildIetfStatelessResetPacket(connection_id,
-                                                  stateless_reset_token);
+    packet = framer.BuildIetfStatelessResetPacket(
+        connection_id, /*received_packet_length=*/100, stateless_reset_token);
   } else {
     packet = framer.BuildPublicResetPacket(header);
   }
@@ -3317,8 +3317,9 @@
   NiceMock<MockQuicConnectionDebugVisitor> visitor;
   client_connection->set_debug_visitor(&visitor);
   if (version_.HasIetfInvariantHeader()) {
-    packet = framer.BuildIetfStatelessResetPacket(incorrect_connection_id,
-                                                  stateless_reset_token);
+    packet = framer.BuildIetfStatelessResetPacket(
+        incorrect_connection_id, /*received_packet_length=*/100,
+        stateless_reset_token);
     EXPECT_CALL(visitor, OnIncorrectConnectionId(incorrect_connection_id))
         .Times(0);
   } else {
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index aff4029..b797da8 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -6893,6 +6893,7 @@
   connection_.SetFromConfig(config);
   std::unique_ptr<QuicEncryptedPacket> packet(
       QuicFramer::BuildIetfStatelessResetPacket(connection_id_,
+                                                /*received_packet_length=*/100,
                                                 kTestStatelessResetToken));
   std::unique_ptr<QuicReceivedPacket> received(
       ConstructReceivedPacket(*packet, QuicTime::Zero()));
@@ -11859,6 +11860,7 @@
 
   std::unique_ptr<QuicEncryptedPacket> packet(
       QuicFramer::BuildIetfStatelessResetPacket(connection_id_,
+                                                /*received_packet_length=*/100,
                                                 kTestStatelessResetToken));
   std::unique_ptr<QuicReceivedPacket> received(
       ConstructReceivedPacket(*packet, QuicTime::Zero()));
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index ed2b8eb..3c18b00 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -609,7 +609,7 @@
     time_wait_list_manager_->ProcessPacket(
         packet_info.self_address, packet_info.peer_address,
         packet_info.destination_connection_id, packet_info.form,
-        GetPerPacketContext());
+        packet_info.packet.length(), GetPerPacketContext());
     return true;
   }
 
@@ -630,7 +630,7 @@
     time_wait_list_manager()->ProcessPacket(
         packet_info.self_address, packet_info.peer_address,
         packet_info.destination_connection_id, packet_info.form,
-        GetPerPacketContext());
+        packet_info.packet.length(), GetPerPacketContext());
     OnNewConnectionRejected();
     return true;
   }
@@ -760,7 +760,8 @@
           server_connection_id));
       time_wait_list_manager_->ProcessPacket(
           packet_info->self_address, packet_info->peer_address,
-          server_connection_id, packet_info->form, GetPerPacketContext());
+          server_connection_id, packet_info->form, packet_info->packet.length(),
+          GetPerPacketContext());
 
       buffered_packets_.DiscardPackets(server_connection_id);
       break;
@@ -1420,21 +1421,32 @@
 void QuicDispatcher::MaybeResetPacketsWithNoVersion(
     const ReceivedPacketInfo& packet_info) {
   QUICHE_DCHECK(!packet_info.version_flag);
-  const size_t MinValidPacketLength =
-      kPacketHeaderTypeSize + expected_server_connection_id_length_ +
-      PACKET_1BYTE_PACKET_NUMBER + /*payload size=*/1 + /*tag size=*/12;
-  if (packet_info.packet.length() < MinValidPacketLength) {
-    // The packet size is too small.
-    QUIC_CODE_COUNT(drop_too_small_packets);
-    return;
+  if (GetQuicReloadableFlag(quic_fix_stateless_reset) &&
+      packet_info.form != GOOGLE_QUIC_PACKET) {
+    // Drop IETF packets smaller than the minimal stateless reset length.
+    if (packet_info.packet.length() <=
+        QuicFramer::GetMinStatelessResetPacketLength()) {
+      QUIC_CODE_COUNT(quic_drop_too_small_short_header_packets);
+      return;
+    }
+  } else {
+    const size_t MinValidPacketLength =
+        kPacketHeaderTypeSize + expected_server_connection_id_length_ +
+        PACKET_1BYTE_PACKET_NUMBER + /*payload size=*/1 + /*tag size=*/12;
+    if (packet_info.packet.length() < MinValidPacketLength) {
+      // The packet size is too small.
+      QUIC_CODE_COUNT(drop_too_small_packets);
+      return;
+    }
+    // TODO(fayang): Consider rate limiting reset packets if reset packet size >
+    // packet_length.
   }
-  // TODO(fayang): Consider rate limiting reset packets if reset packet size >
-  // packet_length.
 
   time_wait_list_manager()->SendPublicReset(
       packet_info.self_address, packet_info.peer_address,
       packet_info.destination_connection_id,
-      packet_info.form != GOOGLE_QUIC_PACKET, GetPerPacketContext());
+      packet_info.form != GOOGLE_QUIC_PACKET, packet_info.packet.length(),
+      GetPerPacketContext());
 }
 
 size_t QuicDispatcher::NumSessions() const {
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc
index 3f2f8cc..da4241b 100644
--- a/quic/core/quic_dispatcher_test.cc
+++ b/quic/core/quic_dispatcher_test.cc
@@ -891,7 +891,7 @@
   // Dispatcher forwards subsequent packets for this connection_id to the time
   // wait list manager.
   EXPECT_CALL(*time_wait_list_manager_,
-              ProcessPacket(_, _, connection_id, _, _))
+              ProcessPacket(_, _, connection_id, _, _, _))
       .Times(1);
   EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
       .Times(0);
@@ -907,11 +907,11 @@
   // list manager.
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
   EXPECT_CALL(*time_wait_list_manager_,
-              ProcessPacket(_, _, connection_id, _, _))
+              ProcessPacket(_, _, connection_id, _, _, _))
       .Times(0);
   EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
       .Times(0);
-  EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _))
+  EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _))
       .Times(1);
   ProcessPacket(client_address, connection_id, /*has_version_flag=*/false,
                 "data");
@@ -922,19 +922,20 @@
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
   CreateTimeWaitListManager();
 
-  char short_packet[22] = {0x70, 0xa7, 0x02, 0x6b};
-  QuicReceivedPacket packet(short_packet, 22, QuicTime::Zero());
+  char short_packet[21] = {0x70, 0xa7, 0x02, 0x6b};
+  QuicReceivedPacket packet(short_packet, 21, QuicTime::Zero());
   char valid_size_packet[23] = {0x70, 0xa7, 0x02, 0x6c};
   QuicReceivedPacket packet2(valid_size_packet, 23, QuicTime::Zero());
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
-  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0);
+  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _))
+      .Times(0);
   EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
       .Times(0);
   // Verify small packet is silently dropped.
-  EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _))
+  EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _))
       .Times(0);
   dispatcher_->ProcessPacket(server_address_, client_address, packet);
-  EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _))
+  EXPECT_CALL(*time_wait_list_manager_, SendPublicReset(_, _, _, _, _, _))
       .Times(1);
   dispatcher_->ProcessPacket(server_address_, client_address, packet2);
 }
@@ -1073,7 +1074,8 @@
   EXPECT_CALL(*dispatcher_,
               CreateQuicSession(TestConnectionId(1), _, client_address, _, _))
       .Times(0);
-  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0);
+  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _))
+      .Times(0);
   EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
       .Times(0);
   ProcessPacket(client_address, TestConnectionId(1), /*has_version_flag=*/true,
@@ -1091,7 +1093,8 @@
 
   // dispatcher_ should drop this packet.
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
-  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0);
+  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _))
+      .Times(0);
   EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
       .Times(0);
   ProcessFirstFlight(client_address, EmptyQuicConnectionId());
@@ -1655,7 +1658,8 @@
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
   QuicConnectionId connection_id = TestConnectionId(1);
   EXPECT_CALL(*dispatcher_, CreateQuicSession(_, _, _, _, _)).Times(0);
-  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0);
+  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _, _))
+      .Times(0);
   EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
       .Times(0);
 
@@ -2404,7 +2408,7 @@
   // New arrived CHLO will be dropped because this connection is in time wait
   // list.
   ASSERT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(conn_id));
-  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, conn_id, _, _));
+  EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, conn_id, _, _, _));
   ProcessFirstFlight(conn_id);
 }
 
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 17fe1b4..059abe3 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -46,6 +46,7 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_goaway, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_key_update_on_first_packet, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_on_stream_reset, true)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_stateless_reset, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_willing_and_able_to_write2, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_h3_datagram, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_pass_path_response_to_validator, false)
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index cead639..39e428d 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -1298,10 +1298,54 @@
 }
 
 // static
+size_t QuicFramer::GetMinStatelessResetPacketLength() {
+  // 5 bytes (40 bits) = 2 Fixed Bits (01) + 38 Unpredictable bits
+  return 5 + sizeof(quic::QuicUint128);
+}
+
+// static
 std::unique_ptr<QuicEncryptedPacket> QuicFramer::BuildIetfStatelessResetPacket(
     QuicConnectionId /*connection_id*/,
+    size_t received_packet_length,
     QuicUint128 stateless_reset_token) {
   QUIC_DVLOG(1) << "Building IETF stateless reset packet.";
+  if (GetQuicReloadableFlag(quic_fix_stateless_reset)) {
+    if (received_packet_length <= GetMinStatelessResetPacketLength()) {
+      QUIC_BUG_V2(362045737_1)
+          << "Tried to build stateless reset packet with received packet "
+             "length "
+          << received_packet_length;
+      return nullptr;
+    }
+    // To ensure stateless reset is indistinguishable from a valid packet,
+    // include the max connection ID length.
+    size_t len = std::min(received_packet_length - 1,
+                          GetMinStatelessResetPacketLength() + 1 +
+                              kQuicMaxConnectionIdWithLengthPrefixLength);
+    std::unique_ptr<char[]> buffer(new char[len]);
+    QuicDataWriter writer(len, buffer.get());
+    // Append random bytes.
+    if (!writer.WriteInsecureRandomBytes(QuicRandom::GetInstance(),
+                                         len - sizeof(quic::QuicUint128))) {
+      QUIC_BUG_V2(362045737_2) << "Failed to append random bytes of length: "
+                               << len - sizeof(quic::QuicUint128);
+      return nullptr;
+    }
+    // Change first 2 fixed bits to 01.
+    buffer[0] &= ~FLAGS_LONG_HEADER;
+    buffer[0] |= FLAGS_FIXED_BIT;
+
+    // Append stateless reset token.
+    if (!writer.WriteBytes(&stateless_reset_token,
+                           sizeof(stateless_reset_token))) {
+      QUIC_BUG_V2(362045737_3) << "Failed to write stateless reset token";
+      return nullptr;
+    }
+    QUIC_RELOADABLE_FLAG_COUNT(quic_fix_stateless_reset);
+    return std::make_unique<QuicEncryptedPacket>(buffer.release(), len,
+                                                 /*owns_buffer=*/true);
+  }
+
   size_t len = kPacketHeaderTypeSize + kMinRandomBytesLengthInStatelessReset +
                sizeof(stateless_reset_token);
   std::unique_ptr<char[]> buffer(new char[len]);
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h
index 5362718..13f9ee5 100644
--- a/quic/core/quic_framer.h
+++ b/quic/core/quic_framer.h
@@ -470,9 +470,13 @@
   static std::unique_ptr<QuicEncryptedPacket> BuildPublicResetPacket(
       const QuicPublicResetPacket& packet);
 
+  // Returns the minimal stateless reset packet length.
+  static size_t GetMinStatelessResetPacketLength();
+
   // Returns a new IETF stateless reset packet.
   static std::unique_ptr<QuicEncryptedPacket> BuildIetfStatelessResetPacket(
       QuicConnectionId connection_id,
+      size_t received_packet_length,
       QuicUint128 stateless_reset_token);
 
   // Returns a new version negotiation packet.
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index c0eac56..a627213 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -9245,6 +9245,60 @@
 }
 
 TEST_P(QuicFramerTest, BuildIetfStatelessResetPacket) {
+  if (GetQuicReloadableFlag(quic_fix_stateless_reset)) {
+    // clang-format off
+    unsigned char packet[] = {
+      // 1st byte 01XX XXXX
+      0x40,
+      // At least 4 bytes of random bytes.
+      0x00, 0x00, 0x00, 0x00,
+      // stateless reset token
+      0xB5, 0x69, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    };
+    // clang-format on
+
+    // Build the minimal stateless reset packet.
+    std::unique_ptr<QuicEncryptedPacket> data(
+        framer_.BuildIetfStatelessResetPacket(
+            FramerTestConnectionId(),
+            QuicFramer::GetMinStatelessResetPacketLength() + 1,
+            kTestStatelessResetToken));
+    ASSERT_TRUE(data);
+    EXPECT_EQ(QuicFramer::GetMinStatelessResetPacketLength(), data->length());
+    // Verify the first 2 bits are 01.
+    EXPECT_FALSE(data->data()[0] & FLAGS_LONG_HEADER);
+    EXPECT_TRUE(data->data()[0] & FLAGS_FIXED_BIT);
+    // Verify stateless reset token.
+    quiche::test::CompareCharArraysWithHexError(
+        "constructed packet",
+        data->data() + data->length() - sizeof(kTestStatelessResetToken),
+        sizeof(kTestStatelessResetToken),
+        AsChars(packet) + ABSL_ARRAYSIZE(packet) -
+            sizeof(kTestStatelessResetToken),
+        sizeof(kTestStatelessResetToken));
+
+    // Packets with length <= minimal stateless reset does not trigger stateless
+    // reset.
+    EXPECT_QUIC_BUG(
+        std::unique_ptr<QuicEncryptedPacket> data2(
+            framer_.BuildIetfStatelessResetPacket(
+                FramerTestConnectionId(),
+                QuicFramer::GetMinStatelessResetPacketLength(),
+                kTestStatelessResetToken)),
+        "Tried to build stateless reset packet with received packet length");
+
+    // Do not send stateless reset >= minimal stateless reset + 1 + max
+    // connection ID length.
+    std::unique_ptr<QuicEncryptedPacket> data3(
+        framer_.BuildIetfStatelessResetPacket(FramerTestConnectionId(), 1000,
+                                              kTestStatelessResetToken));
+    ASSERT_TRUE(data3);
+    EXPECT_EQ(QuicFramer::GetMinStatelessResetPacketLength() + 1 +
+                  kQuicMaxConnectionIdWithLengthPrefixLength,
+              data3->length());
+    return;
+  }
   // clang-format off
   unsigned char packet[] = {
     // type (short header, 1 byte packet number)
@@ -9258,7 +9312,7 @@
   // clang-format on
 
   std::unique_ptr<QuicEncryptedPacket> data(
-      framer_.BuildIetfStatelessResetPacket(FramerTestConnectionId(),
+      framer_.BuildIetfStatelessResetPacket(FramerTestConnectionId(), 0,
                                             kTestStatelessResetToken));
   ASSERT_TRUE(data != nullptr);
   // Skip packet number byte which is random in stateless reset packet.
diff --git a/quic/core/quic_time_wait_list_manager.cc b/quic/core/quic_time_wait_list_manager.cc
index d7ab999..4cbef95 100644
--- a/quic/core/quic_time_wait_list_manager.cc
+++ b/quic/core/quic_time_wait_list_manager.cc
@@ -192,6 +192,7 @@
     const QuicSocketAddress& peer_address,
     QuicConnectionId connection_id,
     PacketHeaderFormat header_format,
+    size_t received_packet_length,
     std::unique_ptr<QuicPerPacketContext> packet_context) {
   QUICHE_DCHECK(IsConnectionIdInTimeWait(connection_id));
   // TODO(satyamshekhar): Think about handling packets from different peer
@@ -240,7 +241,7 @@
           // Send stateless reset in response to short header packets.
           SendPublicReset(self_address, peer_address, connection_id,
                           connection_data->info.ietf_quic,
-                          std::move(packet_context));
+                          received_packet_length, std::move(packet_context));
           return;
         case GOOGLE_QUIC_PACKET:
           if (connection_data->info.ietf_quic) {
@@ -273,7 +274,7 @@
         QUIC_CODE_COUNT(quic_stateless_reset_long_header_packet);
       }
       SendPublicReset(self_address, peer_address, connection_id,
-                      connection_data->info.ietf_quic,
+                      connection_data->info.ietf_quic, received_packet_length,
                       std::move(packet_context));
       return;
     case DO_NOTHING:
@@ -318,10 +319,11 @@
     const QuicSocketAddress& peer_address,
     QuicConnectionId connection_id,
     bool ietf_quic,
+    size_t received_packet_length,
     std::unique_ptr<QuicPerPacketContext> packet_context) {
   if (ietf_quic) {
     std::unique_ptr<QuicEncryptedPacket> ietf_reset_packet =
-        BuildIetfStatelessResetPacket(connection_id);
+        BuildIetfStatelessResetPacket(connection_id, received_packet_length);
     QUIC_DVLOG(2) << "Dispatcher sending IETF reset packet for "
                   << connection_id << std::endl
                   << quiche::QuicheTextUtils::HexDump(
@@ -333,6 +335,7 @@
         packet_context.get());
     return;
   }
+  // Google QUIC public resets donot elicit resets in response.
   QuicPublicResetPacket packet;
   packet.connection_id = connection_id;
   // TODO(satyamshekhar): generate a valid nonce for this connection_id.
@@ -366,9 +369,11 @@
 
 std::unique_ptr<QuicEncryptedPacket>
 QuicTimeWaitListManager::BuildIetfStatelessResetPacket(
-    QuicConnectionId connection_id) {
+    QuicConnectionId connection_id,
+    size_t received_packet_length) {
   return QuicFramer::BuildIetfStatelessResetPacket(
-      connection_id, GetStatelessResetToken(connection_id));
+      connection_id, received_packet_length,
+      GetStatelessResetToken(connection_id));
 }
 
 // Either sends the packet and deletes it or makes pending queue the
diff --git a/quic/core/quic_time_wait_list_manager.h b/quic/core/quic_time_wait_list_manager.h
index 9b3739b..aa5686d 100644
--- a/quic/core/quic_time_wait_list_manager.h
+++ b/quic/core/quic_time_wait_list_manager.h
@@ -118,11 +118,14 @@
   // connection_id. Sending of the public reset packet is throttled by using
   // exponential back off. QUICHE_DCHECKs for the connection_id to be in time
   // wait state. virtual to override in tests.
+  // TODO(fayang): change ProcessPacket and SendPublicReset to take
+  // ReceivedPacketInfo.
   virtual void ProcessPacket(
       const QuicSocketAddress& self_address,
       const QuicSocketAddress& peer_address,
       QuicConnectionId connection_id,
       PacketHeaderFormat header_format,
+      size_t received_packet_length,
       std::unique_ptr<QuicPerPacketContext> packet_context);
 
   // Called by the dispatcher when the underlying socket becomes writable again,
@@ -164,6 +167,7 @@
       const QuicSocketAddress& peer_address,
       QuicConnectionId connection_id,
       bool ietf_quic,
+      size_t received_packet_length,
       std::unique_ptr<QuicPerPacketContext> packet_context);
 
   // Called to send |packet|.
@@ -257,7 +261,8 @@
       QuicTime::Delta /*srtt*/) const {}
 
   std::unique_ptr<QuicEncryptedPacket> BuildIetfStatelessResetPacket(
-      QuicConnectionId connection_id);
+      QuicConnectionId connection_id,
+      size_t received_packet_length);
 
   // A map from a recently closed connection_id to the number of packets
   // received after the termination of the connection bound to the
diff --git a/quic/core/quic_time_wait_list_manager_test.cc b/quic/core/quic_time_wait_list_manager_test.cc
index 189d3f0..37c7f37 100644
--- a/quic/core/quic_time_wait_list_manager_test.cc
+++ b/quic/core/quic_time_wait_list_manager_test.cc
@@ -41,6 +41,8 @@
 namespace test {
 namespace {
 
+const size_t kTestPacketSize = 100;
+
 class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor {
  public:
   FramerVisitorCapturingPublicReset(QuicConnectionId connection_id)
@@ -176,7 +178,7 @@
   void ProcessPacket(QuicConnectionId connection_id) {
     time_wait_list_manager_.ProcessPacket(
         self_address_, peer_address_, connection_id, GOOGLE_QUIC_PACKET,
-        std::make_unique<QuicPerPacketContext>());
+        kTestPacketSize, std::make_unique<QuicPerPacketContext>());
   }
 
   QuicEncryptedPacket* ConstructEncryptedPacket(
@@ -684,7 +686,8 @@
   // Processes IETF short header packet.
   time_wait_list_manager_.ProcessPacket(
       self_address_, peer_address_, connection_id_,
-      IETF_QUIC_SHORT_HEADER_PACKET, std::make_unique<QuicPerPacketContext>());
+      IETF_QUIC_SHORT_HEADER_PACKET, kTestPacketSize,
+      std::make_unique<QuicPerPacketContext>());
 }
 
 TEST_F(QuicTimeWaitListManagerTest,
@@ -707,7 +710,8 @@
   // Processes IETF short header packet.
   time_wait_list_manager_.ProcessPacket(
       self_address_, peer_address_, connection_id_,
-      IETF_QUIC_SHORT_HEADER_PACKET, std::make_unique<QuicPerPacketContext>());
+      IETF_QUIC_SHORT_HEADER_PACKET, kTestPacketSize,
+      std::make_unique<QuicPerPacketContext>());
 }
 
 TEST_F(QuicTimeWaitListManagerTest,
@@ -741,7 +745,7 @@
   for (auto const& cid : active_connection_ids) {
     time_wait_list_manager_.ProcessPacket(
         self_address_, peer_address_, cid, IETF_QUIC_SHORT_HEADER_PACKET,
-        std::make_unique<QuicPerPacketContext>());
+        kTestPacketSize, std::make_unique<QuicPerPacketContext>());
   }
 }
 
diff --git a/quic/test_tools/mock_quic_time_wait_list_manager.h b/quic/test_tools/mock_quic_time_wait_list_manager.h
index aadba94..12bf080 100644
--- a/quic/test_tools/mock_quic_time_wait_list_manager.h
+++ b/quic/test_tools/mock_quic_time_wait_list_manager.h
@@ -36,11 +36,12 @@
 
   MOCK_METHOD(void,
               ProcessPacket,
-              (const QuicSocketAddress& server_address,
-               const QuicSocketAddress& client_address,
-               QuicConnectionId connection_id,
-               PacketHeaderFormat header_format,
-               std::unique_ptr<QuicPerPacketContext> packet_context),
+              (const QuicSocketAddress&,
+               const QuicSocketAddress&,
+               QuicConnectionId,
+               PacketHeaderFormat,
+               size_t,
+               std::unique_ptr<QuicPerPacketContext>),
               (override));
 
   MOCK_METHOD(void,
@@ -61,6 +62,7 @@
                const QuicSocketAddress&,
                QuicConnectionId,
                bool,
+               size_t,
                std::unique_ptr<QuicPerPacketContext>),
               (override));
 
diff --git a/quic/test_tools/quic_dispatcher_peer.cc b/quic/test_tools/quic_dispatcher_peer.cc
index 5736773..3a553ce 100644
--- a/quic/test_tools/quic_dispatcher_peer.cc
+++ b/quic/test_tools/quic_dispatcher_peer.cc
@@ -87,10 +87,11 @@
     const QuicSocketAddress& peer_address,
     QuicConnectionId connection_id,
     bool ietf_quic,
+    size_t received_packet_length,
     std::unique_ptr<QuicPerPacketContext> packet_context) {
   dispatcher->time_wait_list_manager()->SendPublicReset(
       self_address, peer_address, connection_id, ietf_quic,
-      std::move(packet_context));
+      received_packet_length, std::move(packet_context));
 }
 
 // static
diff --git a/quic/test_tools/quic_dispatcher_peer.h b/quic/test_tools/quic_dispatcher_peer.h
index f3be472..fc8ab20 100644
--- a/quic/test_tools/quic_dispatcher_peer.h
+++ b/quic/test_tools/quic_dispatcher_peer.h
@@ -57,6 +57,7 @@
       const QuicSocketAddress& peer_address,
       QuicConnectionId connection_id,
       bool ietf_quic,
+      size_t received_packet_length,
       std::unique_ptr<QuicPerPacketContext> packet_context);
 
   static std::unique_ptr<QuicPerPacketContext> GetPerPacketContext(