[9] RealtimeIncoming{Audio,Video}Source removes sink after derived members destroyed
Severity: Medium | Component: WebCore WebRTC media pipeline | 02e76c6
Medium으로 분류된 이유는, 이 diff가 race window UAF를 수정하기 때문입니다. libwebrtc media thread callback이 이미 소멸된 derived 멤버를 역참조할 수 있는 상황이 해당됩니다. Web content에서 이 window에 도달하려면, preventSourceFromEnding observer가 정상적인 requestToEnd 경로를 차단하는 상태에서 RTCPeerConnection teardown을 유도해야 합니다.
WebRTC audio/video callback이 객체 소멸 중에 이미 소멸된 멤버 변수에 접근하면서 crash가 발생했습니다. Derived 클래스(예: RealtimeIncomingAudioSourceCocoa)의 컴파일러 생성 destructor는 base 클래스 destructor를 호출하기 전에 derived 멤버들을 먼저 소멸시킵니다. 그런데 audio/video track sink를 제거하는 stop() 호출은 base 클래스 destructor 안에 위치해 있었습니다. 이번 수정으로 derived destructor가 멤버 소멸 이전에 stop()을 호출하여 sink를 먼저 제거하도록 변경되었습니다.
Source/WebCore/platform/mediastream/RealtimeIncomingAudioSource.cpp
RealtimeIncomingAudioSource::~RealtimeIncomingAudioSource()
{
- stop();
+ // Subclass는 각자의 destructor에서 stop()을 호출해야 합니다.
+ // derived 멤버가 소멸되기 전에 audio track sink를 반드시 제거해야 하기 때문입니다.
+ // 그렇지 않으면 audio thread의 OnData callback이 이미 소멸된 멤버에 접근할 수 있습니다.
+ 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
네 개의 concrete subclass(RealtimeIncomingAudioSourceCocoa, RealtimeIncomingVideoSourceCocoa, RealtimeIncomingAudioSourceLibWebRTC, RealtimeIncomingVideoSourceLibWebRTC) 모두에 명시적 destructor가 추가되었습니다. 각 destructor는 C++가 derived 멤버를 소멸시키기 전에 stop()을 호출하여 WebRTC audio/video track sink를 먼저 제거합니다. Base destructor에서는 더 이상 stop()을 호출하지 않으며, m_audioTrack->UnregisterObserver(this) / m_videoTrack->UnregisterObserver(this) 호출만 남아 있습니다. 아울러 새로운 계약 조건을 문서화하고 검증하기 위해 ASSERT(!isProducingData())가 추가되었습니다.
C++ 소멸 순서에서 발생하는 use-after-free: 멀티스레드 callback이 접근하는 derived 멤버가 이미 소멸된 이후에 base 클래스 destructor가 해당 callback의 등록을 해제하는 패턴.
Background
Remote peer가 RTCPeerConnection을 통해 audio나 video를 전송하면, libwebrtc는 각 remote track을 webrtc::AudioTrackInterface / webrtc::VideoTrackInterface로 노출합니다. WebCore는 각 track을 RealtimeIncoming*Source로 감싸며, 이 클래스는 libwebrtc의 sink interface(AudioTrackSinkInterface::OnData, VideoSinkInterface::OnFrame)를 구현합니다. WebKit 객체는 startProducingData()에서 AddSink/AddOrUpdateSink를 통해 sink로 자신을 등록하고, 소멸 전에 stopProducingData()의 RemoveSink를 통해 반드시 등록을 해제해야 합니다. libwebrtc는 OnData/OnFrame을 WebCore main thread가 아닌 자체 media thread에서 호출합니다. libwebrtc 내부의 RemoveSink는 sink_lock_을 획득하여 callback dispatch와의 직렬화를 보장합니다.
Derived 클래스 D : B의 소멸 순서는 ~D → D의 멤버 소멸 → ~B 순입니다. 따라서 derived 상태가 살아있는 동안 반드시 완료되어야 하는 작업은 ~D 안에서 처리해야 합니다. RTCPeerConnection::doClose()는 정상적인 teardown 경로로, 각 source에 requestToEnd()를 호출합니다. 그러나 RealtimeMediaSourceObserver의 preventSourceFromEnding()이 true를 반환하면 requestToEnd()가 차단될 수 있습니다. 이 경우 isProducingData()가 여전히 true인 상태로 소멸에 도달하는 상황이 가능합니다.
Analysis
RealtimeIncomingAudioSourceCocoa가 소멸될 때, 컴파일러는 먼저 derived destructor를 실행한 뒤 derived 데이터 멤버(m_audioBufferList, pixel buffer pool)를 소멸시키고, 마지막으로 base destructor를 호출합니다. WebRTC sink를 제거하는 stop()은 base destructor 안에 위치해 있었습니다. 즉, WebRTC track이 부분적으로 소멸된 subclass를 가리키는 pointer를 아직 보유하고 있는 상태에서, media thread의 OnData 또는 OnFrame callback이 이미 실행 중이거나 새로 진입할 수 있었습니다. 이때 callback은 C++ runtime이 이미 해제한 m_audioBufferList 같은 멤버를 역참조하게 됩니다.
Aaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaa Aa Aaaaaaaaaaaaa Aa Aa Aaaaaaaa Aaa Aaaa Aaaaaa Aa a Aaaa Aaaaa Aaa Aa Aaaaa Aaaaaaa Aaa Aa Aaa Aaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaaaaaaaaaaaaaa Aa Aaaaaaa Aa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaa a Aaa Aaa a Aaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaa Aaa Aaaa Aaa Aaaaaa Aa Aaa Aaaaaaaaa a Aaaaaaa Aaaaaaaaaa Aaa Aaaaa Aa Aaaaaaa Aaa Aaaa Aa Aaaa Aaa Aaaa Aa Aa Aaaaaaaaa Aaaaa Aaaaaa Aaaa Aaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaa Aaaa Aaa Aaaaaa
a Aaaaaaaaaaaaaa Aaaaaa Aaaaaa Aaaaa Aa Aaa Aaaaaa Aaaaaaa Aaaaaaa Aaa Aaaaaaaaaa Aaa Aaaaa Aaaaaaaaa Aaaa Aaaaaaaaaa Aaaa Aaa Aa Aaaaaaa Aaaa Aaaaaaaaaa Aa Aaaaa Aaa Aaaaaaaaa Aa Aaaaaaaa Aaa Aaaa Aa Aaaaaaaaaaaaa Aaaaa Aaaa Aaaaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaa Aa Aa Aaaaaaaaaaa Aaaaaaaa Aaa Aaaaa Aaa a Aaaaa a Aa Aaaaaa Aaaaa Aaaaaaaa Aaaaa Aaa Aaa Aaa Aa Aaaa a Aaaaaa Aaaaaa Aa Aaa Aaa Aaaa Aaa Aaa Aaaaaaaaaaaa Aaaa a Aaaa Aaaaaaaaaa Aaaaaaa Aaa Aaa Aaaaaaaaaa Aaaaaa Aaaaaaaaa Aaa Aaaa Aaaa Aaaa Aa Aaaaa Aa Aaa Aaaaa a Aaaa Aaaaaaaaa Aaaaaaaaaaaa Aaa Aaaaa Aaaaa Aaaaaaaaaaaaaa Aa Aaaa Aaaaaaa Aaaaaaaaaaaa Aaaaa Aaaa Aaaaa
🔒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
더 확인하려면 구독해 주세요
Audit directions
a Aaaa Aaaaaaaa Aaaaaaaaaa Aaaa Aaaaaaa Aaaaa Aaaaaaaaaa Aaaaa Aaaaaaaaaaaa Aaaaaaaaa Aaaaaaaaa Aaa Aaaaaa Aaaaaaaaa Aaaaaa Aaaaaaaaa Aaaaaaaaa Aaaaaaaa Aaa Aaaaaaa Aa Aaaa Aaaaa Aaa Aaaa Aaa Aaaaaaaaaaaa Aaaa Aaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aa Aaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aa Aaaaaaa Aaaaaaaaaaaa Aaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaa Aaaaaaaaaaaaa Aaaaaa
a Aaaaaaa Aaaa Aaaa Aaaaaa Aaaaaaa Aaaaa Aaaaaa Aaaaa Aaaa Aaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaa Aa Aaaa Aaaaaa a Aaaaaaaa Aaaaaaaaa Aaaaaaaaa Aaaa Aa Aaaaaaaaaaa Aaa Aaaaaa Aaaa Aaa Aaa a Aa Aaaaaaaa Aaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaa
a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaa Aaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aa Aaa a Aaa Aaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aa Aa Aaaaaaaaa Aaaaaa Aaaaaaaaa Aaa a Aaa Aaaaaa Aa Aaa Aaaaaaaa Aaa Aaa Aaaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaaa Aa Aaa Aaaa Aaaaaa
a Aaaaaa Aaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaa Aaaaa Aaaaaa Aaaaaaa Aaaaaaaa Aa Aaaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaa Aaaaaaaaaaa Aa Aaaaaaaaa Aaaa Aa Aaaaaaaaa Aaa Aa Aaa Aaaaa Aaaaaa
🔒Four reusable audit patterns identified for cross-thread callback lifetime bugs across the WebRTC media pipeline and related WebCore subsystems, with concrete grep targets
더 확인하려면 구독해 주세요