← All issues

[19] PlatformScreen data-race / GPUCanvasContextCocoa worker race

Severity: Medium | Component: WebCore PlatformScreen | 08911bd, cf20d12

두 diff가 프로세스 전역 ScreenProperties singleton의 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

-static ScreenProperties& NODELETE screenProperties()
-{
- ASSERT(isMainThread());
- static NeverDestroyed<ScreenProperties> screenProperties;
- return screenProperties;
-}
+Ref<const PlatformScreen> PlatformScreen::singleton()
+{
+ Locker locker { platformScreenLock() };
+ return instance().get();
+}
+void PlatformScreen::updateSingletonProperties(ScreenProperties&& properties)
+{
+ Locker locker { platformScreenLock() };
+ Ref<PlatformScreen>& platformScreenRef = PlatformScreen::instance();
+ if (platformScreenRef->hasOneRef())
+ platformScreenRef->m_properties = WTF::move(properties);
+ else
+ platformScreenRef = PlatformScreen::create(WTF::move(properties));
+}

독립 함수인 screenData(), getScreenProperties(), primaryScreenDisplayID(), setScreenProperties()는 모두 제거되었습니다. PlatformScreenThreadSafeRefCounted 클래스로 변경되었습니다. 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.

PlatformScreenPlatformDisplayID를 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가 무효화됩니다.

🔒

The threading and ownership model behind this singleton rewrite is dissected, including how the copy-on-write fast path interacts with concurrent readers.

더 확인하려면 구독해 주세요

🔒

Four reusable audit patterns covering singleton caches, escaped HashMap pointers, IPC-driven mutation, and copy-on-write fast paths.

더 확인하려면 구독해 주세요