Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in WebRTC
DescriptionUse after free in WebRTC
ComponentWebRTC
Bug ClassUAF
Tracker503422316
Fix commit13fdb6e802e5 (src) +36/-3
CISA KEVNot listed
Creditedc6eed09fc8b174b0f3eebedcceb1e792
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
if
pc/data_channel_controller.cc
modified
for
pc/data_channel_controller.cc
modified
TEST_F
pc/data_channel_controller_unittest.cc
modified

Files Changed

  • pc/data_channel_controller.cc
  • pc/data_channel_controller_unittest.cc
From 13fdb6e802e5c326533445aa15f9717f2fc26004 Mon Sep 17 00:00:00 2001
From: Harald Alvestrand <[email protected]>
Date: Tue, 21 Apr 2026 14:59:35 +0000
Subject: [PATCH] pc: fix use-after-free in AllocateSctpSids

AllocateSctpSids iterates over newly assigned channels using raw
pointers and calls OnTransportReady on each one. OnTransportReady
can synchronously fail (e.g. if sending the DCEP OPEN message
fails), which may cause the channel to close and be deleted,
leaving the controller with a dangling pointer.

This change ensures the channels are kept alive during the loop by
using scoped_refptr for the temporary collection. This fixes the
UAF for any failure mode that prevents synchronous sending of the
initialization message.

