← All issues

DFG Spread(SetObjectUse) side-effect modeling

fb8bfea

Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h

- if (node->child1().useKind() == SetObjectUse)
- didFoldClobberWorld();
- else
+ if (node->child1().useKind() == SetObjectUse) {
+ bool canFold = false;
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
+ if (Structure* originalSetStructure = globalObject->setStructureConcurrently()) {
+ if (forNode(node->child1()).m_structure.isSubsetOf(RegisteredStructureSet(m_graph.registerStructure(originalSetStructure))))
+ canFold = true;
+ }
+ if (canFold)
+ didFoldClobberWorld();
+ else
+ clobberWorld();
+ } else
clobberWorld();

The DFG abstract interpreter assigns abstract states to values during JIT compilation, including "structure proofs" that let it eliminate CheckStructure guards. didFoldClobberWorld() signals an operation is side-effect-free and proofs survive; clobberWorld() invalidates all structure proofs. When 313031@main allowed Set spreads to invoke user-defined Symbol.iterator functions via operationSpreadSet (which can run arbitrary JS), the unconditional didFoldClobberWorld() became unsound: a user iterator executing during [...set] can redefine properties on other live objects.

This patch gates the fold on whether the operand is proven to carry the original Set structure. If yes, the fast path (no iterator) preserves proofs; otherwise clobberWorld() fires.

Incorrect side-effect attribution caused CheckStructure nodes to be folded away, allowing GetByOffset to read stale property slots after an iterator-induced structure transition — producing a type confusion primitive in JIT-compiled code.

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
🔒

Edge cases in the new structure-proof guard and related collection use-kind modeling are worth security investigation.

Subscribe to read more