[23] XMLHttpRequest GC-thread UAF on m_responseDocument
JSC GC marking thread가 동기화 없이
m_responseDocument를 역참조하는 동안, main thread에서responseXML()/clearResponseBuffers()를 통해 이를 null로 설정할 수 있는 cross-thread race를 수정합니다. Medium으로 평가된 이유입니다.
JSXMLHttpRequest::visitAdditionalChildrenInGCThread는 동기화 없이 wrapped().optionalResponseXML()과 optionalUpload()를 읽습니다. 이때 main thread에서는 m_responseDocument = nullptr;을 수행할 수 있는데, 이는 기존 reference를 해제하는 RefPtr 재할당에 해당합니다.
Source/WebCore/xml/XMLHttpRequest.h
Main thread가 barrier 없이 null로 설정하거나 재할당할 수 있는 refcounted DOM pointer를, GC thread가 동기화 없이 읽는 패턴입니다. 이로 인해 JS opaque root에서 data-race UAF가 발생합니다.
두 멤버에 접근하는 main thread의 모든 경로는 Locker { m_gcLock }으로 보호되어 있습니다. 아울러 더 이상 사용되지 않는 인라인 getter인 optionalResponseXML()과 optionalUpload()는 헤더에서 제거되었습니다.
GC thread가 null이 아닌 Document*를 로드한 직후, main thread에서 마지막 Ref가 0이 되면 해당 Document가 소멸됩니다. 이미 해제된 상태에서 GC thread가 addWebCoreOpaqueRoot(visitor, *document)를 호출하면, marking 대상 객체에 UAF가 발생합니다. 이런 패턴은 반복적으로 나타납니다. GC-thread visitor를 통해 opaque root를 노출하는 wrapper라면, 반드시 불변 멤버를 사용하거나 lock으로 접근을 보호해야 합니다.
이 vulnerability는 GC marking과 main thread DOM mutation 사이의 thread-safety invariant를 깨뜨림으로써, WebContent renderer 내부의 memory safety를 약화시킵니다.
Audit directions
- Other
visitAdditionalChildrenInGCThread/isReachableFromOpaqueRootsoverrides.Source/WebCore/bindings/js를 검색하여, 각 override에서 역참조하는 멤버가 불변이거나 lock으로 보호되어 있는지, 또는 main thread에서만 수정되는지 확인합니다.fetch,EventSource,MessagePort,MediaSource,FileReader부터 살펴보는 것이 좋습니다. const std::unique_ptr<T>/const RefPtr<T>members. GC visitor를 구현하는 클래스에서lazyInitialize(를 검색합니다.addWebCoreOpaqueRoot(visitor, *ptr)patterns. pointer의 읽기와 역참조가 writer를 직렬화하는 동일한 lock 안에서 수행되는지 확인합니다.- Lock ordering with
ScriptExecutionContext/loader locks.XMLHttpRequest::createRequest,responseXML,didSendData,dispatchErrorEvents에서 중첩 lock의 순서 invariant를 검토합니다.