[3] DFG MapIterationEntryKey Type Confusion
Severity: Critical | Component: JSC DFG JIT — DFGNodeType.h | 3f6f783
관찰 가능한 효과가 DFG type confusion으로, object key가 integer semantics로 잘못 표현됩니다. 첨부된 test case는 heap spray를 통한 GC-assisted object aliasing이라는 거의 완성된 exploitation sequence를 시연합니다. 단일 토큰 수준의 root cause와 PoC 수준의 regression test를 근거로 confidence 0.88로 Critical이 산출되었습니다.
MapIterationEntryKey는 임의의 JSValue를 반환합니다. Map의 key는 어떤 타입도 될 수 있기 때문입니다. 따라서 MapIterationEntryValue와 맞추어 NodeResultJS로 선언되어야 합니다.
Source/JavaScriptCore/dfg/DFGNodeType.h
macro(MapIterationEntry, NodeResultJS) \
- macro(MapIterationEntryKey, NodeResultInt32) \
+ macro(MapIterationEntryKey, NodeResultJS) \
macro(MapIterationEntryValue, NodeResultJS) \
JSTests/stress/map-forEach.js
+ function inlinee(value, key) {
+ object.key = key;
+ }
+
+ for (let i = 0; i < 200; i++) {
+ map.forEach(inlinee);
+ }
+
+ for (let i = 0; i < 100; i++) {
+ startScan();
+ (new Map([[{0: 1.1}, 1]])).forEach(inlinee);
+ const spray = [];
+ for (let j = 0; j < 1000; j++) {
+ spray.push({0: 2.2});
+ }
+ if (object.key[0] === 2.2) {
+ throw new Error("bad");
+ }
+ }
임의 타입의 JSValue 출력에 대해 JIT가 integer representation으로 speculation하도록 유발하는, 잘못된 DFG node result-type 선언.
Patch Details
수정 내용은 DFGNodeType.h의 FOR_EACH_DFG_OP 매크로 테이블에서 단일 토큰을 변경한 것입니다. MapIterationEntryKey가 NodeResultInt32에서 NodeResultJS로 변경되었습니다. 함께 추가된 regression test는 object key를 이용한 Map.forEach로 버그를 재현합니다. hot loop를 통해 DFG 컴파일을 유발한 뒤, heap spray로 type confusion이 실제 object aliasing을 만들어냄을 확인합니다.
Background
JSC의 DFG JIT는 FOR_EACH_DFG_OP 매크로로 구성된 node 타입 테이블을 사용합니다. 각 IR node는 NodeResultInt32, NodeResultJS, NodeResultDouble 같은 플래그로 결과 타입을 선언합니다. 이 플래그들은 DFG 파이프라인 전반에 걸쳐 prediction 전파, 레지스터 할당, representation 선택(boxed vs. unboxed), write barrier 생성을 결정합니다. NodeResultInt32는 해당 node가 항상 unboxed 32-bit integer를 생성한다는 것을 컴파일러에 알립니다. 이 선언에 의해 downstream node들은 tag를 제거하고 raw 32-bit payload를 직접 처리합니다.
JSC는 64-bit 플랫폼에서 JSValue를 인코딩하기 위해 NaN-boxing을 사용합니다. object pointer, double, integer 모두 tag bit로 구분되는 64-bit 표현을 공유합니다. DFG가 int32 speculation 하에 코드를 컴파일할 때는 tag를 제거하고 raw 32-bit payload를 처리하며, downstream에서 boxed JSValue가 필요하면 다시 tagging합니다.
JavaScript의 Map은 object, string, number, symbol 등 모든 타입을 key로 가질 수 있습니다. Map.prototype.forEach는 각 entry를 순회하며 (value, key, map)을 callback에 전달합니다. DFG가 이 callback을 inline 처리할 때는 MapIterationEntryKey와 MapIterationEntryValue node를 생성하여 각 entry에서 key와 value를 추출합니다. MapIterationEntryValue는 이미 NodeResultJS로 올바르게 선언되어 있었습니다.
Analysis
root cause는 DFG node 타입 테이블의 플래그 하나가 잘못된 것입니다. MapIterationEntryKey가 NodeResultInt32로 선언되어 결과가 항상 unboxed 32-bit integer라고 명시되어 있었습니다. 실제로 Map의 key는 임의의 JSValue입니다.
DFG 컴파일러가 Map.forEach callback을 처리하면서 iteration key를 만나면, NodeResultInt32 선언을 바탕으로 prediction 전파와 코드 생성을 진행합니다. 해당 key가 실제로는 object pointer(NaN-boxing에서의 64-bit tagged JSValue)임에도, DFG는 이를 integer representation semantics로 처리하는 코드를 생성합니다.
이 잘못된 표현은 DFG 파이프라인 전반에서 object pointer가 잘못된 타입 가정으로 처리되는 결과를 낳습니다. 값이 잘려나가거나 잘못 tagging되거나, GC write barrier가 누락될 가능성이 있습니다. 다만 잘못된 representation이 collector로부터 pointer를 숨겨 GC가 해당 reference를 제대로 추적하지 못하는 경우, 원래의 key object가 수집되는 동안 stale pointer가 남아 use-after-free로 이어질 가능성이 있습니다.
MapIterationEntryKey(Int32)와 MapIterationEntryValue(JS) 사이의 비대칭은 copy-paste 실수이거나, map key를 임의의 값이 아닌 integer 인덱스로 가정한 결과일 가능성이 높습니다. FOR_EACH_DFG_OP 테이블은 DFG 파이프라인 전체에서 node 결과 타입의 단일 진실 공급원입니다. 이 테이블의 오류는 prediction, speculation, 레지스터 할당, write barrier 생성 전반에 자동으로 전파됩니다.
Aaa Aaaa Aaaaa Aaaa Aaaaaa Aa Aaaaaaaaaaa Aaaa Aaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaa Aaaaaa a Aaa Aaa Aaaaa
Aa Aaaaaa Aaaaaaaa Aaaaaaa Aa Aaaa Aaaaaa
Aa Aaaa Aaaaaaaaa Aaaa Aaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaaaa a Aaaaaa
Aa Aaaaaaaaa Aaa Aaaaaaaaa Aaaaa Aaaa Aaa Aaaa Aaaaaa Aaaa Aaaaaaaaaaaaaaaaa Aaa Aa Aaaa Aaaaaaa Aaaaaaaaaaaaaaa
Aa Aaa Aaa Aa Aaaaaaaaaa Aa Aaaaa Aaa Aaaa Aaa Aaaaaa Aaaa Aaaaaaa Aaaaaaaaaa Aa Aaaa Aaa Aa Aa Aaa Aaaaaaa
Aa Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaa Aa Aaa Aaaaaa Aaa Aaaaaaaaaaaaaaaa Aa Aa Aaa Aaaaaaa Aaa Aaaaaaaa Aa Aa Aaaaaa
Aa Aaa Aaa Aaaaaaaaaaa Aaaaaaa Aaaa Aaaaaa Aaaa Aaa Aaaa Aaaaaaa
Aa Aaaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaa Aaaaaaaa Aaaaaa Aaaaaaa Aaaaaaaaaaa Aaaaaaaa Aaa Aaaaaa Aaaaaaaa Aaaa Aaaa Aaaaaaaaaa Aaaaaa Aaaaaaaaa Aaaaaa
Aaaaa Aaaaaaa Aaaaa Aaa Aaaa Aaaaa Aaaaaa Aa Aa Aa Aaa Aaa Aaaaaaaaaa a Aaa Aaa Aaaaa Aaaaaaaaaaaaa Aaa Aa Aaaaaaaaaaaaaaaaaaaa Aaa Aaaa Aaaaaaa Aaaaaaaaaaa Aaaaaa Aaaaa Aaaaa Aaa Aaa Aaaaa Aa Aaa Aaaaaa Aaa Aaaaaa
a Aaaa Aaaaaa Aaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaaaaaa Aaaaaaa Aaaaaa Aaaa Aaaaa Aa Aaa Aaaaaa Aaaaaaaaa Aaa Aaa Aaaaaaa Aaaaaaaaa Aaaaaa Aaaa Aaaa Aaaaaaaa Aaa Aa Aaaa Aaaaaaaa Aaa a Aaa Aaaaaaa Aaaa Aaaaa Aaaa Aaaaa Aaaaaaaaaaaaa Aaa Aaa Aaaaaaa Aaaa Aaaa Aaaaaaaaa Aaaaaaaaaa Aaaaaa a Aaa Aaaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaa Aaaa Aa Aaa Aaa Aaaa Aaa Aaaaaaa Aaaaaaa Aaaaaa
a Aaaa Aaaaaaaa Aaaaaaa Aaa Aaa Aa Aaaa Aaa Aa Aaa Aaaaaaa Aaa Aaaa Aaa Aaaaaa Aaaaaaaa Aa Aaaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaa Aaaa Aaa Aaaaa Aaa Aaa Aaa Aaaaaaa Aa Aaaa Aaaa Aaa Aaaa Aa Aaa Aaaaaa Aaaa Aaaaa Aaaa Aaaaaa Aa Aaaaaaaa Aa Aaa Aa Aaaaaaa Aaaaaaaaa a Aaa Aa Aaaa Aaaa Aaaaaaa Aaaaa Aaaaaaaaaa Aaa Aaa Aa Aaaaa
Audit directions
a Aaa Aa Aa Aaaaaaaaaaa Aa Aaa Aaaa Aaaa Aaa Aaaa Aaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaa Aaaa Aaaa Aaaa Aaaaaaaa Aaa Aaa Aaaaaaaa Aaa a Aa Aaaaa Aaa Aaa Aaa Aa Aaaa Aaaaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaa Aaa Aaa Aa a Aaa Aa Aaa Aaaa Aaaa Aaaa Aa Aaaa Aaaa Aaaaaaaa Aaaaaaaaa Aaaaaa Aaaaa Aaaaa Aaaaaaaaaaaaaaaaaa Aaaaa Aaaaa Aaa Aaa Aaaaa
a Aaa Aaa Aaa Aa Aaa Aaa Aa Aaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa a Aaaa Aaa Aa Aaaaaa Aaaaaaaaaaaaaaaaa Aaaaa Aa Aa Aaaa Aaaaaaaaaaaaaaaaa Aaa Aaaaaaaa Aaaaaaaaaaa Aa Aaa Aaa Aaaaaaaaa Aaaaaaaaaa Aaaaa Aaa Aa Aaa a Aaaa Aaaaaa Aaaaa Aa Aaa Aaa Aaaa Aaaa Aa Aa Aa Aaaaaaaaaa Aaa Aa Aaa Aaaaaa
a Aaa Aaa Aaa Aa Aaaaaa Aaaaaaaaaa Aaaa Aaa Aaaa Aaaaaaaaaaaa Aaaa Aaaa Aaaaaa Aaaaaaaa Aa Aa Aa Aaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaa Aaa Aa Aaaaaaaaaa Aaa Aaaa Aa Aaaaa Aaaaaaaa Aa Aa Aaaaaaa Aaa Aaaa Aaaaa Aaaa Aaaaaaaaaaaaa Aa Aaaaaa Aaaaaaaa Aaaaaaaaaaa Aaaaa Aaaaa Aaaaa Aaaaaaa Aa Aaa a Aa Aa Aaa Aaaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaa Aaaaa Aa Aaaaaaaaaaaaaaaaa Aaaaa Aa Aaaaa Aaaaaaa a Aaaaaa Aaaaaaaa Aaa Aaaaaaa Aaa Aaa Aaaaa
Aaaaaaaaa Aaa Aaaaaa Aaaaa Aaa Aaa Aaaaaaaaaa Aaa Aaa Aaa Aaaaa Aaa Aaaa Aaa Aaa Aaaa Aaaaa Aaaaa Aa a Aa Aa Aaa Aaaa Aaaaa Aaa Aaa Aa Aaaa Aaa Aaaaa Aa Aaaa Aaaaaaaaaa a Aaaaaaaaaaaa Aaa Aaa Aaaaaaaaaa Aaaaaa Aa Aaaaaa