[8] FTL ValueRepReduction type confusion in MultiGetByOffset constant cases
Severity: High | Component: JSC FTL JIT | f2c5bf8
웹 콘텐츠에서 도달 가능한 FTL 컴파일 코드에서 non-Number JSValue bit pattern이 IEEE 754 double로 재해석되는 type confusion이 발생하기 때문에 High로 평가됩니다. Confused 값은 항상 동일하게 결정됩니다. MultiGetByOffset case에 어떤 constant가 등장하는지에 따라 결과가 고정되기 때문입니다. Downstream array index 혼동 또는 bounds check 우회로의 확장은 confidence 0.92로 예측됩니다. 다만 생성되는 구체적인 수치와 Load-kind case가 emission 시점에 변환을 처리하는지는 commit만으로는 확인되지 않습니다.
ValueRepReductionPhase가 MultiGetByOffset 노드를 NodeResultDouble로 변환할 때, Constant-kind case에 포함된 FrozenValue는 올바른 double 숫자로 변환되어야 합니다. 패치 이전에는 toNumberFromPrimitive()를 통한 변환 없이, non-Number constant(예: undefined)의 raw JSValue bit pattern이 그대로 double 결과로 출력되었습니다.
Source/JavaScriptCore/dfg/DFGValueRepReductionPhase.cpp
- case MultiGetByOffset:
case GetByOffset: {
candidate->setResult(NodeResultDouble);
candidate->mergeFlags(NodeMustGenerate);
@@ ...
+ case MultiGetByOffset: {
+ MultiGetByOffsetData& data = candidate->multiGetByOffsetData();
+ for (unsigned i = 0; i < data.cases.size(); ++i) {
+ GetByOffsetMethod& method = data.cases[i].method();
+ if (method.kind() == GetByOffsetMethod::Constant) {
+ // NumberUse를 예측하지만 non-Number인 constant가 있을 수 있습니다. 예: undefined.
+ std::optional<double> doubleConstant = method.constant()->value().toNumberFromPrimitive();
+ method.setConstantValue(m_graph.freeze(jsDoubleNumber(*doubleConstant)));
+ }
+ }
+ candidate->setResult(NodeResultDouble);
+ resultNode = candidate;
+ break;
+ }
JSTests/stress/ftl-valuerepreduction-double-undefined.js
+for (let v0 = 0; v0 < 100; v0++) {
+ const v1 = {a: v0};
+ for (let v2 = 0; v2 < 5; v2++) {
+ v1.length += v0;
+ v0[1] = v2;
+ v2.constructor;
+ }
+ for (let v4 = 0; v4 < 50; v4++) {
+ function f5() { return v1;}
+ for (let v8 = 0; v8 < 100; v8++) {}
+ }
+}
Patch Details
이 fix는 DoubleRepUse 변환 로직에서 MultiGetByOffset을 기존의 GetByOffset 케이스와 분리합니다. MultiGetByOffset에 대해서는 MultiGetByOffsetData::cases 전체를 순회하며, 각 case의 method가 GetByOffsetMethod::Constant인 경우 toNumberFromPrimitive()를 호출하여 결과를 frozen jsDoubleNumber로 교체합니다. 이 mutation을 지원하기 위해 GetByOffsetMethod::setConstantValue()와 non-const MultiGetByOffsetCase::method() 두 개의 accessor가 추가되었습니다. fix에서는 toNumberFromPrimitive()가 반환하는 std::optional<double>을 nullopt 확인 없이 *doubleConstant로 무조건 역참조합니다. 이는 toNumberFromPrimitive()가 모든 JS primitive에 대해 항상 값을 반환한다는 전제 하에 안전합니다.
Before: After:
MultiGetByOffset (NodeResultJS) MultiGetByOffset (NodeResultDouble)
case A: Load from offset case A: Load from offset (same)
case B: Constant(undefined) case B: Constant(NaN) ← converted
[raw JSValue bits emitted [jsDoubleNumber(NaN)]
as "double"]
Background
MultiGetByOffset은 property load에 대한 polymorphic inline cache를 표현하는 DFG/FTL IR 노드입니다. 여러 case를 포함하며, 각 case는 객체 구조 집합을 매칭하고 값 조회 방식을 지정합니다. 방식은 객체 내 특정 offset에서 값을 읽는 Load이거나, constant를 반환하는 Constant(예: 존재하지 않는 property에 대한 undefined) 두 가지입니다.
ValueRepReductionPhase는 FTL 최적화 단계입니다. boxed JSValue를 생성하면서 항상 unboxed double(DoubleRep 노드를 통해)로만 소비되는 노드를 탐지하고, 해당 노드가 NodeResultDouble을 직접 생성하도록 변환합니다. 이를 통해 box→unbox overhead를 제거합니다.
FrozenValue는 JIT가 생성된 코드에 직접 embed할 수 있는 JSValue의 compile-time snapshot입니다. MultiGetByOffset의 constant case가 emit될 때, FTL은 frozen value의 bit를 그대로 노드 출력으로 materialization합니다. load와 달리 값이 runtime에 메모리에서 읽히는 것이 아니라 IR에 고정되기 때문에, constant case에는 명시적인 변환이 필요합니다.
JSC의 값 인코딩에서 non-double 값은 64비트 워드의 상위 영역에 tag bit를 이용해 인코딩됩니다. undefined, null, true, false, 그리고 object pointer는 모두 의미 있는 IEEE 754 double에 대응되지 않는 고유한 bit pattern을 가집니다.
Analysis
FTL 최적화 파이프라인에서 IR 노드의 result type이 JSValue에서 double로 narrowing될 때, embedded constant에 대한 값 표현 변환이 누락된 패턴.
패치 이전에는 ValueRepReductionPhase가 MultiGetByOffset 노드를 NodeResultDouble로 변환할 때, 노드 case에 embedded된 constant 값을 변환하지 않은 채 result 표현만 바꾸었습니다. Constant-kind case의 경우 FrozenValue가 IR에 직접 embed됩니다. 변환이 이루어지지 않으면 non-Number constant(예: undefined)의 raw JSValue bit pattern이 그대로 double 결과로 출력됩니다. 결과적으로 downstream JIT 코드는 undefined의 JSValue 인코딩 비트를 double-precision floating point 숫자로 해석하게 됩니다. JavaScript 명세가 요구하는 NaN 대신 잘못된 값이 생성되는 것입니다.
Load-kind case는 emission 시점에 메모리에서 값을 읽기 때문에, emitter가 materialization 과정에서 JSValue-to-double 변환을 처리할 가능성이 높습니다. fix가 Constant-kind case만 변환한다는 점에서 추론한 사항입니다. 반면 Constant-kind case는 값이 IR에 고정되어 있으므로 명시적인 변환이 필요합니다.
Aaa Aaaa Aaaaaaa Aaa Aaa Aaaa Aaa Aaaaa Aa Aa Aaaa Aaaaa Aaaaaaaa Aa Aaaaaaaa Aaaaa Aaaaaa a Aaaa Aaaa Aaaaaa Aaaaaaaaaaaa Aaaa Aa Aaa Aaa Aaaaa a Aaaaa Aaaa Aaaaaaaaaaaaaaaaaa Aaaaa Aaaa Aaaaaaaaaa Aaaaaaaaa Aaa a Aaaaa Aaaaaaaaaaaa Aaaaaaa Aaaaaaa Aaaaaaaa Aa Aaa Aaaaaaa Aaaaaaa Aaaa Aa Aaa Aaaaaaaa Aaaaaa Aa Aaaaaa
Aaaaaaaaaa Aaa a Aaaaaaaa Aaaaaa Aa Aaaaaa Aaaa Aaaaa Aaaaaa Aaaa Aaa Aaaaaaaaaa Aaaaaaaaaa Aa Aaaa Aaaaa Aaaaaa Aaaaaaaa Aaaaaaaaaaaa Aaa Aaaaaaaa Aaaaaaa Aaaaa Aaa Aa Aaaaaaa Aaaaaaa Aa Aaaa Aa Aaaaaa a Aa Aaaaaa Aaa Aa Aaa Aaa Aaa Aaaa Aaaaa Aaaaa Aaaaa Aaaaaa Aaa Aaa Aaa Aaaaa Aa Aaaaaaaaaaa Aaaaaa Aaaa a Aaa Aaaa Aaaaa Aaaa Aaaaaaaaaa Aaa Aaaaaaaa Aa Aa Aaaaaaaaaaa Aaaaaa Aaa Aaaaaaaa Aa Aaaaaa
a Aaaaaaaaaaaaaa Aaaaaaaa Aaaaaaa a Aaa Aaaaa Aaaaaaaaaaa Aaa Aaaaaaa Aaaa Aaaaaaa Aaaaaaaaaaaaaaaaaa Aaa Aaa Aaaa Aaa Aaaaaaa Aaaaa Aaaaaa Aaa Aaaaaaaaaa Aaaaaaaa Aaa Aaa Aaaaaaaa Aaaaaaa Aaaaaa Aaaaaaaaaa Aaa Aaa Aaaaaaaaaa Aaa Aa Aaa Aa Aaaaaaa Aaaaaaa Aa Aaa Aaa Aaaa Aaaa Aaaa
Aaa Aaaa Aaaa Aaaaaaaaaa Aaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaa Aaaaaaa Aa Aa Aaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaaaa Aa Aaaaaaaaa Aaaa Aaaaaaaaaaaaaaa Aaa Aaaa Aaa Aaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aa Aaaaaaaaaa Aa Aa Aa Aaaaa Aaa Aaa Aaaaaa
🔒Explores how the confused double value interacts with downstream JIT operations and whether it can be escalated beyond incorrect computation
더 확인하려면 구독해 주세요
Audit directions
a Aaa Aaaaaaaaaa Aa Aa a Aaaaaaaa Aaaaaaaa Aaaa Aaaa Aa a Aaa Aaaaaa Aaa Aaaaaaaaaaa Aaa Aaa Aaaaaaaaaa a Aaaaaaa Aaaaaaa a Aaaaaa Aaaaaaa a Aaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaaa Aaaaaa Aaaa Aa a Aa Aaaaaaaaaaaaa Aaaaa Aaaa Aaaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa a Aaaaaa Aaaaaaaa Aaaaaaaa Aa Aaa Aaaa Aaaa
a Aaa Aaa Aaa Aaaaaa Aaaaa Aaaaaaaa Aaaaaaaaaaaa Aa Aaa Aaaaaaa Aa Aa Aaaa Aaaaaaaaaaa Aaaaaaaaa Aaaa Aa Aaaaaaa Aaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaaa Aa Aaa Aa Aa Aa Aaaa Aaa Aaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaa Aa Aaa Aaaaaaaaaaaaa Aaa Aaaa Aaaa
a Aaa Aaaa Aaaa Aaaa Aaaaaa Aaaaaaaaa Aa Aaaaaaaa Aa Aaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaa Aa Aaaa Aaa Aaa Aaaa Aaa Aaaa Aa Aaaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaa Aaa Aaa Aaa Aaaa Aaaa Aa Aa Aaaa Aaa Aaaa Aaaaaaa Aaaaaaaaa Aaaaaaaa Aaaaa Aaa Aaaa Aaaaa
Aaaaaaaaa Aa Aa Aaaaa Aa Aaaaaaaaa Aaaaa Aaaaaaaa Aaa Aaa Aaaaaa Aaa Aaa Aaaa Aaaa Aaaa Aaaa a Aaaa Aaaa Aaa Aaa Aaa Aaa Aaaa Aaa Aaaaaa Aa Aaaaaa Aaaaaaaaaa Aaaa Aaaaaaaaa Aaaa Aa Aaaa Aaaaa Aa Aaa Aa Aa Aaaaaa
🔒Multiple reusable audit patterns identified across JIT optimization phases, with concrete starting points for variant discovery
더 확인하려면 구독해 주세요