[9] RealtimeIncoming{Audio,Video}Source removes sink after derived members destroyed
Severity: Medium | Component: WebCore WebRTC media pipeline | 02e76c6
Rated Medium because the diff fixes a race-window UAF where libwebrtc media-thread callbacks can dereference destroyed derived state; reaching the window from web content requires driving RTCPeerConnection teardown while a preventSourceFromEnding observer blocks the normal requestToEnd path.
Crashes occurred when WebRTC audio/video callbacks accessed destroyed member variables during object destruction. The compiler-generated destructors in derived classes (e.g. RealtimeIncomingAudioSourceCocoa) destroy derived members before calling the base class destructor, but the base class destructor's stop() call is what removes the audio/video track sink. The fix ensures derived destructors call stop() to remove sinks before any member destruction occurs.
Source/WebCore/platform/mediastream/RealtimeIncomingAudioSource.cpp
RealtimeIncomingAudioSource::~RealtimeIncomingAudioSource()
{
- stop();
+ // Subclasses must call stop() in their destructors to ensure the audio
+ // track sink is removed BEFORE derived members are destroyed. Otherwise,
+ // the OnData callback may access destroyed members on the audio thread.
+ ASSERT(!isProducingData());
m_audioTrack->UnregisterObserver(this);
}
Source/WebCore/platform/mediastream/cocoa/RealtimeIncomingAudioSourceCocoa.cpp
+RealtimeIncomingAudioSourceCocoa::~RealtimeIncomingAudioSourceCocoa()
+{
+ stop();
+}
Source/WebCore/platform/mediastream/cocoa/RealtimeIncomingVideoSourceCocoa.mm
+RealtimeIncomingVideoSourceCocoa::~RealtimeIncomingVideoSourceCocoa()
+{
+ stop();
+}
Patch Details
Explicit destructors are added to all four concrete subclasses (RealtimeIncomingAudioSourceCocoa, RealtimeIncomingVideoSourceCocoa, RealtimeIncomingAudioSourceLibWebRTC, RealtimeIncomingVideoSourceLibWebRTC). Each calls stop() to remove the WebRTC audio/video track sink BEFORE C++ proceeds to destroy any derived members. The base destructors no longer call stop(); they retain only m_audioTrack->UnregisterObserver(this) / m_videoTrack->UnregisterObserver(this) and now ASSERT(!isProducingData()) to document and verify the new contract.
Use-after-free across C++ destruction order: a base-class destructor unregistered a still-active multi-threaded callback after the derived members it accesses had already been destroyed.
Background
When a remote peer sends audio or video via RTCPeerConnection, libwebrtc surfaces each remote track as a webrtc::AudioTrackInterface/webrtc::VideoTrackInterface. WebCore wraps each track in a RealtimeIncoming*Source that implements libwebrtc's sink interfaces (AudioTrackSinkInterface::OnData, VideoSinkInterface::OnFrame). The WebKit object registers itself as a sink via AddSink/AddOrUpdateSink from startProducingData() and must remove itself via RemoveSink from stopProducingData() before it dies. libwebrtc invokes OnData/OnFrame on its own media threads, not on the WebCore main thread. RemoveSink inside libwebrtc takes a sink_lock_ that serialises against the callback dispatch.
For a derived class D : B, ~D runs first, then D's member destructors, then ~B — so any operation that must complete while derived state is alive has to happen inside ~D. RTCPeerConnection::doClose() is the normal teardown path and calls requestToEnd() on its sources, but a RealtimeMediaSourceObserver whose preventSourceFromEnding() returns true can block requestToEnd(), so the source can reach destruction with isProducingData() still true.
Analysis
When a RealtimeIncomingAudioSourceCocoa is destroyed, the compiler first runs the derived destructor, then destroys derived data members (m_audioBufferList, the pixel buffer pool), and only then invokes the base destructor. The base destructor was the one calling stop(), which removes the WebRTC sink. That means a media-thread OnData or OnFrame callback could already be running, or could be entered, while the WebRTC track still held a pointer to the partially-destroyed subclass; the callback then dereferences members like m_audioBufferList that the C++ runtime has already torn down.
Aaaaaaaa Aaaaaaaaaaaa Aaaaaaaaaaaa Aaaa Aaaaaaaaaaa Aaaaaaaaa Aaa Aaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaa Aaaaaaa Aaa Aaaa a Aaaa Aaaaa Aaa Aaaaaaa Aaaaa Aa Aaaaa Aaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaa Aaaaaaaaaaaaaaaa Aa Aaaa Aaa Aaaaaa Aaaaaa Aaa Aaaa Aaaa Aaa Aa Aaaaaaa Aaaa a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaa Aaa Aa Aaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaa Aa Aaaaa Aaaaa Aaa Aaa Aaaaa Aaaaaaaa Aaaa Aaaa Aaaaaaa Aaaaaaaaaa Aa Aaa Aaaa Aa Aaaaaaaaaaaa Aaaa Aaa Aaaaaaa Aaaaaaaa Aaaaaaa Aaaaa Aaa Aaaaaaa Aaaaaaa Aaa Aaaaa Aaaaaa Aaa Aaaa Aaaaaaaaaa Aaa Aaaa Aaaaaaaa Aaa Aaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaaa Aaaaaa Aaaaaa Aaaaa Aaa Aaaaaa Aaaaaaaaaaaa Aaaaaaaaa Aaaaa Aaa Aaaaaaaaa Aaaaa Aaaaaaaa Aa Aaaa Aa Aaaaaa Aaaaaaaaaaaa a Aaaaaaaaa Aaaa Aaaaaaaaa Aaaa Aaaaaaa Aaa Aaaaaaa Aaaa Aaaaaaaa a Aaaaaaaaaaaa Aaaa Aaaaaaaa Aaaaaa Aaa Aaaaaaaa Aaaaa Aaa Aaaaaaaa Aaaaa Aa Aaaaaaaaaa a Aaaaaa Aaaa Aaa Aaaaaaaaaaaaaaaaaaa Aa Aaaaaaa Aaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaa Aaaa Aaa Aaaaaaaaaaa Aa a Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa a Aaaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaaaaa Aaaaaaaaa Aaaaa Aaa Aaaaaa Aaaaaaa a Aaaaaaaaaaaaaaaaaaa Aaa Aaaaaa Aa a Aaaaaa Aaaaa Aaaaaaa Aa Aaaaa Aaaaaa Aaaaaaa Aaa Aaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaa Aaaaaaa a Aaa Aaaaaaaaa Aaaaaa Aaa Aaaaaaaaaa Aaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaa Aa Aaaaaaaaa Aaa Aaa Aaaaaaaaaa a Aa Aaaa Aaaaaaaa Aaa Aaaaaaaa Aaaa Aaaaaa Aaaa Aaaaaaaaaaaaa Aaa Aaaa Aaaaaa Aaaa Aaa Aaaaaaa Aaaaaaaaaa Aaaa Aaaa Aaaaaa Aaaaaaaaaaaaa
🔒Detailed C++ destruction-order and cross-thread-callback lifetime analysis, including how a normal teardown path can be subverted and what primitives the resulting race could yield
Subscribe to read more
Audit directions
a Aaaaaaaaa Aaaaaaa Aaaa Aaaaaaaaa a Aaaaaaaaaaa Aaaaaaaa Aaaaaaaaaaa Aaaaaaaaaa Aaaaa Aaaaaaaaaaaa Aaaaaaaaa Aaaaaaaaa Aaa Aaaaaa Aaaaaaaaa Aaaaaa Aaaaaaaaa Aaaaaaaaa Aaaaaaaaa Aaa Aaaaaaaa Aaaaaa Aaaa Aaa Aaaaaaa Aaaaaa Aaa Aaaaaaaaaa Aa a Aaaaaaaaaa Aaaaaaaaaaa Aaaaa Aaa Aaaaaaa Aaaa Aaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaa Aaa Aaaaaaaaaaaa Aaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaa Aaaaaaaa Aaaaaaaaaaaaa
a Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaa Aaaa Aaaaaaaaaaaaaa Aaaaaaa Aaaaaa Aaaaaaaa Aaaaa Aaaaaa Aaaaaa Aaaaaa Aaa Aaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaa Aaaaaaaa Aaaaaaaa Aaa Aaa Aaa Aaaaaaaaaa Aaaaaaa Aaaaaaaaa Aaaaa Aaaaaaaaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaa Aaaaaaa
a Aaaaaaaaaa Aaaaaaaaaa Aaaaaaaaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaa Aa Aaaaaaaaa Aa a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aa Aaaaaaaaa Aaaaa Aaaaaaaaa Aaa Aaaaa Aaaaaa Aaaaaaaaa Aaa Aaa Aaaa Aaaaaaaa Aaaaaa Aaa Aaaaaaaa Aaaaaaaaaaa Aaaa Aaaaaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaa Aa Aaaaa Aaaaa
a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaaaaaa Aa Aaaa Aaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaa Aaaaaaa Aaaaaaaa Aaaaa Aaa Aaaaa Aaaa Aaaaa Aaaaaaaa a Aaaaaaaaaa Aaaaa Aaaaaaa Aa Aaaaaaaa Aaaaaaaaaa a Aaaaaaaa Aaaaaaaaa Aaa Aaaaaaaaa
🔒Four reusable audit patterns identified for cross-thread callback lifetime bugs across the WebRTC media pipeline and related WebCore subsystems, with concrete grep targets
Subscribe to read more