[1] Unsafe argument capture in EXTDisjointTimerQueryWebGL2::queryCounterEXT()
Severity: High | Component: WebGL — EXTDisjointTimerQueryWebGL2 | 84075f4
WebGL 2 context를 가진 임의의 웹 페이지에서 WebGLQuery 객체에 대한 use-after-free가 발생한다는 점이 High로 평가된 근거입니다. Lambda 생성 시점부터 GC에 의한 파괴 시점까지의 타이밍 window는 공격자가 제어할 수 있습니다. Controlled UAF primitive로의 확장은 heap grooming 가능성에 달려 있으나, 이 부분은 검증되지 않았습니다. Dangling reference 경로 자체는 diff를 통해 신뢰도 0.95로 확인됩니다.
queueMicrotask에 전달된 lambda가 WebGLQuery& query 파라미터를 참조로 캡처했습니다. microtask는 비동기로 실행되기 때문에, 실행 전에 query 객체가 파괴될 가능성이 있었습니다. 수정 방법은 캡처 대상을 Ref smart pointer로 감싸는 것이었습니다.
Source/WebCore/html/canvas/EXTDisjointTimerQueryWebGL2.cpp
- protect(protect(context->scriptExecutionContext())->eventLoop())->queueMicrotask(protect(context->scriptExecutionContext())->vm(), [&] {
- query.makeResultAvailable();
+ protect(protect(context->scriptExecutionContext())->eventLoop())->queueMicrotask(protect(context->scriptExecutionContext())->vm(), [query = Ref { query }] {
+ query->makeResultAvailable();
});
Patch Details
이번 패치는 EXTDisjointTimerQueryWebGL2::queryCounterEXT() 내의 단일 호출 지점을 수정합니다. Lambda의 캡처 목록이 [&](모두 참조로 캡처)에서 [query = Ref { query }](reference-counted smart pointer로 캡처)로 변경되었습니다. Lambda 본문에서는 query.makeResultAvailable()이 smart pointer 역참조 문법에 맞게 query->makeResultAvailable()로 바뀌었습니다. 그 외 로직 변경은 없으며, 수정 내용은 전적으로 캡처 방식에 한정됩니다.
비동기 task에 전달된 ref-counted 객체를 참조로 캡처한 lambda에서 발생한 dangling reference.
Background
EXT_disjoint_timer_query_webgl2 extension은 WebGL 2 성능 측정을 위한 GPU timestamp query 기능을 제공합니다. JavaScript에서 queryCounterEXT()를 호출하면, 현재 GPU 작업이 완료된 이후 query 결과를 available로 표시하기 위한 microtask가 예약됩니다.
queueMicrotask는 현재 JavaScript 실행이 완료된 직후, event loop로 제어가 돌아가기 전에 lambda를 비동기적으로 실행하도록 예약합니다. 이로 인해 lambda가 생성되는 시점과 실제로 실행되는 시점 사이에 시간적 간격이 생깁니다.
WebGLQuery는 GPU query를 나타내는 ref-counted WebGL 객체입니다. JavaScript 측에서 모든 참조를 제거하고 garbage collection이 실행되면 파괴될 수 있습니다. 한편 Ref는 WebKit의 intrusive reference-counting smart pointer입니다. 참조로부터 Ref를 생성하면 refcount가 증가하고, Ref가 유지되는 동안은 객체가 파괴되지 않습니다. queryCounterEXT의 주변 코드에서는 execution context, event loop, GL 객체에 대해 이미 protect() wrapper를 사용하고 있었습니다. 그러나 query 파라미터 자체는 일반 C++ 참조로 캡처되고 있었습니다.
Analysis
Root cause는 동기적 scope를 넘어 실행되는 lambda 안에서 ref-counted 객체를 참조로 캡처한 것입니다. 수정 전에는 queryCounterEXT가 WebGLQuery& query를 파라미터로 받고, queueMicrotask에 전달된 lambda 내에서 [&]를 통해 이를 캡처했습니다. microtask는 비동기로 실행되기 때문에, lambda는 WebGLQuery 객체의 reference count를 유지하지 않습니다. queryCounterEXT 호출과 microtask 실행 사이에 JavaScript가 query 객체에 대한 모든 참조를 제거하고 garbage collection이 트리거되면, 해당 객체는 파괴됩니다. 이후 microtask가 실행되면 해제된 메모리에 대해 query.makeResultAvailable()이 호출됩니다. 이것이 use-after-free입니다.
이 취약점은 코드 리뷰를 통해 발견할 수 있습니다. 비동기 API에 전달된 lambda에서의 [&] 캡처 패턴은 잘 알려진 C++ antipattern으로, async scheduling 호출 지점의 캡처 목록을 검사하면 기계적으로 찾아낼 수 있습니다.
Aaaaaaa Aaa Aaa Aaaaa Aaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaa Aaa a Aa Aa Aa Aaa Aa Aaaaa Aaaaaaaaaa Aaaa Aa Aaa Aaaaaaa Aaaaaaaaaa Aaaa Aaaaaaaa Aaaaaaaaaa Aa Aaa Aaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaa Aaa Aaaa Aaa Aaa Aaaaaaaaaa Aaaaaaaaaaaa Aaaa a Aaaa Aa Aaa Aaa Aaaa Aaaa Aaaa Aa Aaaa Aaaa Aaa Aaaa Aaa a a Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaa Aa Aaa Aaa Aaaa Aaaaa a Aa Aaaa Aaa Aa Aaaaa Aaaaaaaaaa Aaa Aaaa Aaaaa
a Aaaa Aaaaa a Aaaaa Aaaaa Aaaaaaaaaa Aa Aaa Aaaaaaaaa Aa Aaa Aaaaaaaaaaa Aaaaaaa Aaaa Aaa Aaaaaaa Aaa a Aaaa Aaa Aaaa Aaaaa Aaa Aaaa a Aaaaa Aaaaa Aaa Aaa Aaaaaaaaaa Aaaaa Aaa Aaa Aaaaa Aa Aaaa Aaaaaaaaa Aaaaaa
a Aaaa Aaaaaaaaaa Aaaaaaaa Aaaaaaaa Aaa Aaaa Aaaaaaa Aaaaa Aaaaa Aaaaa Aaaa Aaaaa a Aaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaa Aaa a Aaaaa Aa Aaaaaa Aaaaaaaaaaaaaaaaa Aaa Aaaaaaaaa Aa Aaa Aaaaaaaaaaaa Aaa Aaa Aaa Aaaaa Aaaaaaaa Aaaaaaaaaa Aa Aaa Aaaa Aaaa a Aaaa Aaaaa Aa Aaaaaaaa Aaaaaaa Aaaa Aaaa Aaaaaaaaaaa Aaa Aaaa Aaaaa Aaa Aaa Aaa Aaaa Aaa Aaaaaaa Aaaaaaa Aaaaaa
Aa Aaaa Aaaaa Aaaaaa Aa Aaa Aaaa Aaaaaa Aa Aaaaa Aaaaaaaa Aaaaa Aaaaa Aa Aaa Aa Aaaaaaaaaaa Aaaaaaaa Aaaaa Aaaa Aaaaaa Aaa Aaaaa Aaaa Aaaa Aaaa Aaaaa Aa Aaaa Aaaaaa Aaaaaaaaa Aa Aaa Aaaa Aaaa Aaaa Aaaaaa
Aaaaaaaaa Aa Aa Aaaaa Aaaaaa Aa Aaa Aa Aaa Aaa Aaa Aa Aaa Aaa Aa Aaa Aaaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaaaa Aaaa Aaaa Aa Aaa Aaaaa Aaa Aaa Aaaaa a Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaa Aaaaa Aaa Aa Aaaaaa Aa Aa Aaa Aaaaa Aa Aaa Aaa Aa Aaaa Aaaaaa
🔒Explores the exploitation window and heap reclamation feasibility for this asynchronous lifetime bug
더 확인하려면 구독해 주세요
Audit directions
a Aaaaaaaaaa Aaa Aaaaaaaaaa Aaaa Aaa Aaaaaaaa Aaaaaaaaaaa Aaa Aaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaaaaaa Aaaaa Aaaaaaaaaa Aaa Aaa Aaaaa Aaaa Aa Aaa Aaa Aa Aaa Aaaaaaa Aaaaaa Aaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaaaaaaaaaa Aaaaaaaaaaa Aaa Aaaaaaa Aaaaa Aa Aaaa Aa Aaa Aaaaaaa Aaa Aaa Aaa Aaaa Aa Aaaaa a Aa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aa Aaaaa
a Aaaaaaaaaa Aaaaa Aa Aaaaaa Aaa Aa Aaaaaaaa Aaa Aaaa Aaaaa Aaaaaaaaa Aaaaaa Aaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaa Aaaa Aaaaa Aa Aaaaaa Aaa Aa Aaa Aa Aaaaaa Aaaaa Aaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaa Aaa Aaaa Aaaaaaaa Aaa Aaa Aaaaaaa
a Aaaaaaaaa Aaa Aaaaaaaaaaaaaaaa Aa Aaa Aaaaaa Aaaaa Aa Aaa Aaa Aaa Aaaa Aaa Aaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaa Aaaaa Aaaa a Aa Aaaaa Aa Aa Aaa Aaaa Aaaa Aaaaaa Aaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaa Aaa a a Aaaaaaa Aa Aaa Aaaaaaa
🔒Multiple audit patterns identified for similar async capture bugs across WebGL and WebCore scheduling APIs
더 확인하려면 구독해 주세요