Bug: chromium:503422316
Change-Id: I1b927386fb4ed4279036835a244320aba6acf875
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/465701
Commit-Queue: Harald Alvestrand <[email protected]>
Auto-Submit: Harald Alvestrand <[email protected]>
Reviewed-by: Danil Chapovalov <[email protected]>
Cr-Commit-Position: refs/heads/main@{#47503}
---

diff --git a/pc/data_channel_controller.cc b/pc/data_channel_controller.cc
index 09b62cd..8b42607 100644
--- a/pc/data_channel_controller.cc
+++ b/pc/data_channel_controller.cc
@@ -475,7 +475,7 @@
   const bool ready_to_send =
       data_channel_transport_ && data_channel_transport_->IsReadyToSend();
 
-  std::vector<SctpDataChannel*> channels_to_start;
+  std::vector<scoped_refptr<SctpDataChannel>> channels_to_start;
   std::vector<scoped_refptr<SctpDataChannel>> channels_to_close;
   for (auto it = sctp_data_channels_n_.begin();
        it != sctp_data_channels_n_.end();) {
@@ -484,7 +484,7 @@
       if (sid.has_value()) {
         (*it)->SetSctpSid_n(*sid);
         AddSctpDataStream(*sid, (*it)->priority());
-        channels_to_start.push_back((*it).get());
+        channels_to_start.push_back(*it);
       } else {
         channels_to_close.push_back(std::move(*it));
         it = sctp_data_channels_n_.erase(it);
@@ -496,7 +496,7 @@
   // Since OnTransportReady can cause sending, and sending may fail and cause
   // channel to close, do this outside the loop.
   if (ready_to_send) {
-    for (auto* channel : channels_to_start) {
+    for (auto& channel : channels_to_start) {
       RTC_LOG(LS_INFO) << "AllocateSctpSids: Id assigned, ready to send.";
       channel->OnTransportReady();
     }
diff --git a/pc/data_channel_controller_unittest.cc b/pc/data_channel_controller_unittest.cc
index fa04960..9089df8 100644
--- a/pc/data_channel_controller_unittest.cc
+++ b/pc/data_channel_controller_unittest.cc
@@ -267,6 +267,39 @@
   EXPECT_THAT(ch2.value()->state(), Eq(DataChannelInterface::kClosed));
 }
 
+// This test reproduces the UAF reported in b/503422316.
+// It creates a data channel, drops the external reference, and then triggers
+// AllocateSctpSids. AllocateSctpSids calls OnTransportReady, which fails
+// synchronously, causing the channel to close and be deleted while
+// AllocateSctpSids (and SctpDataChannel::UpdateState) are still on the stack.
+TEST_F(DataChannelControllerTest, AllocateSctpSidsUafRepro) {
+  NiceMock<MockDataChannelTransport> transport;
+  // Reject all SendData with "message too large"
+  EXPECT_CALL(transport, SendData(_, _, _))
+      .WillRepeatedly(
+          Return(RTCError(RTCErrorType::INVALID_RANGE, "Message too large")));
+  bool ready_to_send = false;
+  EXPECT_CALL(transport, IsReadyToSend())
+      .WillRepeatedly(ReturnPointee(&ready_to_send));
+  EXPECT_CALL(transport, DtlsRole())
+      .WillOnce(Return(std::nullopt))
+      .WillRepeatedly(Return(SSL_CLIENT));
+  ON_CALL(transport, MaxChannels).WillByDefault(Return(100));
+
+  DataChannelControllerForTest dcc(pc_.get(), &transport);
+  auto ret = dcc.InternalCreateDataChannelWithProxy(
+      "ch1", InternalDataChannelInit(DataChannelInit()));
+  ASSERT_TRUE(ret.ok());
+
+  // Drop the reference.
+  ret.MoveValue();
+
+  ready_to_send = true;
+  pc_->network_thread()->BlockingCall([&] { dcc.OnTransportConnected(); });
+
+  run_loop_.Flush();
+}
+
 TEST_F(DataChannelControllerTest, BufferedAmountIncludesFromTransport) {
   NiceMock<MockDataChannelTransport> transport;
   EXPECT_CALL(transport, buffered_amount(0)).WillOnce(Return(4711));
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/pc/data_channel_controller_unittest.cc b/pc/data_channel_controller_unittest.cc
index fa04960..9089df8 100644
--- a/pc/data_channel_controller_unittest.cc
+++ b/pc/data_channel_controller_unittest.cc
@@ -267,6 +267,39 @@
   EXPECT_THAT(ch2.value()->state(), Eq(DataChannelInterface::kClosed));
 }
 
+// This test reproduces the UAF reported in b/503422316.
+// It creates a data channel, drops the external reference, and then triggers
+// AllocateSctpSids. AllocateSctpSids calls OnTransportReady, which fails
+// synchronously, causing the channel to close and be deleted while
+// AllocateSctpSids (and SctpDataChannel::UpdateState) are still on the stack.
+TEST_F(DataChannelControllerTest, AllocateSctpSidsUafRepro) {
+  NiceMock<MockDataChannelTransport> transport;
+  // Reject all SendData with "message too large"
+  EXPECT_CALL(transport, SendData(_, _, _))
+      .WillRepeatedly(
+          Return(RTCError(RTCErrorType::INVALID_RANGE, "Message too large")));
+  bool ready_to_send = false;
+  EXPECT_CALL(transport, IsReadyToSend())
+      .WillRepeatedly(ReturnPointee(&ready_to_send));
+  EXPECT_CALL(transport, DtlsRole())
+      .WillOnce(Return(std::nullopt))
+      .WillRepeatedly(Return(SSL_CLIENT));
+  ON_CALL(transport, MaxChannels).WillByDefault(Return(100));
+
+  DataChannelControllerForTest dcc(pc_.get(), &transport);
+  auto ret = dcc.InternalCreateDataChannelWithProxy(
+      "ch1", InternalDataChannelInit(DataChannelInit()));
+  ASSERT_TRUE(ret.ok());
+
+  // Drop the reference.
+  ret.MoveValue();
+
+  ready_to_send = true;
+  pc_->network_thread()->BlockingCall([&] { dcc.OnTransportConnected(); });
+
+  run_loop_.Flush();
+}
+
 TEST_F(DataChannelControllerTest, BufferedAmountIncludesFromTransport) {
   NiceMock<MockDataChannelTransport> transport;
   EXPECT_CALL(transport, buffered_amount(0)).WillOnce(Return(4711));
Loading diff…

Original Bug Report

reported by [email protected]

Use-After-Free in AllocateSctpSids via DCEP OPEN Message Failure Leads to Renderer Crash

Use-After-Free in AllocateSctpSids via DCEP OPEN Message Failure Leads to Renderer Crash

Summary

A use-after-free vulnerability exists in the WebRTC SCTP data channel implementation. When DataChannelController::AllocateSctpSids iterates over newly assigned channels using raw pointers, the act of sending the DCEP OPEN control message can synchronously fail if the message exceeds the negotiated max-message-size. The failure path destroys the SctpDataChannel object while the calling function still holds and uses the now-dangling raw pointer. A web page can trigger this deterministically, without any race condition, by creating data channels with long labels and then munging the remote SDP to set an artificially small max-message-size. The bug affects all platforms. MiraclePtr does not protect this code path.

Bisect

Introducing Commit: 1fbc1f1be61c6ad0ee5212b5e29ebc4103e8b8eb

Root Cause

DataChannelController::AllocateSctpSids assigns stream IDs to pending data channels and then starts them. The function collects channels into a local vector of raw pointers before calling OnTransportReady on each one:

// third_party/webrtc/pc/data_channel_controller.cc:455-488
void DataChannelController::AllocateSctpSids(SSLRole role) {
  std::vector<SctpDataChannel*> channels_to_start;
  // ...
  for (auto it = sctp_data_channels_n_.begin();
       it != sctp_data_channels_n_.end();) {
    if (!(*it)->sid_n().has_value()) {
      std::optional<StreamId> sid = sid_allocator_.AllocateSid(role);
      if (sid.has_value()) {
        (*it)->SetSctpSid_n(*sid);
        AddSctpDataStream(*sid, (*it)->priority());
        channels_to_start.push_back((*it).get());  // raw pointer extracted
      }
      // ...
    }
    ++it;
  }
  if (ready_to_send) {
    for (auto* channel : channels_to_start) {
      channel->OnTransportReady();  // uses raw pointer after potential free
    }
  }
}

A comment in the code acknowledges that “OnTransportReady can cause sending, and sending may fail and cause channel to close,” yet the function stores only raw pointers rather than scoped_refptr to keep the channels alive.

OnTransportReady delegates to UpdateState, which, for channels in the kConnecting state with handshake_state_ equal to kHandshakeShouldSendOpen, serializes a DCEP OPEN message and passes it to SendControlMessage:

// third_party/webrtc/pc/sctp_data_channel.cc:791-822
void SctpDataChannel::UpdateState() {
  switch (state_) {
    case kConnecting: {
      if (connected_to_transport() && controller_) {
        if (handshake_state_ == kHandshakeShouldSendOpen) {
          CopyOnWriteBuffer payload;
          WriteDataChannelOpenMessage(label_, protocol_, priority_, ordered_,
                                      max_retransmits_, max_retransmit_time_,
                                      &payload);
          SendControlMessage(payload);
        }
        // ... execution continues here after SendControlMessage returns
        if (handshake_state_ == kHandshakeReady ||
            handshake_state_ == kHandshakeWaitingForAck) {
          SetState(kOpen);
        }
      }
      break;
    }
    case kOpen: {  // line 823: the UAF read occurs here
      break;
    }
    // ...
  }
}

SendControlMessage calls controller_->SendData, which routes through DcSctpTransport::SendData. That function enforces the negotiated max-message-size:

// third_party/webrtc/media/sctp/dcsctp_transport.cc:341-348
auto max_message_size = socket_->options().max_message_size;
if (max_message_size > 0 && payload.size() > max_message_size) {
  return RTCError(RTCErrorType::INVALID_RANGE);
}

When the DCEP OPEN message (12 bytes of header plus the label and protocol strings) exceeds this limit, SendData returns an INVALID_RANGE error. SendControlMessage then calls CloseAbruptlyWithError:

// third_party/webrtc/pc/sctp_data_channel.cc:970-1003
bool SctpDataChannel::SendControlMessage(const CopyOnWriteBuffer& buffer) {
  RTCError err = controller_->SendData(*id_n_, send_params, buffer);
  if (!err.ok()) {
    CloseAbruptlyWithError(err);
  }
  return err.ok();
}

CloseAbruptlyWithError transitions the channel through kClosing to kClosed via SetState. Each SetState call notifies the controller through OnChannelStateChanged:

// third_party/webrtc/pc/sctp_data_channel.cc:853-865
void SctpDataChannel::SetState(DataState state) {
  state_ = state;
  if (observer_)
    observer_->OnStateChange();
  if (controller_)
    controller_->OnChannelStateChanged(this, state_);
}

When the state reaches kClosed, OnChannelStateChanged calls OnSctpDataChannelClosed, which erases the channel from the controller’s sctp_data_channels_n_ vector:

// third_party/webrtc/pc/data_channel_controller.cc:110-119
void DataChannelController::OnChannelStateChanged(
    SctpDataChannel* channel,
    DataChannelInterface::DataState state) {
  if (state == DataChannelInterface::DataState::kClosed)
    OnSctpDataChannelClosed(channel);
  // ...
}

// third_party/webrtc/pc/data_channel_controller.cc:497-508
void DataChannelController::OnSctpDataChannelClosed(SctpDataChannel* channel) {
  auto it = absl::c_find_if(sctp_data_channels_n_,
                            [&](const auto& c) { return c.get() == channel; });
  if (it != sctp_data_channels_n_.end()) {
    sctp_data_channels_n_.erase(it);  // drops the scoped_refptr
  }
}

If the JavaScript page does not retain a reference to the RTCDataChannel wrapper, the scoped_refptr in sctp_data_channels_n_ is the last reference. Erasing it destroys the SctpDataChannel object. Control then unwinds back through CloseAbruptlyWithError, SendControlMessage, and into UpdateState, which proceeds to evaluate the switch statement against state_, now reading freed memory at offset 232 within the deallocated 312-byte region.

The attacker controls both the label length (which determines the DCEP OPEN message size) and the remote SDP (which sets max-message-size through the a=max-message-size attribute). By creating data channels with labels of approximately 2000 bytes and munging the answer SDP to set max-message-size to 10, the DCEP OPEN message of roughly 2012 bytes is guaranteed to exceed the limit. The bug is fully deterministic and requires no race condition or timing sensitivity.

Reproduce

Tested at commit bb43679d94d13.

Build with ASAN:

autoninja -C ~/chromium/src/out/asan-release chrome

Launch:

ASAN_OPTIONS=detect_odr_violation=0 ~/chromium/src/out/asan-release/chrome \
  --no-sandbox --disable-gpu \
  --js-flags="--expose-gc" \
  --user-data-dir=/tmp/poc-$(date +%s) \
  ~/chromium/src/issue_webrtc032/poc.html

The renderer process crashes with a heap-use-after-free within seconds of the SCTP association establishing. No user interaction is required.

==2497007==ERROR: AddressSanitizer: heap-use-after-free on address 0x7c1bb8d10ba8 at pc 0x7efc2bf06be6 bp 0x7afaac6c5370 sp 0x7afaac6c5368
READ of size 4 at 0x7c1bb8d10ba8 thread T12 (WebRTC_W_and_N)
    #0 0x7efc2bf06be5 in webrtc::SctpDataChannel::UpdateState() third_party/webrtc/pc/sctp_data_channel.cc:823:13
    #1 0x7efc2bdbc17f in webrtc::DataChannelController::AllocateSctpSids(webrtc::SSLRole) third_party/webrtc/pc/data_channel_controller.cc:501:16
    #2 0x7efc2bcb241f in non-virtual thunk to webrtc::DcSctpTransport::OnConnected() third_party/webrtc/media/sctp/dcsctp_transport.cc:581:25
    #3 0x7efc2bcb6866 in dcsctp::CallbackDeferrer::TriggerDeferred() third_party/webrtc/net/dcsctp/socket/callback_deferrer.cc:49:5
    #4 0x7efc2bcc5122 in dcsctp::DcSctpSocket::ReceivePacket(std::__Cr::span<unsigned char const, 18446744073709551615ul>) third_party/webrtc/net/dcsctp/socket/callback_deferrer.h:54:44
    #5 0x7efc2bae945a in webrtc::callback_list_impl::CallbackListReceivers::Foreach(webrtc::FunctionView<void (webrtc::UntypedFunction&)>) third_party/webrtc/api/function_view.h:96:12
    #6 0x7efc2b9c2f29 in webrtc::PacketTransportInternal::NotifyPacketReceived(webrtc::ReceivedIpPacket const&) third_party/webrtc/rtc_base/callback_list.h:211:16
    #7 0x7efc2be0ec0c in webrtc::DtlsTransportInternalImpl::OnDtlsEvent(int, int) third_party/webrtc/p2p/dtls/dtls_transport.cc:1019:9
    #8 0x7efc2be03b93 in webrtc::StreamInterfaceChannel::OnPacketReceived(std::__Cr::span<unsigned char const, 18446744073709551615ul>) third_party/abseil-cpp/absl/functional/internal/any_invocable.h:766:1
    #9 0x7efc2be0dad8 in webrtc::DtlsTransportInternalImpl::OnReadPacket(webrtc::PacketTransportInternal*, webrtc::ReceivedIpPacket const&, bool) third_party/webrtc/p2p/dtls/dtls_transport.cc:1114:21
    #10 0x7efc2bae945a in webrtc::callback_list_impl::CallbackListReceivers::Foreach(webrtc::FunctionView<void (webrtc::UntypedFunction&)>) third_party/webrtc/api/function_view.h:96:12
    #11 0x7efc2b9c2f29 in webrtc::PacketTransportInternal::NotifyPacketReceived(webrtc::ReceivedIpPacket const&) third_party/webrtc/rtc_base/callback_list.h:211:16
    #12 0x7efc2b9c0f00 in void absl::internal_any_invocable::LocalInvoker<false, void, webrtc::P2PTransportChannel::AddConnection(webrtc::Connection*)::$_0&, webrtc::Connection*, webrtc::ReceivedIpPacket const&>(absl::internal_any_invocable::TypeErasedState*, absl::internal_any_invocable::ForwardedParameter<webrtc::Connection*>::type, absl::internal_any_invocable::ForwardedParameter<webrtc::ReceivedIpPacket const&>::type) third_party/webrtc/p2p/base/p2p_transport_channel.cc:2270:3
    #13 0x7efc2b98985e in webrtc::Connection::OnReadPacket(webrtc::ReceivedIpPacket const&) third_party/abseil-cpp/absl/functional/internal/any_invocable.h:766:1
    #14 0x7efc2c0124a8 in webrtc::UDPPort::HandleIncomingPacket(webrtc::AsyncPacketSocket*, webrtc::ReceivedIpPacket const&) third_party/webrtc/p2p/base/stun_port.cc:363:3
    #15 0x7efbde87d286 in blink::(anonymous namespace)::IpcPacketSocket::OnDataReceived(net::IPEndPoint const&, base::span<unsigned char const, 18446744073709551615ul, unsigned char const*>, base::TimeTicks const&, webrtc::EcnMarking) third_party/blink/renderer/platform/p2p/ipc_socket_factory.cc:709:3
    #16 0x7efbde88a16a in non-virtual thunk to blink::P2PSocketClientImpl::DataReceived(blink::Vector<mojo::StructPtr<network::mojom::blink::P2PReceivedPacket>, 0u, blink::PartitionAllocator>) third_party/blink/renderer/platform/p2p/socket_client_impl.cc:187:18
    #17 0x7efbdf6d7770 in network::mojom::blink::P2PSocketClientStubDispatch::Accept(network::mojom::blink::P2PSocketClient*, mojo::Message*) gen/services/network/public/mojom/p2p.mojom-blink.cc:2039:13
    #18 0x7efc3e0f2062 in mojo::InterfaceEndpointClient::HandleValidatedMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:1085:54
    #19 0x7efc3e10942b in mojo::MessageDispatcher::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/message_dispatcher.cc:44:19
    #20 0x7efc3e0f7914 in mojo::InterfaceEndpointClient::HandleIncomingMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:747:20
    #21 0x7efc3e118a0e in mojo::internal::MultiplexRouter::ProcessIncomingMessage(mojo::internal::MultiplexRouter::MessageWrapper*, mojo::internal::MultiplexRouter::ClientCallBehavior, base::SequencedTaskRunner*) mojo/public/cpp/bindings/lib/multiplex_router.cc:1204:42
    #22 0x7efc3e11723d in mojo::internal::MultiplexRouter::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/multiplex_router.cc:790:7
    #23 0x7efc3e10942b in mojo::MessageDispatcher::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/message_dispatcher.cc:44:19
    #24 0x7efc3e0ddc8f in mojo::Connector::DispatchMessage(mojo::ScopedHandleBase<mojo::MessageHandle>) mojo/public/cpp/bindings/lib/connector.cc:567:49
    #25 0x7efc3e0df4de in mojo::Connector::ReadAllAvailableMessages() mojo/public/cpp/bindings/lib/connector.cc:628:14
    #26 0x7efc3e0e0404 in base::internal::Invoker<base::internal::FunctorTraits<void (mojo::Connector::*&&)(), base::WeakPtr<mojo::Connector>&&>, base::internal::BindState<true, true, false, void (mojo::Connector::*)(), base::WeakPtr<mojo::Connector>>, void ()>::RunOnce(base::internal::BindStateBase*) base/functional/bind_internal.h:740:12
    #27 0x7efc3cb61b59 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&) base/functional/callback.h:155:12
    #28 0x7efc3cbdc1d0 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*) base/task/common/task_annotator.h:112:5
    #29 0x7efc3cbdb1a6 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork() base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:336:40
    #30 0x7efc3ca02e24 in base::MessagePumpDefault::Run(base::MessagePump::Delegate*) base/message_loop/message_pump_default.cc:42:55
    #31 0x7efc3cbdd823 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run(bool, base::TimeDelta) base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:640:12
    #32 0x7efc3caccc72 in base::RunLoop::Run(base::Location const&) base/run_loop.cc:135:14
    #33 0x7efc3cc75892 in base::Thread::Run(base::RunLoop*) base/threading/thread.cc:356:13
    #34 0x7efc3cc75df5 in base::Thread::ThreadMain() base/threading/thread.cc:426:3
    #35 0x7efc3ccdaa4c in base::(anonymous namespace)::ThreadFunc(void*) base/threading/platform_thread_posix.cc:102:13
    #36 0x55a454fd0b36 in asan_thread_start(void*) asan_interceptors.cpp

0x7c1bb8d10ba8 is located 232 bytes inside of 312-byte region [0x7c1bb8d10ac0,0x7c1bb8d10bf8)
freed by thread T12 (WebRTC_W_and_N) here:
    #0 0x55a45500cdc2 in operator delete(void*, unsigned long) (out/asan-release/chrome+0x681cdc2) (BuildId: 436e46875e0ded00)
    #1 0x7efc2bf0e39c in webrtc::RefCountedObject<webrtc::SctpDataChannel>::Release() const third_party/webrtc/rtc_base/ref_counted_object.h:42:7
    #2 0x7efc2bdbb4bb in webrtc::DataChannelController::OnSctpDataChannelClosed(webrtc::SctpDataChannel*) third_party/webrtc/api/scoped_refptr.h:105:13
    #3 0x7efc2bdbaf32 in webrtc::DataChannelController::OnChannelStateChanged(webrtc::SctpDataChannel*, webrtc::DataChannelInterface::DataState) third_party/webrtc/pc/data_channel_controller.cc:119:5
    #4 0x7efc2bf0b244 in webrtc::SctpDataChannel::SendControlMessage(webrtc::CopyOnWriteBuffer const&) third_party/webrtc/pc/sctp_data_channel.cc:1013:5
    #5 0x7efc2bf0655c in webrtc::SctpDataChannel::UpdateState() third_party/webrtc/pc/sctp_data_channel.cc:817:11
    #6 0x7efc2bdbc17f in webrtc::DataChannelController::AllocateSctpSids(webrtc::SSLRole) third_party/webrtc/pc/data_channel_controller.cc:501:16
    #7 0x7efc2bcb241f in non-virtual thunk to webrtc::DcSctpTransport::OnConnected() third_party/webrtc/media/sctp/dcsctp_transport.cc:581:25
    #8 0x7efc2bcb6866 in dcsctp::CallbackDeferrer::TriggerDeferred() third_party/webrtc/net/dcsctp/socket/callback_deferrer.cc:49:5
    #9 0x7efc2bcc5122 in dcsctp::DcSctpSocket::ReceivePacket(std::__Cr::span<unsigned char const, 18446744073709551615ul>) third_party/webrtc/net/dcsctp/socket/callback_deferrer.h:54:44
    #10 0x7efc2bae945a in webrtc::callback_list_impl::CallbackListReceivers::Foreach(webrtc::FunctionView<void (webrtc::UntypedFunction&)>) third_party/webrtc/api/function_view.h:96:12
    #11 0x7efc2b9c2f29 in webrtc::PacketTransportInternal::NotifyPacketReceived(webrtc::ReceivedIpPacket const&) third_party/webrtc/rtc_base/callback_list.h:211:16
    #12 0x7efc2be0ec0c in webrtc::DtlsTransportInternalImpl::OnDtlsEvent(int, int) third_party/webrtc/p2p/dtls/dtls_transport.cc:1019:9
    #13 0x7efc2be03b93 in webrtc::StreamInterfaceChannel::OnPacketReceived(std::__Cr::span<unsigned char const, 18446744073709551615ul>) third_party/abseil-cpp/absl/functional/internal/any_invocable.h:766:1
    #14 0x7efc2be0dad8 in webrtc::DtlsTransportInternalImpl::OnReadPacket(webrtc::PacketTransportInternal*, webrtc::ReceivedIpPacket const&, bool) third_party/webrtc/p2p/dtls/dtls_transport.cc:1114:21
    #15 0x7efc2bae945a in webrtc::callback_list_impl::CallbackListReceivers::Foreach(webrtc::FunctionView<void (webrtc::UntypedFunction&)>) third_party/webrtc/api/function_view.h:96:12
    #16 0x7efc2b9c2f29 in webrtc::PacketTransportInternal::NotifyPacketReceived(webrtc::ReceivedIpPacket const&) third_party/webrtc/rtc_base/callback_list.h:211:16
    #17 0x7efc2b9c0f00 in void absl::internal_any_invocable::LocalInvoker<false, void, webrtc::P2PTransportChannel::AddConnection(webrtc::Connection*)::$_0&, webrtc::Connection*, webrtc::ReceivedIpPacket const&>(absl::internal_any_invocable::TypeErasedState*, absl::internal_any_invocable::ForwardedParameter<webrtc::Connection*>::type, absl::internal_any_invocable::ForwardedParameter<webrtc::ReceivedIpPacket const&>::type) third_party/webrtc/p2p/base/p2p_transport_channel.cc:2270:3
    #18 0x7efc2b98985e in webrtc::Connection::OnReadPacket(webrtc::ReceivedIpPacket const&) third_party/abseil-cpp/absl/functional/internal/any_invocable.h:766:1
    #19 0x7efc2c0124a8 in webrtc::UDPPort::HandleIncomingPacket(webrtc::AsyncPacketSocket*, webrtc::ReceivedIpPacket const&) third_party/webrtc/p2p/base/stun_port.cc:363:3
    #20 0x7efbde87d286 in blink::(anonymous namespace)::IpcPacketSocket::OnDataReceived(net::IPEndPoint const&, base::span<unsigned char const, 18446744073709551615ul, unsigned char const*>, base::TimeTicks const&, webrtc::EcnMarking) third_party/blink/renderer/platform/p2p/ipc_socket_factory.cc:709:3
    #21 0x7efbde88a16a in non-virtual thunk to blink::P2PSocketClientImpl::DataReceived(blink::Vector<mojo::StructPtr<network::mojom::blink::P2PReceivedPacket>, 0u, blink::PartitionAllocator>) third_party/blink/renderer/platform/p2p/socket_client_impl.cc:187:18
    #22 0x7efbdf6d7770 in network::mojom::blink::P2PSocketClientStubDispatch::Accept(network::mojom::blink::P2PSocketClient*, mojo::Message*) gen/services/network/public/mojom/p2p.mojom-blink.cc:2039:13
    #23 0x7efc3e0f2062 in mojo::InterfaceEndpointClient::HandleValidatedMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:1085:54
    #24 0x7efc3e10942b in mojo::MessageDispatcher::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/message_dispatcher.cc:44:19
    #25 0x7efc3e0f7914 in mojo::InterfaceEndpointClient::HandleIncomingMessage(mojo::Message*) mojo/public/cpp/bindings/lib/interface_endpoint_client.cc:747:20
    #26 0x7efc3e118a0e in mojo::internal::MultiplexRouter::ProcessIncomingMessage(mojo::internal::MultiplexRouter::MessageWrapper*, mojo::internal::MultiplexRouter::ClientCallBehavior, base::SequencedTaskRunner*) mojo/public/cpp/bindings/lib/multiplex_router.cc:1204:42
    #27 0x7efc3e11723d in mojo::internal::MultiplexRouter::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/multiplex_router.cc:790:7
    #28 0x7efc3e10942b in mojo::MessageDispatcher::Accept(mojo::Message*) mojo/public/cpp/bindings/lib/message_dispatcher.cc:44:19
    #29 0x7efc3e0ddc8f in mojo::Connector::DispatchMessage(mojo::ScopedHandleBase<mojo::MessageHandle>) mojo/public/cpp/bindings/lib/connector.cc:567:49

previously allocated by thread T12 (WebRTC_W_and_N) here:
    #0 0x55a45500c1bd in operator new(unsigned long) (out/asan-release/chrome+0x681c1bd) (BuildId: 436e46875e0ded00)
    #1 0x7efc2bf04232 in webrtc::SctpDataChannel::Create(webrtc::WeakPtr<webrtc::SctpDataChannelControllerInterface>, std::__Cr::basic_string_view<char, std::__Cr::char_traits<char>>, bool, webrtc::InternalDataChannelInit const&, webrtc::Thread*, webrtc::Thread*) third_party/webrtc/api/make_ref_counted.h:90:27
    #2 0x7efc2bdbf7c9 in webrtc::DataChannelController::CreateDataChannel(std::__Cr::basic_string_view<char, std::__Cr::char_traits<char>>, webrtc::InternalDataChannelInit&) third_party/webrtc/pc/data_channel_controller.cc:409:44
    #3 0x7efc2bdc5081 in void webrtc::FunctionView<void ()>::CallVoidPtr<webrtc::RTCErrorOr<webrtc::scoped_refptr<webrtc::SctpDataChannel>> webrtc::Thread::BlockingCall<webrtc::DataChannelController::InternalCreateDataChannelWithProxy(std::__Cr::basic_string_view<char, std::__Cr::char_traits<char>>, webrtc::InternalDataChannelInit const&)::$_0, webrtc::RTCErrorOr<webrtc::scoped_refptr<webrtc::SctpDataChannel>>, void>(webrtc::DataChannelController::InternalCreateDataChannelWithProxy(std::__Cr::basic_string_view<char, std::__Cr::char_traits<char>>, webrtc::InternalDataChannelInit const&)::$_0&&, base::Location const&)::'lambda'()>(webrtc::FunctionView<void ()>::VoidUnion) third_party/webrtc/pc/data_channel_controller.cc:444:24
    #4 0x7efbd14258e4 in webrtc::ThreadWrapper::ProcessPendingSends() third_party/webrtc/api/function_view.h:96:12
    #5 0x7efbd1428314 in base::internal::Invoker<base::internal::FunctorTraits<void (webrtc::ThreadWrapper::*&&)(), base::WeakPtr<webrtc::ThreadWrapper>&&>, base::internal::BindState<true, true, false, void (webrtc::ThreadWrapper::*)(), base::WeakPtr<webrtc::ThreadWrapper>>, void ()>::RunOnce(base::internal::BindStateBase*) base/functional/bind_internal.h:740:12
    #6 0x7efc3cb61b59 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&) base/functional/callback.h:155:12
    #7 0x7efc3cbdc1d0 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*) base/task/common/task_annotator.h:112:5
    #8 0x7efc3cbdb1a6 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork() base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:336:40
    #9 0x7efc3ca02e24 in base::MessagePumpDefault::Run(base::MessagePump::Delegate*) base/message_loop/message_pump_default.cc:42:55
    #10 0x7efc3cbdd823 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run(bool, base::TimeDelta) base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:640:12
    #11 0x7efc3caccc72 in base::RunLoop::Run(base::Location const&) base/run_loop.cc:135:14
    #12 0x7efc3cc75892 in base::Thread::Run(base::RunLoop*) base/threading/thread.cc:356:13
    #13 0x7efc3cc75df5 in base::Thread::ThreadMain() base/threading/thread.cc:426:3
    #14 0x7efc3ccdaa4c in base::(anonymous namespace)::ThreadFunc(void*) base/threading/platform_thread_posix.cc:102:13
    #15 0x55a454fd0b36 in asan_thread_start(void*) asan_interceptors.cpp

SUMMARY: AddressSanitizer: heap-use-after-free third_party/webrtc/pc/sctp_data_channel.cc:823:13 in webrtc::SctpDataChannel::UpdateState()
Shadow bytes around the buggy address:
  0x7c1bb8d10900: fa fa fa fa fa fa f7 fa f7 00 00 00 00 00 00 00
  0x7c1bb8d10980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7c1bb8d10a00: 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa fa
  0x7c1bb8d10a80: fa fa fa fa fa fa f7 fa fd fd fd fd fd fd fd fd
  0x7c1bb8d10b00: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x7c1bb8d10b80: fd fd fd fd fd[fd]fd fd fd fd fd fd fd fd fd fa
  0x7c1bb8d10c00: fa fa fa fa fa fa f7 fa 00 00 00 00 00 00 00 01
  0x7c1bb8d10c80: fc 00 00 00 00 00 00 00 00 00 01 fc 00 00 00 00
  0x7c1bb8d10d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fa
  0x7c1bb8d10d80: fa fa fa fa fa fa f7 fa 00 00 00 00 00 00 00 01
  0x7c1bb8d10e00: fc 00 00 00 00 00 00 00 00 00 01 fc 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb

==2497007==ADDITIONAL INFO

==2497007==Note: Please include this section with the ASan report.
Task trace:
    #0 0x7efc3e0dff96 in mojo::Connector::ScheduleDispatchOfPendingMessagesOrWaitForMore(unsigned long) mojo/public/cpp/bindings/lib/connector.cc:588:7
    #1 0x7efc3e0dff96 in mojo::Connector::ScheduleDispatchOfPendingMessagesOrWaitForMore(unsigned long) mojo/public/cpp/bindings/lib/connector.cc:588:7
    #2 0x7efc3e0dff96 in mojo::Connector::ScheduleDispatchOfPendingMessagesOrWaitForMore(unsigned long) mojo/public/cpp/bindings/lib/connector.cc:588:7
    #3 0x7efc3e0dff96 in mojo::Connector::ScheduleDispatchOfPendingMessagesOrWaitForMore(unsigned long) mojo/public/cpp/bindings/lib/connector.cc:588:7

MiraclePtr Status: NOT PROTECTED
No raw_ptr<T> access to this region was detected prior to this crash.
This crash is still exploitable with MiraclePtr.
Refer to https://chromium.googlesource.com/chromium/src/+/main/base/memory/raw_ptr.md for details.

==2497007==END OF ADDITIONAL INFO

==2497007==ABORTING

References

Credit

Please use c6eed09fc8b174b0f3eebedcceb1e792 as the credit for this vulnerability. Thank you.

View on issue tracker