[4] WebRTC DTLS UAF on RTCP-mux renegotiation
Severity: High | Component: libwebrtc DTLS transport | c458fe1
Rated High because the diff adds destructor-time unsubscription of this-capturing callbacks that the pre-fix code left subscribed; the absence of any matching UnsubscribeReceivingState API upstream confirms the dangling-closure path is real, and a notification fired after DtlsTransportInternalImpl destruction performs virtual dispatch on freed memory — a UAF primitive in a libwebrtc-hosting renderer process.
DTLSTransport is not unregistering itself from receive/send callbacks, which can trigger a UAF. We fix this by adding API to register/unregister a specific listener, and use that API in DtlsTransportInternalImpl's destructor and in DtlsTransportInternalImpl::ConnectToIceTransport. We manually validated the fix using a specific STUN server. We should upgrade our testing infra to support running the test with the STUN server.
Source/ThirdParty/libwebrtc/Source/webrtc/p2p/dtls/dtls_transport.cc
DtlsTransportInternalImpl::~DtlsTransportInternalImpl() {
ice_transport()->ResetDtlsStunPiggybackCallbacks();
ice_transport()->DeregisterReceivedPacketCallback(this);
+#if WEBRTC_WEBKIT_BUILD
+ ice_transport()->UnsubscribeReceivingState(this);
+ ice_transport()->UnsubscribeWritableState(this);
+#endif
}
Source/ThirdParty/libwebrtc/Source/webrtc/p2p/base/packet_transport_internal.cc
+#if WEBRTC_WEBKIT_BUILD
+void PacketTransportInternal::UnsubscribeReceivingState(void* tag)
+{
+ receiving_state_callbacks_.RemoveReceivers(tag);
+}
+#endif
Patch Details
The patch adds a new UnsubscribeReceivingState(void* tag) method on webrtc::PacketTransportInternal that delegates to receiving_state_callbacks_.RemoveReceivers(tag), mirroring the existing UnsubscribeWritableState. ~DtlsTransportInternalImpl is amended to call both UnsubscribeReceivingState(this) and UnsubscribeWritableState(this) on its underlying ICE transport, in addition to the pre-existing ResetDtlsStunPiggybackCallbacks and DeregisterReceivedPacketCallback(this) cleanup. All changes are guarded by #if WEBRTC_WEBKIT_BUILD, marking this as a WebKit downstream patch on the bundled libwebrtc.
Failure to deregister a self-referencing callback during destruction, leaving the publisher holding a dangling closure that fires on a freed observer.
Background
WebRTC negotiates media transports via SDP. By default an RTCPeerConnection audio/video m-section has two transports — one for RTP and one for RTCP — each with its own ICE+DTLS stack. RTCP-mux is an SDP-level optimization (a=rtcp-mux) that merges RTP and RTCP onto a single 5-tuple; when later negotiation enables it, the RTCP-only transport is torn down while its peer's parent transport persists.
In libwebrtc, DtlsTransportInternalImpl owns a DTLS state machine that sits on top of an IceTransportInternal (which derives from PacketTransportInternal). PacketTransportInternal exposes CallbackList-based subscription APIs (SubscribeReceivingState, SubscribeWritableState, etc.) where each callback is an absl::AnyInvocable keyed by a void* tag; calling Notify* dispatches every registered callback. absl::AnyInvocable is a move-only type-erased callable that owns its captured state — capturing this records a raw pointer to the subscriber. Callback list lifetime is independent of the lifetimes of objects whose this is captured; the publisher does not learn that a subscriber has been destroyed unless the subscriber explicitly unregisters.
Analysis
The bug is a dangling callback subscription. DtlsTransportInternalImpl::ConnectToIceTransport subscribes this as a tagged receiver on the underlying ICE transport's receiving_state_callbacks_ and writable_state_callbacks_ lists. Those entries are absl::AnyInvocable<void(PacketTransportInternal*)> closures that capture this (the DtlsTransportInternalImpl*). The pre-fix destructor only cleared the piggyback callbacks and the received-packet callback; it did not remove the receiving-state and writable-state subscriptions. The upstream PacketTransportInternal API did not even expose an Unsubscribe for ReceivingState, so there was no way to detach. After the DtlsTransportInternalImpl is destroyed, the ICE transport survives and may later invoke NotifyReceivingState/NotifyWritableState, dispatching the stale closure on freed memory. The closure invokes a member function on the dead C++ object, performing virtual dispatch on a vtable pointer that points into reclaimable storage — a textbook UAF.
Aaa Aaaaaaaaaa Aaaaaaaa Aaaa Aaa Aaaaaaaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aa Aaaaaaaaaaaa Aaaa Aaaaaaaaa Aaaa Aaa Aaaaaaa Aaaaaaaaaaaa Aaaaaaaa Aaaaaaaa Aa a Aaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaa Aaaa Aaaaaaaaaaaa Aa Aaa Aaaa Aaaa Aaaaaaaaa Aa Aaaaaaaaa Aaaaa Aaa Aaaaaa Aaa Aaaaaaaaa Aaaaaaaaa Aaaaa Aaaaa Aaaaaaaaaaa Aa Aaa Aaaaaaaaa Aaa Aaaaaaaaa a Aaa Aaaaaaa Aaa a Aaaaaaaaaa Aaaaaaaaa Aaaa Aaaa Aaaaa Aaaaaaaaaaaa Aa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa a Aa Aaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaa Aaa Aaaaaaaaaa Aaaaa Aaa Aaaaaaaaa Aaaa Aaaaaaa Aaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaa Aaaaaa Aa Aaaaa Aaaaaaa Aaa Aaa Aaaaaaa Aaaaaaaa Aaaaaaa Aaaaaaaa Aa Aaa Aaaa Aaaaaaa Aaa Aaaaaa Aaaaaaa Aaaaaa Aaa Aaaaa Aaa Aaaaaaaaaa Aaaaaaa a Aaaaaaaa Aaaa Aaaaaa Aaaaaa Aaaa Aaaaa Aaaaaaaa Aaaaaaaaaaaaa Aaaaaaaa Aaa Aaaaa Aaaaaaaa Aa Aaaaa Aaa Aaaaaaaaaaaaaa Aa Aaaaa Aaa Aaaaa Aa Aaaaaaaaaa Aaaa Aaa Aaaaa Aaaaa Aaaaaaaaa Aa a Aaaaaaaaaa Aaaa Aaaaaa Aaaaaa Aaaa Aaaaaaa Aaaaaaaa
Aaa Aaaaaaaa Aaaaaa Aa a Aaaaaaaaaaaaaa Aaaa Aaaaaaa Aaaaaaaa Aa a Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaa Aaaa Aa Aaaaaaaaa Aa Aa Aaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaaaa Aaa Aaaa Aaa Aaa Aaaaaaaaaaaa a Aaaaaaaaaaaaa Aaaaaaa Aaaaaa Aa Aaaaaa Aaaaaa Aaaaaaa Aaaaaaaaa Aaaa Aa Aaa Aaa Aaaaaaa Aaaaaaaaaa a Aaa Aaaaaa Aaaa Aaaaa Aa Aaaaaaaaaa Aa Aaaaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaa Aa Aa Aaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaa Aaaaaa Aaaaaaaa Aaaaaaaaaaaa Aaa Aaaaaaaaa Aaaaaa Aa a Aaaaaaaa Aaaaa Aaaaaaaaa Aaaa Aaa Aaaaaaaa
Aaaaaaaaa Aaaaaaaa Aaaaaa Aaa Aaaaaaaaaa Aaa Aaaaaaaaaaa Aaaaaaa Aaaaaaa Aaaaaaaaa Aa Aaaaa Aaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaaaaa Aaaaaa Aaaaaa Aaa Aaaaaaaaaaaaa Aaaaaa Aaaaaaaaa Aaa Aaaaa Aaaaaaa Aa Aaaaaaaa Aaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaa Aaaaaaaaaaaa Aaaaaaa Aaaaaa Aaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaaa Aa Aaaaa Aaaaaaaaa Aaaaa Aaaaa Aaaaaaaaaaa a Aaa Aaaa Aaaa Aaaaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaa a Aaaaaaa a Aaaaaaaa Aaaaaaaaaaaaa Aaaaaaa a Aaaaaaa Aaaa Aaaa Aaa Aaaa Aaa Aaa Aaaaaaaaa Aaaaaaaaa Aaaaa Aaaaaaa Aaaaa Aaaaaaaaa Aaaaaa a Aaa Aaaaaaaaa Aa Aaa Aaaaaaa Aaaaaaa Aaaaaaaaaa a Aaaaaaaa Aaaaaaa Aaaaaa Aa Aaaaa Aaaaaaaa Aaa Aaaa Aaaaaa Aaaaaaaaaaa
Aaaaaaaa Aaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaaa Aaaaaaaa Aaaaaaaaaaaaaa Aaaaaaa Aaa Aaaa Aa Aaa Aaaaaaaaaaaa Aaaa a Aaaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaa a Aaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaa Aaaaaaaaaa Aaaaaaa Aaaa Aaaa Aaaaa Aaaaaa Aaa Aaaaaaaaaaaaaaa Aaaaaa Aa Aaa Aaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaa Aaaa Aaa Aaaaaa Aaaaaaaa Aaa Aaa Aaaa Aaaaa Aaaaaaaaa Aaaaaaaaaa
🔒Multiple reusable audit patterns identified for pub/sub lifetime bugs across libwebrtc, with concrete starting points for variant discovery.
Subscribe to read more
Audit directions
a Aaaaaaaaaaaa Aaaaaaa Aaaa Aaaaa Aaaaaaaaaaa Aaaaaa Aaaaaaa a Aaaaaaaa Aaaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaa Aaaa Aaaaaaaa a Aaaaaaaaaa Aaa Aa Aaaaaaaaa Aaaaaa Aaa Aaaaaaaaaaaa Aaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaa Aa Aaaaaaaaa Aaa Aaaaaaaa Aaaaaa Aaaa Aaa Aaaa Aaaaaaaaaa Aaaaa Aa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaa Aaa Aaaaaaaaaa Aaaa Aaa a Aaaaaa Aaaaaaaaaaaaaa Aaaa Aaaaa Aaaaaaaaaaaaa Aaaaa Aaaaaaaaaa Aaaaaaaa Aaaaaa Aaaaaaa a Aaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaa
a Aaaaaaaaaaa Aaaaa Aaaa Aaaaaaa a Aaaaa Aaaaaaaaa Aaaaa Aaa Aaaaaa Aaaaaaaaa Aaaaaaaa Aaaa Aaaaaaaa Aaaaaaaa Aaaaa Aaaaaa Aaa Aaaaaaaa Aa Aaaaaaaa Aaaaaaaaaaaa Aaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aa Aaaaaaaaa Aaa Aaaaa Aaaaaaaa Aaaaaaaaaaaaa Aaaa Aaaa Aaaaaaaa Aaaaaaaaaa Aaaaaaaa Aaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaa a Aaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaa Aaa Aaaaaaa Aaaaa Aa a Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaaaaaaaaaa Aaaaaaaaaaa
a Aaaaaaaa Aaa Aaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaaaaaaa Aaaaaa Aaaaaaaaaaa Aaa Aaaaaaaa Aaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaaaa Aaa Aaaaa Aaaaaaa Aaaa Aaaaaaa Aaa Aaaaaaa Aaaaaaaaa Aaaaaaa Aaaaaaaaaaaaa Aaaaa Aaaaa Aa a Aaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaaaa Aaaaaaaaaaa Aaaaaaa Aaaaa Aaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaa Aaaaaa Aaaaaaaa Aaaaaaa Aaaaaa Aaaaaaaa Aaaa Aaaaaaaaaaa Aaaaaaaaa Aaaaa Aaaaaaaa Aaaaaaaa Aaaaaaaaaaaaa Aa Aaaaaaaaaa Aaaaaaa Aaaaaaaaaaa
a Aaaaaaaaaaaaa Aaaaaaa Aaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaaaaa Aaaaa Aaa Aaaaaaaaaa Aaaaaaaaaaaaa Aaaa Aaaaa Aaaaa a Aaa Aaaa Aaaaaaaa Aaaaaaaaa Aaaa Aaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaa Aaa Aaaaaaaa Aaaaaaaaaa Aaaaaaaaaaa a Aaaaa Aaa Aaaaaaaaaaaa Aaaaa Aaaaa Aaaaaaaaa Aaa Aaaaaaaa Aaaaaaaaaaa Aaaaaa Aaaa Aaaaaaaaa Aaa Aaaaa Aaaaaaaa Aa a Aaaaaaaaaaaaa Aaaaa Aaaaaaaa
🔒Multiple reusable audit patterns identified for pub/sub lifetime bugs across libwebrtc, with concrete starting points for variant discovery.
Subscribe to read more