← All issues

[4] [JSC] Check initial object structure in tryEnsureAbsence in DFG

DFG's tryEnsureAbsence validated the prototype chain but skipped the head — the JIT baked in absence assumptions the receiver itself disproved.

Severity: High | Component: JSC DFG JIT | 78c04ea

Rated High because the diff removes a soundness violation in DFG speculation: a property-absence assumption could be installed on a receiver that owned the property as its own; the regression test sketches a controlled JSObject/Double type confusion that flows directly into the standard fakeobj/addrof recipe.

Graph::tryEnsureAbsence validated cacheability on the prototype chain but never on the head receiver itself. If the head object owned the property as an OWN property, the DFG would still install an ObjectPropertyConditionSet claiming absence — an invariant that is false from compile time.

Source/JavaScriptCore/dfg/DFGGraph.cpp

+ auto isAbsenceCacheable = [&](Structure* structure) {
+ if (structure->typeInfo().overridesGetOwnPropertySlot())
+ return false;
+ if (!structure->propertyAccessesAreCacheable())
+ return false;
+ if (!structure->propertyAccessesAreCacheableForAbsence())
+ return false;
+ unsigned attributes;
+ if (isValidOffset(structure->getConcurrently(identifier.uid(), attributes)))
+ return false;
+ if (structure->hasPolyProto())
+ return false;
+ return true;
+ };
+
+ if (!isAbsenceCacheable(headStructure))
+ return ObjectPropertyConditionSet::invalid();

The cacheability/absence checks become an isAbsenceCacheable lambda applied to headStructure before generateConditionsForPropertyMissConcurrently runs, and reused for every prototype object. The added comment notes explicitly that generateConditionsForPropertyMissConcurrently only walks the prototype chain — head validation is the caller's responsibility.

Missing self-check in a DFG absence-condition builder that only validated the prototype chain, allowing the JIT to install an absence assumption contradicted by the receiver's own property.

JSC's optimizing tiers avoid runtime property lookups by recording structural invariants — for example, "property P is absent on object O" — into an ObjectPropertyConditionSet that downstream code folds into machine code. tryEnsureAbsence builds the absent variant. generateConditionsForPropertyMissConcurrently walks the prototype chain emitting Miss conditions per prototype; it does NOT examine the head structure. PolyProto indicates per-instance prototype storage, which defeats prototype-chain caching. Re-entrancy points (Promise thenable detection, JSON.stringify toJSON, ToPrimitive) can invoke user code via getters that mutate adjacent typed storage.

The pre-fix code bailed only if headStructure was null; it never consulted overridesGetOwnPropertySlot, propertyAccessesAreCacheable[ForAbsence], or structure->getConcurrently(identifier.uid(), ...) on the head. A { toJSON: 1, a: 1 } head placed on a chain with Object.create would pass — the DFG would then JIT a fast path on the false assumption that tmp.toJSON is missing.

Regression test walkthrough: the PoC constructs object1 = { toJSON: 1, a: 1 }, builds container1 -> object1, then performs +tmp.toJSON inside a Promise.resolve thenable detection in a hot DFG-tier function. A then getter installed on the thenable swaps array[0] = {} (changing array indexing type Double→Contiguous) when a trigger flag is set. After warm-up, re-entry with trigger = true lets the DFG fast path execute against contradicted assumptions; array[0] = 2.3023e-320 writes a raw double pattern into storage the runtime now treats as object-typed, and array[0].x dereferences a double as a JSObject pointer — an object-vs-double type confusion in the WebContent process. Chained with the array-indexing swap, this would yield a fakeobj/addrof primitive pair, the standard JSC launching pad for R/W.

This vulnerability weakens DFG's type-system soundness: a compile-time invariant baked into machine code could be installed despite being false from the moment of compilation, because the receiver's own property was never inspected.