[19] PlatformScreen data-race / GPUCanvasContextCocoa worker race
두 diff가 프로세스 전역
ScreenPropertiessingleton의 concurrent HashMap 접근을 수정한다는 점에서 Medium으로 평가되었습니다. Worker thread의 reader가screenDataMap.values()를 순회하는 동안 main thread가 rehash를 수행했고, ARM 하드웨어에서 MTE tag-mismatch crash로 드러났습니다.
동일한 root cause에서 비롯된 두 가지 fix가 함께 발견되었습니다. 첫 번째(cf20d12)는 기존 screenProperties() accessor에 ASSERT(isMainThread())를 추가하고, off-main-thread의 GPUCanvasContextCocoa read를 callOnMainThread + BinarySemaphore를 통해 처리하도록 변경되었습니다. 두 번째(08911bd)는 구조적인 수정입니다. NeverDestroyed<ScreenProperties> accessor를 ThreadSafeRefCounted<PlatformScreen> singleton으로 교체하고, Ref<const PlatformScreen>을 통해 접근하도록 변경되었습니다. platformScreenLock() 하에서 copy-on-write 방식이 적용되었습니다.
Source/WebCore/platform/PlatformScreen.cpp
Patch Details
독립 함수인 screenData(), getScreenProperties(), primaryScreenDisplayID(), setScreenProperties()는 모두 제거되었습니다. PlatformScreen은 ThreadSafeRefCounted 클래스로 변경되었습니다. Concurrent reader가 존재하는 경우, writer는 slot에 새 인스턴스를 교체합니다. Reader는 Ref<const PlatformScreen> snapshot을 생성하고 refcount를 통해 이를 유지합니다. 모든 호출 지점(HTMLMediaElement, GPUCanvasContextCocoa, VP9/GStreamer 스캐너, PlatformScreen{Mac,iOS,GTK,WPE})은 PlatformScreen::singleton()->... 방식으로 마이그레이션되었습니다.
Singleton HashMap의 backing storage를 가리키는 pointer와 iterator를 reader가 외부로 노출하는 동안, 다른 thread가 동일한 HashMap을 변경하면서 발생하는 data race / use-after-free.
PlatformScreen은 PlatformDisplayID를 key로 하여 디스플레이별 상태(직사각형 영역, EDR headroom, color space)를 캐싱하는 프로세스별 캐시입니다. UIProcess는 WebProcess::setScreenProperties를 통해 업데이트를 전달하는데, 이전에는 file-scope setter를 호출하여 singleton을 덮어쓰는 방식이었습니다. WTF HashMap은 thread-safe하지 않습니다. Concurrent insert 또는 operator=가 발생하면 bucket array가 rehash되어 기존 배열이 해제되고, concurrent reader가 보유한 iterator가 무효화됩니다.
이와 같은 취약 패턴은 반복적으로 등장합니다. 원래 main thread 전용으로 설계된 프로세스별 캐시에 새로운 서브시스템(media pipeline, WebGPU, HDR observer)이 연결되면서 off-main-thread 호출자가 추가됩니다. 이때 invariant를 유일하게 추적하는 수단은 ASSERT(isMainThread())뿐입니다. 이 assertion이 강제되지 않고 틀린 상태로 남아있으면, 조용한 data race가 발생합니다. 여기서는 Apple silicon 하드웨어에서 MTE tag-check fault로 드러났습니다. ThreadSafeRefCounted + COW 패턴은 이 유형의 문제에 대한 표준적인 해결책으로 자리잡고 있습니다.
이 vulnerability는 프로세스별 display-properties 캐시가 single-thread 접근을 보장한다는 memory-safety invariant를 약화시킵니다. WebGPU/OffscreenCanvas 기반의 worker가 screen state를 읽는 도중 screen-properties 업데이트와 race condition이 발생하면, renderer process에서 memory corruption primitive를 유발할 가능성이 있습니다.
Audit directions
NeverDestroyed<HashMap>singletons guarded only byASSERT(isMainThread()).Source/WebCore/platform/와page/디렉터리에서static NeverDestroyed<+ non-atomic 컨테이너를 검색하고, OffscreenCanvas 도달 가능 여부를 호출 지점에서 추적합니다.- Returning raw pointers from
HashMap::find()/begin()/values()across API boundaries. WebGPU / WebAudio / Worklet 컨텍스트에서find\(.*\)\s*;\s*return &.*->value및.values\(\)순회 패턴을 검색합니다. - IPC-driven cache updates mutating singletons while off-main subsystems read.
NeverDestroyedsingleton을 재할당하는WebProcess::set*handler를 점검합니다. - Copy-on-write
updateSingletonPropertiespatterns.hasOneRef()+ in-place mutation이 lock 하에서 실제로 안전한지 확인합니다. check와 mutation 사이에서 다른 thread가 Ref를 해제하고 새로운 read를 시작하는 경우가 없어야 합니다.