Fix potential dcSCTP NackBetweenAckBlocks OOB access

Add strict GapAckBlock boundary validation in
RetransmissionQueue::IsSackValid to drop malformed SACK chunks that
acknowledge TSNs beyond the currently outstanding sequence numbers.
Additionally, adds bounds checking within NackBetweenAckBlocks to
prevent Out-Of-Bounds iterating.

Bug: webrtc:495700476
Change-Id: I6ca4011cd074ef3777ff972488dad79227e6d45b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/460720
Reviewed-by: Victor Boivie <boivie@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47283}
diff --git a/net/dcsctp/tx/outstanding_data.cc b/net/dcsctp/tx/outstanding_data.cc
index 89f1ea2..b996dbd 100644
--- a/net/dcsctp/tx/outstanding_data.cc
+++ b/net/dcsctp/tx/outstanding_data.cc
@@ -256,7 +256,8 @@
     UnwrappedTSN cur_block_first_acked =
         UnwrappedTSN::AddTo(cumulative_tsn_ack, block.start);
     for (UnwrappedTSN tsn = prev_block_last_acked.next_value();
-         tsn < cur_block_first_acked && tsn <= max_tsn_to_nack;
+         tsn < cur_block_first_acked && tsn <= max_tsn_to_nack &&
+         tsn < next_tsn();
          tsn = tsn.next_value()) {
       ack_info.has_packet_loss |=
           NackItem(tsn, /*retransmit_now=*/false,
diff --git a/net/dcsctp/tx/outstanding_data_test.cc b/net/dcsctp/tx/outstanding_data_test.cc
index f56f1d5..7818d9b 100644
--- a/net/dcsctp/tx/outstanding_data_test.cc
+++ b/net/dcsctp/tx/outstanding_data_test.cc
@@ -732,5 +732,20 @@
                           Pair(TSN(16), State::kAcked)));
 }
 
+TEST_F(OutstandingDataTest, NackBetweenAckBlocksDoesNotAccessOutOfBounds) {
+  for (int i = 0; i < 5; ++i) {
+    buf_.Insert(kMessageId, gen_.Ordered({1}, ""), kNow);
+  }
+
+  // Inject a malformed SACK where the GapAckBlock exceeds the number of
+  // outstanding items, potentially triggering an OOB read/write.
+  std::vector<SackChunk::GapAckBlock> malformed_blocks = {
+      SackChunk::GapAckBlock(1, 40000)};
+
+  // This should not crash or trigger ASAN errors.
+  buf_.HandleSack(unwrapper_.Unwrap(TSN(10)), malformed_blocks,
+                  /*is_in_fast_recovery=*/false);
+}
+
 }  // namespace
 }  // namespace dcsctp
diff --git a/net/dcsctp/tx/retransmission_queue.cc b/net/dcsctp/tx/retransmission_queue.cc
index 3dc2bc7..2cfc6ef 100644
--- a/net/dcsctp/tx/retransmission_queue.cc
+++ b/net/dcsctp/tx/retransmission_queue.cc
@@ -250,6 +250,14 @@
   } else if (cumulative_tsn_ack > outstanding_data_.highest_outstanding_tsn()) {
     return false;
   }
+
+  for (const auto& block : sack.gap_ack_blocks()) {
+    if (UnwrappedTSN::AddTo(cumulative_tsn_ack, block.end) >
+        outstanding_data_.highest_outstanding_tsn()) {
+      return false;
+    }
+  }
+
   return true;
 }