[3] Use-after-free of StreamingCompiler::m_ticket across iframe teardown
Severity: High | Component: JavaScriptCore Wasm streaming compiler | acee67e
diff는 raw deferred-work ticket과 raw JSGlobalObject capture를 각각 liveness 검사가 포함된 weak reference로 전환했습니다. 이를 통해 iframe teardown 이후 해제된 realm 범위 상태에 대한 안정적인 역참조 취약점이 수정된 점에서 High로 평가됩니다. 더 강력한 memory-corruption primitive로의 확장은 공격자가 해제된 allocation을 안정적으로 재사용할 수 있어야 하는데, diff에서는 이를 확인할 수 없습니다.
이 패치는 Wasm streaming compiler가 컴파일 완료 전에 종료되는 iframe 내에서 호출될 때 발생하는 UAF를 수정합니다. streaming 컴파일의 완료 결과는 컴파일 시작 시 생성된 lambda에서 처리됩니다. 이 lambda는 globalObject에 대한 raw pointer를 직접 capture하며, streaming compiler를 통해 TicketData에 대한 raw pointer도 간접적으로 보유합니다. lambda가 이 두 객체보다 오래 살아남는 상황이 가능합니다. 컴파일 완료 전에 iframe이 제거되면, iframe의 globalObject가 수집되고 ticket은 취소 및 소멸됩니다. 컴파일이 완료되는 시점에 lambda는 dangling pointer를 역참조하게 됩니다.
Source/JavaScriptCore/wasm/WasmStreamingCompiler.h
- DeferredWorkTimer::Ticket m_ticket;
+ ThreadSafeWeakPtr<DeferredWorkTimer::TicketData> m_ticket;
Source/JavaScriptCore/wasm/WasmStreamingCompiler.cpp
void StreamingCompiler::didComplete()
{
auto result = makeValidationResult(*m_plan);
- auto ticket = std::exchange(m_ticket, nullptr);
+ auto ticket = takeTicketIfActive();
+ if (!ticket)
+ return;
...
+RefPtr<DeferredWorkTimer::TicketData> StreamingCompiler::takeTicketIfActive()
+{
+ auto ticket = m_ticket.get();
+ m_ticket = nullptr;
+ if (!ticket || ticket->isCancelled())
+ return nullptr;
+ return ticket;
+}
+
+JSGlobalObject* StreamingCompiler::globalObjectIfActive()
+{
+ auto ticket = m_ticket.get();
+ if (!ticket || ticket->isCancelled())
+ return nullptr;
+ return uncheckedDowncast<JSGlobalObject>(ticket->dependencies()[0]);
+}
Source/WebCore/bindings/js/JSDOMGlobalObject.cpp
- inputResponse->consumeBodyReceivedByChunk([globalObject, compiler = WTF::move(compiler)](auto&& result) mutable {
- VM& vm = globalObject->vm();
+ inputResponse->consumeBodyReceivedByChunk([vmPtr = &vm, compiler = WTF::move(compiler)](auto&& result) mutable {
+ auto& vm = *vmPtr;
JSLockHolder lock(vm);
+ auto* globalObject = compiler->globalObjectIfActive();
+ if (!globalObject)
+ return;
Patch Details
m_ticket은 raw DeferredWorkTimer::Ticket에서 ThreadSafeWeakPtr<TicketData>로 변경되었습니다. 새로 추가된 takeTicketIfActive()는 weak pointer를 RefPtr로 승격하며, ticket이 소멸되거나 취소된 경우 null을 반환하고 m_ticket을 비웁니다. didComplete(), fail(), cancel()의 호출 지점은 이제 std::exchange(m_ticket, nullptr)를 무조건 실행하는 대신, null 결과를 받으면 조기 반환합니다. 추가된 helper인 globalObjectIfActive()는 ticket의 dependency 목록에서 살아있는 JSGlobalObject*를 조회하도록 설계되었습니다. JSDOMGlobalObject.cpp에서는 consumeBodyReceivedByChunk lambda가 raw globalObject를 직접 capture하는 방식을 중단합니다. 대신 VM*과 compiler만 capture하고, compiler->globalObjectIfActive()를 통해 global object를 다시 조회하도록 변경되었습니다.
iframe teardown 경계를 넘어 소유자보다 오래 살아남은 비동기 소유 객체의 raw pointer를 liveness 재검증 없이 역참조하는 패턴.
Background
WebAssembly.compileStreaming / instantiateStreaming은 fetch Response를 받아 body가 스트리밍되는 동안 모듈을 점진적으로 컴파일합니다. DeferredWorkTimer는 지연된 결과를 JS 스레드로 다시 전달하는 JSC의 메커니즘입니다. addPendingWork는 대상 promise와 그 dependency 객체(요청한 JSGlobalObject 포함)를 살아있게 유지하는 TicketData(typedef명 Ticket)를 반환합니다. ThreadSafeWeakPtr<T>::get()은 객체가 아직 살아있으면 원자적으로 RefPtr<T>로 승격하고, 그렇지 않으면 null을 반환합니다. iframe은 자체 JSGlobalObject를 가진 독립 realm을 구성하며, DOM에서 iframe을 제거하면 해당 global object가 수집 대상이 됩니다. streaming 컴파일은 background worklist에서 실행되므로, 완료 callback은 원래 realm이 소멸된 이후에도 실행될 수 있습니다.
Analysis
비동기 경계를 넘어 소유자가 소멸된 뒤에도 살아남은 dangling raw pointer로 인한 use-after-free입니다. TicketData와 JSGlobalObject의 수명은 모두 요청한 iframe/realm에 묶여 있습니다. 이 상태에서 컴파일 완료 전에 iframe이 제거되면, iframe의 JSGlobalObject는 GC 대상이 되고 deferred-work ticket은 취소 및 소멸됩니다.
Aa Aaaaaaaaaa Aaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaa Aa Aaa Aaaaaaaaaaaaaa Aaaa Aaa Aaaaaa Aaaaa Aaaaaaaaaa Aaaaaaa Aa Aaa Aaaaaa Aaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaa Aaaaaa Aaaaaaaaa Aaaaa Aaaaaaaaa Aaa Aaaaaaaa Aa Aaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaa Aaaaa Aa Aaaaa Aaaaa Aaaa Aaaa Aaaa Aaaaaaa Aaaa Aaaaaaa Aaaa Aaa Aaaaaaaaaaa Aaaaa Aaaa a Aaaa Aaaaaaaa Aaaaaaa Aaa Aaaaaaaa a a Aaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaa Aaaa Aaaaa Aa a Aaa Aaaa Aa Aaaaaa Aaaa Aaaa Aaaaaaaaaaa Aaaaa Aaa Aaaaaaa Aaaaaaa Aaaaaa
a Aaaaaaaaaaaaaa Aaaaaaaaaa Aaaaaaa Aa Aaaaaa Aaaaaaa Aaaaaaa Aaaaaaa Aa Aaa Aaaaaaaaaaaaa Aaa Aa Aaa Aa Aaaaa Aa Aa Aaaaa Aaaa Aaaaaa Aa Aaaa Aaaaaaaa Aa Aaaa Aaa Aaaaaaaaaaaaaa Aa Aaa Aaa Aaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaa Aaa Aa a Aaa Aaaaaaa Aaa Aaaa Aaaaaaaaaaa Aaa Aaa a Aa Aaaaaaaaaaaaaa Aaa Aaaaa a Aaa Aaaaaa a Aaa Aaaaa Aaa Aaa Aaa Aaaa Aaa Aaaa Aaa Aaa Aaaa Aaaa Aa Aaaa Aaaaaa
Aaaaaaaaa Aaaaaa Aaaaaaaa Aaaaaaaa Aaaaaaaa Aa Aaaaaaaa Aaaa Aaaaaaa Aa Aaaa Aa Aa Aaaa Aaa Aaaaa Aa Aaaaaaaa Aaa Aa Aa a Aaaaaaa Aaaa Aaaaaaaaaa Aaaa Aaaaaa Aaaaaaa Aa Aaaa Aa a a Aaaaa Aa Aaa Aaaaaaa
🔒The lifetime and ownership story behind this dangling-pointer bug is traced across the iframe-teardown boundary, with an assessment of how far the crash could be pushed.
더 확인하려면 구독해 주세요
Audit directions
a Aaaaa Aaaaa Aaaaaaaa Aaaaaaaaa Aaa Aaaaaaaaaaa Aa Aaaaaaaaaaaaa Aaa Aaaa Aaa a Aa Aaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaa Aaaaaaaaaaaaa Aaaaaaaa Aaaaaaaaa Aaaa Aaa Aaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaa Aa Aaaaa Aaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaaa Aaa Aa Aaaaaaa Aaaaaaaaaaa Aaaaaaaaaaaaa Aaaa Aaaaaa
a Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaa Aaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaaa Aaaaaa Aaaaaaa Aaa Aaaaaaa Aa Aa Aaaaaaaa Aa Aaa Aa Aaaa Aaaaaaaa Aaa Aaaa Aaaaaa
a Aaaa Aaaa Aaaaaaaaa Aaaaaaaaa Aaaaa Aaaaaaaaaaa Aaaaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaaa Aaaaa Aa Aaaa Aaa a Aaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaa Aaaaaa Aaaaaaaaaaa Aa Aa a Aaaaa Aaa Aaa Aa Aaa Aa Aa Aaaa Aaaaaa
Aa Aa Aaa Aa Aaaaaaaa Aa Aaaa Aaaaaaa a Aa Aa Aa Aaa Aaaaaaaa Aaaaaaaaaaaa Aaaa Aa Aaaaaaaaaaaaaaaaaaa Aaaaaaa Aaa Aaa Aaa Aaaaaaaaaaa Aaa a Aaaaa a Aaaaaaa Aa Aa Aaa Aaaaaa Aaa a Aaaaa Aaaaaaaaaaaaaaaaaaaaaa a Aa Aaaa Aaaaaaaaaa Aaaa Aa Aaaa Aaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaa Aa Aaaaaaaaaa Aaaa Aaaa
🔒Multiple reusable audit patterns identified for finding sibling async-lifetime bugs, with concrete deferred-work and binding-callback starting points.
더 확인하려면 구독해 주세요
Aa Aa Aaa a Aaaaa Aaaaaaaaaaa Aa Aaaaaa Aaa Aaa Aaa Aaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaa Aa Aaaaa Aaaaaa Aaa Aaaaaaaaa Aaa Aaaaa Aaaaaa Aaaaaaaa Aaa Aaa Aa Aaaaaaa Aaaaaaaaa Aaaaaaaa Aaaaaa a Aa Aa Aaa Aaaa Aa Aaaaaaaaaa Aaaa Aaaaa Aaaa a Aa Aaa Aaa Aa Aaaaaaa Aaaa Aa Aaa Aaaa Aaaa Aaaaaaaaaa Aaaaa a Aa Aaa a Aaaaaa Aaaaaaaaaaa Aaaaaaa Aa Aa a Aaa Aaaa Aa Aaaaaaa Aaaaa Aaa Aaaaaaaa Aaa Aaa Aaaaa Aaa Aaaa Aaaaaaaaaaaaaaaaaaaaaa a a Aaaa Aaaaa Aaa Aaaaaaaaaa Aaaaaaaa Aaaa Aa Aa Aaaaaaaaaaa Aaaaaa Aa a Aa Aaaaaaaa Aaaaaaaaaaaaaaaaaa Aaaa Aa Aa Aaaaaaa Aaaaaa Aaa Aaa Aaaaaa Aaa Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaa Aaaaa Aaa Aaaaaa Aa Aaaaa Aaaaaa Aa Aaaaa Aaaaa
Aaaa Aaaaaaaa Aaaaaaaaaa Aaaa Aaaa Aaaaa Aaaaaaa Aaa Aa Aaaaaaaaaaa Aaaaaaa Aaa Aaaaa Aaa a Aaa Aaaaaa a Aa Aaaaaaaaaaaaaaa Aaaaaaaaaa Aa Aaa Aaa Aaaaaaaaaaaaaaaaaaaa Aaaaaa Aaa Aaaa Aaaaa Aa Aaa Aaa Aa Aaaaaaa Aaaa Aa Aaaaaa Aaaaaaaaa Aaa Aaaaa Aaa Aaaa Aaaa Aa Aaa Aaaaaaaaaaa Aa Aaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaa a Aaaaa Aa Aaa Aaaaaaaaaa Aaaaa Aaa Aaaaaaaaa Aaa Aaa Aaaaaa Aaa Aaaa Aaaa Aaaaaaaaa Aaaaa Aaaaaaaa Aa Aaa Aaaaaaaaaa Aaaaaaaa Aa Aaaaaa
a Aaaa Aaaaaaaa Aaa Aaa Aa Aaaa Aaaaaaa Aa Aaa Aaaaaaaaaaaaaaaa Aaaaaaaa Aaaaaaa Aaa a Aaa Aaaaaaaaaaaa Aaaaaaaaaa Aa Aaaa Aaaa Aaa Aaaaa Aaaaaa Aaa Aa Aaaa Aaa Aaaaaaaa Aaaaaaaaaaa Aaaaaaa Aa Aa Aaa Aaaa Aaa a Aa Aaa Aaaaaaaa Aaaaaaaaaaaa Aaaaaaa Aaaaaa Aaa Aa Aaa Aaaaaa Aa Aa Aaa Aaaa Aaaaaaa Aaaaa Aaa a Aa Aaa Aaaaaaa Aaaa Aaaaaa Aaa Aaaaaaaaaaaa Aaaa Aa Aaaa Aa Aa Aaa Aaaa Aa Aaaa Aaaaaa Aaa Aaa Aaaaaa Aaa Aaaa Aaaa Aaa Aaaa Aaaa
Aaaaaaaaa Aaa Aaaaaaaaa Aaaaaaaaaa Aaaaa Aaa Aaaaaa Aaaa Aaaaaaaa Aaaa Aaa Aaaaaa Aaaa Aa Aaa Aaaa Aaaaaa Aa Aaaaa Aaaaa Aa Aa Aaaaaaa a Aa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaa a Aaaa Aaaaa Aaaa Aaaaaa Aaa Aaaaaaa
🔒The lifetime and ownership story behind this dangling-pointer bug is traced across the iframe-teardown boundary, with an assessment of how far the crash could be pushed.
더 확인하려면 구독해 주세요
Audit directions
a Aaaa Aaaaaaaaaaa Aa Aaa Aaaa Aaaaaaaaa Aaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaa a Aa Aaaaaaaaaaa Aaa Aaa a Aaaa Aaaaaaaaa Aaa Aaaa Aaaaaa Aaa Aaaa Aaaa Aaa a Aa Aaa Aaaaa Aa Aaa Aaaa Aa Aaaaaaaaaaaaaaaaaa Aaaa Aaaaa Aaaaaaaaa Aa Aa Aaa Aaa Aaa Aaaaa Aaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaa Aa Aa Aaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aa Aaaa Aaaa
a Aaaa Aa Aaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaa Aaaaaa Aaaaaaa a Aaaa Aa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaaaa Aaa Aaaaaaaaaaa a Aa Aaa Aaaaaaaaaa Aaaaaaaaaa Aaaaaaaaaa Aaaaa Aaaa Aaaa Aaaaa Aaaaaa Aaa Aaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaa a Aaaaaaaaa Aaaa Aaaaaa Aaaaaaa
a Aaaa Aaaaaaaaaaa Aaa Aaaa Aa Aaa Aaaaaaaaa Aaaaaaaaaaaaa Aaaaaaaaaaaaaaaa Aa Aaa a Aaa Aa Aaaaaaa Aaa Aaaa Aaaa Aaaaa Aaaa Aaaa Aa Aaa Aa Aaaaaaaaaa Aaa Aaaaa Aaaaaaa Aaaaaaaa Aaa Aaa Aa Aaa Aaa Aaa Aaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaa Aa Aaa Aaaa Aaaa
🔒Multiple reusable audit patterns identified for finding sibling async-lifetime bugs, with concrete deferred-work and binding-callback starting points.
더 확인하려면 구독해 주세요