fix fuzzing error introduced by cl/800613375

The fuzz error is triggered by gfe2_reloadable_flag_quic_fail_on_empty_ack, which itself fixes a different fuzz error due to the same behavior. The change here is an additional state check, which is low risk.

I was able to repro the fuzz failure; it no longer fails.

Protected by FLAGS_quic_reloadable_flag_quic_empty_ack_early_exit.

PiperOrigin-RevId: 814006597
diff --git a/quiche/common/quiche_feature_flags_list.h b/quiche/common/quiche_feature_flags_list.h
index bd8cf14..4792a48 100755
--- a/quiche/common/quiche_feature_flags_list.h
+++ b/quiche/common/quiche_feature_flags_list.h
@@ -33,6 +33,7 @@
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_disable_version_q046, false, true, "If true, disable QUIC version Q046.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_disable_version_rfcv1, false, false, "If true, disable QUIC version h3 (RFCv1).")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_discard_initial_packet_with_key_dropped, false, true, "If true, discard INITIAL packet if the key has been dropped.")
+QUICHE_FLAG(bool, quiche_reloadable_flag_quic_disconnect_early_exit, false, false, "If true, does not update ack state if the connection has been closed.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enable_disable_resumption, true, true, "If true, disable resumption when receiving NRES connection option.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enable_mtu_discovery_at_server, false, false, "If true, QUIC will default enable MTU discovery at server, with a target of 1450 bytes.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_enable_server_on_wire_ping, true, true, "If true, enable server retransmittable on wire PING.")
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 8a2aa77..90a79e0 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -6073,6 +6073,11 @@
     }
     ResetAckStates();
   }
+  if (GetQuicReloadableFlag(quic_disconnect_early_exit) && !connected_) {
+    // FlushAckFrame can result in a closed connection. If so, avoid updating
+    // ack state that could trigger QUICHE_BUGs.
+    return;
+  }
 
   const QuicTime timeout =
       uber_received_packet_manager_.GetEarliestAckTimeout();
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index 2e02a64..2bc345b 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -18195,6 +18195,43 @@
   TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION);
 }
 
+// Regression test for b/443473227.
+TEST_P(QuicConnectionTest, DoNotUpdateAckStateAfterConnectionClose) {
+  if (!version().UsesTls()) {
+    return;
+  }
+  // Test will fail if this flag is false.
+  SetQuicReloadableFlag(quic_disconnect_early_exit, true);
+  // Path validation must occur after the handshake is confirmed.
+  connection_.RemoveEncrypter(ENCRYPTION_INITIAL);
+  connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+  // Avoid packets being held to coalesce.
+  EXPECT_CALL(visitor_, GetHandshakeState())
+      .WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
+  // Silence nagging that connection_ isn't sending retransmittable frames.
+  EXPECT_CALL(visitor_, OnAckNeedsRetransmittableFrame).Times(AnyNumber());
+
+  // Build a big ACK frame by processing widely spaced packets.
+  // Must be encoded in 8 bytes when a gap.
+  uint64_t packet_number_increment = 0x40000002;
+  // Test passes if <= 134.
+  uint64_t max_packet_number = packet_number_increment * 135ULL;
+  // Make an ACK frame that is too large to fit in a single packet.
+  for (uint64_t packet_number = packet_number_increment;
+       packet_number < max_packet_number;
+       packet_number += packet_number_increment) {
+    ProcessPacket(packet_number);
+  }
+
+  QuicFrames peer_frames;
+  peer_frames.push_back(QuicFrame(QuicPathChallengeFrame()));
+  writer_->SetShouldWriteFail();
+  writer_->SetWriteError(-109);
+  EXPECT_CALL(visitor_, OnConnectionClosed);
+  ProcessFramesPacketAtLevel(max_packet_number, peer_frames,
+                             ENCRYPTION_FORWARD_SECURE);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic