← All issues

[15] ScrollerMac cross-thread UAF

Severity: High | Component: WebCore macOS scrolling | a926a67

High로 평가된 이유는 이 diff가 매번 재현 가능한 cross-thread UAF를 수정하기 때문입니다. WebCore: Scrolling 스레드에서 실행 중이던 NSAnimation callback이, main thread에서 ScrollerPairMac이 해제한 CheckedPtr<ScrollerMac>를 역참조하고 있었습니다.

ScrollerMacWebScrollbarPartAnimationMacWebScrollerImpDelegateMac에서 CheckedPtr로 참조되는 구조였습니다. 소유권도 없고 cross-thread 동기화도 없는 참조 방식이었습니다. main thread에서 ScrollerPairMac::~ScrollerPairMac이 실행된 이후, scrolling thread의 [setCurrentProgress:] callback이 이미 해제된 메모리를 역참조하게 됩니다.

Source/WebCore/page/scrolling/mac/ScrollerMac.h

-class ScrollerMac final : public CanMakeThreadSafeCheckedPtr<ScrollerMac> {
+class ScrollerMac final : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<ScrollerMac, WTF::DestructionThread::Main> {

Source/WebCore/page/scrolling/mac/ScrollerMac.mm

@interface WebScrollbarPartAnimationMac : NSAnimation {
- CheckedPtr<WebCore::ScrollerMac> _scroller;
+ ThreadSafeWeakPtr<WebCore::ScrollerMac> _scroller;
- (void)setCurrentProgress:(NSAnimationProgress)progress
{
[super setCurrentProgress:progress];
+ RefPtr scroller = _scroller.get();
+ if (!scroller)
+ return;

ScrollerMacThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<ScrollerMac, DestructionThread::Main>으로 변경되었습니다. Delegate들은 ThreadSafeWeakPtr<ScrollerMac>을 보유하며, callback마다 local RefPtr로 승격합니다. ScrollerPairMac은 scroller를 Ref<ScrollerMac>으로 저장합니다. ~ScrollerMac()isMainThread()를 assert합니다. 한편 m_pair에 대한 null 검사가 lastKnownMousePositionInScrollbar, visibilityChanged, updateMinimumKnobLength에 추가되었습니다. 이는 scrolling thread의 RefPtrScrollerPairMac보다 오래 유지될 수 있는 구간을 처리하기 위한 조치입니다.

객체의 lifetime이 다른 스레드에 귀속된 상태에서, callback 실행과 소멸 사이에 동기화 없이 non-owning cross-thread pointer를 사용한 패턴.

ScrollerMac은 macOS WebCore에서 방향별 scrollbar 객체입니다. ScrollerPairMac당 수직과 수평 각각 하나씩 존재합니다. WebScrollbarPartAnimationMacNSAnimation의 서브클래스로, setCurrentProgress:는 AppKit의 display-link에 의해 WebCore: Scrolling 스레드에서 호출됩니다. CheckedPtr<T>CanMakeCheckedPtr과 쌍을 이루어, 역참조 시점에 대상 객체가 살아있는지 assert합니다. 다만 이는 debug 시 UAF 감지 용도일 뿐이며, cross-thread 안전성은 보장하지 않습니다. ThreadSafeWeakPtr::get()RefPtr<T>을 원자적으로 반환하며, DestructionThread::Main은 최종 delete가 main thread에서 실행되도록 라우팅합니다.

CheckedPtr은 "안전한 raw pointer"처럼 보이지만, 안전성 보장은 동일 스레드 내 접근에만 적용됩니다. 스레드를 넘어서면 lifetime 연장도 동기화도 기대할 수 없습니다.

🔒

Detailed cross-thread lifetime analysis covering the race window and reclamation feasibility for this scrolling-thread crash

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

🔒

Four reusable audit patterns identified for cross-thread lifetime bugs in Cocoa-bridging code, with concrete starting points across WebCore scrolling and platform layers

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