[dcsctp] Ignore nacks for already acked packets

To avoid leaving 'OutstandingData' in inconsistent state when such
specially crafted SACK packet arrives

Bug: chromium:502356094
Change-Id: Ie0e5d89b5ba769f337f62637b6812d9a83305494
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/466940
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Victor Boivie <boivie@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47567}
diff --git a/net/dcsctp/tx/outstanding_data.cc b/net/dcsctp/tx/outstanding_data.cc
index b996dbd..e033e04 100644
--- a/net/dcsctp/tx/outstanding_data.cc
+++ b/net/dcsctp/tx/outstanding_data.cc
@@ -276,6 +276,10 @@
                                bool retransmit_now,
                                bool do_fast_retransmit) {
   Item& item = GetItem(tsn);
+  // Ignore NACKs for chunks that have already been acknowledged.
+  if (item.is_acked()) {
+    return false;
+  }
   bool was_outstanding = item.is_outstanding();
 
   Item::NackAction action = item.Nack(retransmit_now);
diff --git a/net/dcsctp/tx/outstanding_data_test.cc b/net/dcsctp/tx/outstanding_data_test.cc
index 7818d9b..658802e 100644
--- a/net/dcsctp/tx/outstanding_data_test.cc
+++ b/net/dcsctp/tx/outstanding_data_test.cc
@@ -747,5 +747,30 @@
                   /*is_in_fast_recovery=*/false);
 }
 
+TEST_F(OutstandingDataTest, HandlesSacksWithOutOfBoundsTsns) {
+  // Send chunks with TSNs 10, 11, 12, 13, 14, 15, 16
+  for (int i = 0; i < 7; ++i) {
+    buf_.Insert(kMessageId, gen_.Ordered({1}, ""), kNow);
+  }
+
+  // This NACKs TSN 11, 13, 15 (1st miss indication)
+  SackChunk::GapAckBlock sack1[] = {{2, 2},   // TSN 12
+                                    {4, 4},   // TSN 14
+                                    {6, 6}};  // TSN 16
+  buf_.HandleSack(unwrapper_.Unwrap(TSN(10)), sack1,
+                  /*is_in_fast_recovery=*/false);
+  EXPECT_EQ(buf_.unacked_items(), 3u);  // 11, 13, 15
+
+  // The gap between block1-end (12) and block2-start (1011) causes
+  // NackBetweenAckBlocks to loop TSN 13..1010, but only TSN 13..16 are valid.
+  SackChunk::GapAckBlock sack2[] = {{1, 1},          // TSN 12
+                                    {1000, 60000}};  // TSN 1011..60011
+
+  buf_.HandleSack(unwrapper_.Unwrap(TSN(11)), sack2,
+                  /*is_in_fast_recovery=*/true);
+  // Packet 11 has been acknowledged.
+  EXPECT_EQ(buf_.unacked_items(), 2u);  // 13, 15
+}
+
 }  // namespace
 }  // namespace dcsctp