DFG Spread(SetObjectUse) side-effect modeling
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
DFG abstract interpreter는 JIT 컴파일 과정에서 각 값에 abstract state를 부여합니다. 여기에는 CheckStructure guard를 제거할 수 있는 "structure proof"도 포함됩니다. didFoldClobberWorld()는 해당 연산이 side effect 없이 실행됨을 나타내며, 기존 proof가 유지된다는 신호입니다. 반면 clobberWorld()는 모든 structure proof를 무효화합니다.
313031@main에서 Set spread가 operationSpreadSet을 통해 사용자 정의 Symbol.iterator 함수를 호출할 수 있게 되었습니다. 이 함수는 임의의 JS를 실행할 수 있어, 이 시점부터 무조건적인 didFoldClobberWorld() 호출은 더 이상 타당하지 않게 되었습니다. [...set] 실행 중 user iterator가 다른 live 객체의 property를 재정의할 수 있기 때문입니다.
이 patch는 fold 수행 여부를 operand의 structure 검증 결과에 따라 결정합니다. operand가 원래의 Set structure를 보유하고 있다고 증명된 경우, fast path(iterator 없음)에서 기존 proof가 유지됩니다. 그렇지 않으면 clobberWorld()가 호출됩니다.
Significance
잘못된 side effect 귀속으로 인해 CheckStructure 노드가 제거되었습니다. 이로 인해 iterator가 structure 전환을 일으킨 이후에도 GetByOffset이 stale property slot을 읽는 상황이 가능해졌습니다. 그 결과 JIT 컴파일된 코드에서 type confusion primitive가 발생할 수 있었습니다.
Warmup: compiler infers set has originalSetStructure
CheckStructure(o, S_double)
Spread(set, SetObjectUse) → AI: didFoldClobberWorld() // proof for o survives
GetByOffset(o, slot 0, Double) // CheckStructure folded away
Exploit:
iterator: Object.defineProperty(obj, 'a', {get(){return 1}})
→ obj transitions S_double → S_accessor
GetByOffset reads slot 0 as Double on S_accessor → type confusion
Audit directions
- Bottom-structured operands hitting the fast path. fold는 singleton set에 대해
isSubsetOf를 호출합니다. abstract value의 structure set이 비어 있는 경우(Bottom, 이론상 unreachable),isSubsetOf는 공허하게 true를 반환하여canFold=true가 설정됩니다. Bottom structure를 가진SetObjectUse가 이 코드에 도달할 수 있는지 확인이 필요합니다. - Prototype-change watchpoints. fast path의 안전성은 컴파일 시점에 등록된 watchpoint가
Set.prototype[Symbol.iterator]변경을 감지한다는 가정에 의존합니다. 해당 watchpoint가 실제로Set에 대해 발행되는지, 그리고 모든 무효화 경로(prototype에 대한Reflect.defineProperty, lookup을 가로채는 Proxy 등)를 포괄하는지 점검이 필요합니다. - Other collection UseKinds with new slow paths.
MapObjectUse,WeakSetObjectUse,WeakMapObjectUse에서didFoldClobberWorld()를 호출하는 지점을 검색합니다. 각 collection의 AI modeling이 작성된 이후 유사한 user-iterator slow path가 추가되지 않았는지 확인이 필요합니다. - Concurrent JIT consistency. 테스트는
--useConcurrentJIT=0으로 고정되어 있습니다. concurrent compiler의 동일한 로직도 함께 업데이트되었는지 확인이 필요합니다. concurrent하게 변경되는 structure를 기반으로canFold결정이 내려지는 상황이 없어야 합니다.