[10] WGSL GlobalVariableRewriter iterator-invalidation UAF
Severity: Medium | Component: WebGPU WGSL compiler — GlobalVariableRewriter | efa18a8
WGSL 컴파일 중 HashMap backing storage에서 use-after-free가 발생하며, WebGPU API를 통해 web content에서 도달 가능합니다. 다만 exploit을 위해서는 rehash 시점의 heap 상태를 제어해야 하는데, HashMap 자체의 재할당 동작 특성상 이를 통제하는 것은 간단하지 않습니다. 이러한 이유로 Medium으로 평가되었습니다.
GlobalVariableRewriter에서 iteration 중에 HashMap이 변경되는 문제가 발생했습니다. callee에서 caller로 length parameter 목록을 전파하는 과정에서 iteration 도중 HashMap을 직접 변경하고 있었습니다. 패치는 삽입 작업을 별도의 vector로 지연시키는 방식으로 수정되었습니다.
Source/WebGPU/WGSL/GlobalVariableRewriter.cpp
+ Vector<std::pair<AST::Function*, String>> pendingLengthParameters;
for (auto& lengthParameter : it->value) {
...
for (auto& [caller, call] : callee.callSites) {
...
- auto result = m_lengthParameters.add(caller, ListHashSet<String> { });
- result.iterator->value.add(identifier);
+ pendingLengthParameters.append({ caller, identifier });
...
}
}
+
+ for (auto& [caller, lengthParameter] : pendingLengthParameters) {
+ auto result = m_lengthParameters.add(caller, ListHashSet<String> { });
+ result.iterator->value.add(lengthParameter);
+ }
Tools/TestWebKitAPI/Tests/WGSL/shaders/fuzz-166715941.wgsl
+fn fn0(a0: ptr<storage, array<vec4i>, read_write>) {
+ var a = a0[0];
+}
+fn fn1() { fn0(&buffer32); }
+fn fn2() { fn1(); fn0(&buffer32); }
+fn fn3() { fn2(); fn0(&buffer32); }
+fn fn4(a0: ptr<storage, array<i32>, read>) {
+ var a = buffer32[0];
+ var b = a0[0];
+}
+@compute @workgroup_size(3, 1, 1)
+fn fn5() { fn4(&buffer36); fn3(); }
Patch Details
RewriteGlobalVariables::visitCallee()에서 m_lengthParameters HashMap에 대한 변경 작업이 iteration 완료 후로 지연되도록 수정되었습니다. 기존 코드에서는 it->value를 순회하는 루프 내부에서 m_lengthParameters.add(caller, ...)를 호출하고 있었습니다. 이 과정에서 새로운 key가 삽입되어 rehash가 유발될 수 있었고, 결과적으로 외부 iterator가 무효화되는 문제가 발생했습니다.
compiler pass의 iteration 중 삽입으로 인한 HashMap iterator 무효화 패턴.
Background
WTF HashMap은 WebKit의 hash map 구현체입니다. 새로운 key를 삽입하면 backing store를 더 큰 버퍼로 재할당하는 rehash가 유발될 수 있으며, 이 과정에서 기존 버퍼가 해제되고 모든 iterator가 무효화됩니다.
WGSL GlobalVariableRewriter는 Metal 코드 생성을 위해 WGSL의 global variable 접근을 명시적인 함수 parameter로 변환하는 compiler pass입니다. call graph를 bottom-up 방식으로 순회하며, 각 함수에 필요한 global 변수와 해당 배열의 길이를 파악합니다. 그런 다음 이를 call site에 추가 인자로 삽입하는 방식으로 전파합니다.
WGSL의 arrayLength()는 storage buffer 내 runtime-sized 배열의 크기를 런타임에 반환하는 built-in 함수입니다. 컴파일러는 버퍼의 byte 길이를 call chain 전체에 걸쳐 추가 parameter로 전달해야 하는데, 이 정보가 m_lengthParameters에서 관리됩니다.
Analysis
패치 이전에는 visitCallee()가 m_lengthParameters HashMap 내 iterator it를 통해 m_lengthParameters[callee.target]를 순회하고 있었습니다. 이는 fix 패턴과 데이터 타입으로부터 강하게 추론됩니다. 이 루프 내부에서는 각 caller의 call site마다 m_lengthParameters.add(caller, ...)를 호출하여 length parameter를 call graph 상위로 전파하고 있었습니다.
WTF HashMap에 새로운 key를 삽입하면 rehash가 유발될 수 있습니다. rehash가 발생하면 backing store가 재할당되고 기존 iterator가 모두 무효화되는데, 외부 루프에서 여전히 사용 중인 it도 여기에 포함됩니다. rehash 이후 외부 루프는 이미 해제된 backing store를 가리키는 dangling iterator를 역참조하게 됩니다.
Aaaaaa Aaaa Aaaaa a Aaa Aaaa a Aaa Aaaa Aaaaa Aaa a Aaaaaa Aaaaaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaa Aaaa Aaaa Aaaaaaaaaaa a Aaaaa a Aaaaaa Aa Aaaaaaaaaaa Aaaa Aa Aaaaaa Aa Aaaaaaa Aa a Aaaaaaaa Aaaa Aaaaaaa Aaaaaa Aaaaaaaaaa Aaaaa Aa Aaaaaaaaa Aaaaa Aaa Aa Aaa Aaa Aaaa Aaaa Aaaa
a Aaa Aaa Aaaa Aaaaaaa Aaaaaa Aaaa Aa Aaaa Aaaa Aaa Aaaaaaaaa Aa Aaaaaa Aaaaaaaa Aaaa Aaaaaa Aaa Aaaa Aaa Aaaaa Aaa Aaaaaaa Aaaaaa Aaaa Aaa Aa Aaaa Aaaaa Aa Aaaa Aaaaa a Aaaa Aaaaaaaa Aaaaaaaa Aaaaaaaaa Aaaaaa Aaaa Aaaa Aaaaa Aaaaa Aaaaa Aaaaa Aaaaaaa Aaa Aaaa Aaa Aaa Aa Aaaa a Aa Aaaaaa
a Aaaaaaaaaaaaaa Aaaa Aaaaaaa Aaaaa Aaaaaaa Aa Aaaaaa Aaaaaaa Aaa Aaaaa Aaa Aaaa Aaaaa Aaa Aa Aaaa Aaaaaaa Aaa Aaaa Aaaaaaaaa Aa Aaaaaaa Aaaaaaa Aaaa Aaaaa Aaa Aaaaa Aaa Aa a Aaaaa Aaaa Aaa Aa Aaa Aaa Aaaaaa a Aaa Aaa Aaaaa Aaaaaa Aa Aaaaa Aaaaaa Aaaa Aaaaaaa Aaa Aa Aaa Aaaaaa Aaaaaaaaaa Aaaaaaaaaa Aaa Aaaa Aaaaa
Aaaa Aaaaaa Aa Aaa Aaaa Aaaa Aaaaaaaa Aaaaa Aaaaa a Aaa Aaa Aaaaaa Aa Aaaa Aaaaa Aa Aa Aaa Aaa Aaa Aaaaa Aaa Aa Aaaaaa
Aaaaaaaaa Aaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaa Aaaaa Aa Aaaaaaaaaaaaaaa Aaa Aaaaaa Aaa Aaa Aaa Aaaa Aaaa Aaa Aaaaaaaaaaaaa Aaa Aaaaaa Aaaaaaaa Aaaa Aa Aaaaa Aaa Aa Aa Aaaaaaa
🔒Explores the memory corruption mechanism and whether heap conditions could escalate beyond a crash
더 확인하려면 구독해 주세요
Audit directions
a Aaaaaa Aaaaaa Aa Aaa Aaaa Aaaaaaaa Aaaaaa Aaaaaaaaa a Aaaaaaa Aa Aaaaaaaa Aaaa Aaaaa Aaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aa Aaaa Aaaaa Aaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaa Aa Aaaa Aaaaaaaa Aaaa a Aaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aa Aaa Aaaa Aaa Aaaa Aaaa Aaa Aaaaaaaa Aa Aaaa Aa Aaa Aaaaaaaa Aaaaaaa Aaa Aaaa Aaa Aaaaa
a Aaaaaa Aaaa Aaaaaaa Aa Aaa Aaaaaaaaaa Aaaaaaaa Aaaaaa Aa Aaaaaaaaa Aaaaaa Aa Aaa Aaaaaaaa Aa a Aaaaaaaa Aaaa Aaaa Aaaaa Aa Aaa Aaaa Aaa Aaaa Aaaaa Aaaaaaaa Aaaa Aaaaaa Aaa Aaaaa Aaaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aa Aaaaa a Aaaaa Aaaaaaaa Aaaaa Aaaa Aaaaaa Aa Aaa Aaaaaaaaa Aaaa Aaa Aa Aaaaaa
a Aaaaaaa Aaaaaaa Aaaa Aa Aaaa Aaa Aaaa Aaa Aaaaa Aaa Aaaaaa Aaaaaaaaa Aaaa Aa Aaaaa Aaa Aaaaaa Aa Aaa Aaaaa Aaa Aaaaa Aaaaaaa Aaaaaaaaaaaaa Aaa Aaaaaa Aaaaaa Aaa a Aaaaa a Aa Aaa Aaa Aaa Aaaa Aa Aaaaaa Aaa Aaaa Aaaaa
🔒Multiple audit patterns identified for similar iterator-invalidation bugs across WebKit compiler passes
더 확인하려면 구독해 주세요