From: Michael Froman Date: Thu, 18 Sep 2025 12:09:00 -0500 Subject: Bug 1985396 - Cherry-pick upstream libwebrtc commit 63a59de808 Upstream commit: https://webrtc.googlesource.com/src/+/63a59de808ba7669e60bded703e71f360e5337e0 dcsctp: Avoid iterator invalidation The ExpireOutstandingChunks method iterated through the `outstanding_data_` container while conditionally calling `AbandonAllFor`. The `AbandonAllFor` method can, in some cases, add new elements to `outstanding_data_`, which invalidates the iterators used by the loop in `ExpireOutstandingChunks`. This could lead to undefined behavior. This aligns the C++ implementation to the Rust dcsctp port, which handles this correctly by using a two-pass approach. Bug: chromium:443213199 Change-Id: Ib5ac73b743c5ed900a69806088780c213536a4a6 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/408002 Reviewed-by: Emil Lundmark Commit-Queue: Emil Lundmark Auto-Submit: Victor Boivie Cr-Commit-Position: refs/heads/main@{#45566} Mercurial Revision: https://hg.mozilla.org/mozilla-central/rev/b85c57c2d6f0604075bf639a9a7342ece543cdf1 --- net/dcsctp/tx/outstanding_data.cc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/net/dcsctp/tx/outstanding_data.cc b/net/dcsctp/tx/outstanding_data.cc index a92fc6f638..e74843d6c7 100644 --- a/net/dcsctp/tx/outstanding_data.cc +++ b/net/dcsctp/tx/outstanding_data.cc @@ -392,6 +392,7 @@ std::vector> OutstandingData::GetChunksToBeRetransmitted( } void OutstandingData::ExpireOutstandingChunks(Timestamp now) { + std::vector tsns_to_expire; UnwrappedTSN tsn = last_cumulative_tsn_ack_; for (const Item& item : outstanding_data_) { tsn.Increment(); @@ -401,15 +402,22 @@ void OutstandingData::ExpireOutstandingChunks(Timestamp now) { if (item.is_abandoned()) { // Already abandoned. } else if (item.is_nacked() && item.has_expired(now)) { - RTC_DLOG(LS_VERBOSE) << "Marking nacked chunk " << *tsn.Wrap() - << " and message " << *item.data().mid - << " as expired"; - AbandonAllFor(item); + tsns_to_expire.push_back(tsn); } else { // A non-expired chunk. No need to iterate any further. break; } } + + for (UnwrappedTSN tsn_to_expire : tsns_to_expire) { + // The item is retrieved by TSN, as AbandonAllFor may have modified + // `outstanding_data_` and invalidated iterators from the first loop. + Item& item = GetItem(tsn_to_expire); + RTC_DLOG(LS_WARNING) << "Marking nacked chunk " << *tsn_to_expire.Wrap() + << " and message " << *item.data().mid + << " as expired"; + AbandonAllFor(item); + } RTC_DCHECK(IsConsistent()); }