[4] [JSC] Check initial object structure in tryEnsureAbsence in DFG
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();
Patch Details
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.
Background
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.
Analysis
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.
Aaaaaaaaaa Aaaa Aaaaaaaaaaaa Aaa Aaa Aaaaaaaaaa Aaaaaaaa a a Aaaaaaa Aa Aa a Aaa Aaaaaa Aaaaaaaaaaa Aa Aaaaaaaaa Aaaa Aaaaaaaa Aaaaaaaaaaaaa Aaaaaa a Aaaaaaaaaaaaaaa Aaaaaaaa Aaaaaaaaa Aa a Aaa Aaaaaaaa Aaaaaaaaa a Aaaaaa Aaaaaa Aaaaaaaaa Aa Aaa Aaaaaaaa Aaaaa Aaaaaaaaa a Aaa Aaaaaaaaa Aaaaa Aaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaa Aaaa a Aaaaaaa Aaaa Aa Aaaa Aaaaa Aaaaaaaa Aaaaaaaa Aaaa Aaaaaaaa a Aaaaa Aaaa Aaa Aaa Aaaa Aaaa Aaaaaaa Aaaaaaa Aaaaaaaaaaaa Aaaaaaaaaaaa Aaaaaaaaa a Aaaaaaaaaaaa Aaaaaa a Aaa Aaaaaa Aaaaaaa Aaaa Aaaaaaa Aaa Aaaaaaa Aaa Aaaaaa Aa Aaaaaaaaaaaaa Aaa Aaaaaaaaaaaa Aaaaaaaaaaaa a Aaaaaa Aa a Aaaaaaaa Aaaaaaa a Aa Aaaaaaaaaaaaaaaa Aaaa Aaaaaaaaa Aa Aaa Aaaaaaaaaa Aaaaaaaa Aaaaaaa Aaaa Aaa Aaaaaaaaaaaaaa Aaaaa Aaaa Aaaaa Aaaaa a Aaaaaaaaaaaaaa Aaaaaaaaa Aaaaa Aaa Aaaaaaaa Aaa Aaaaaaaaa Aaa Aaa Aaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaaaa Aaaaaaaaaaa Aaaaaaaaaa a Aaaaaaaaaaaa Aaaaaaaaa Aaaaa Aaaa Aaaaaaa Aaaa Aaaaa Aa Aaaaaaaaa Aaaaaaa Aaaaa Aaaaa Aaaa Aaa Aaaaaa Aa Aaaaaaaaaaaa Aaaaaaa Aaa Aaaaaaaaaa Aaa Aaaaaaaa Aaa Aaaaa Aaaaaaaaaa
🔒How a missing head-receiver check in a DFG absence helper undermines JIT speculation soundness, and how the regression test composes it into a realistic type-confusion shape.
Subscribe to read more
Audit directions
a Aaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaa Aaaa Aaaaaaaaa Aaaaaa Aaa Aaaaaa Aaa Aaaaaa Aaaaaaaaa Aaa Aaaaaaa Aaaaa Aaaaa Aaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaa
a Aaaaaaaaaaaaaaaaaa Aaaaaaaaaa Aaaa Aaaa a Aaaa Aaaa Aaaaaaaa a Aaaaaaaa Aaaaaaaaaaa Aaaaa Aaa Aaaaaaa Aa Aaaaa Aaa Aaa Aaaaaaaa Aaa Aaaaaa Aaaaaaaa Aaaaa Aaaaaaaa Aaa Aaa Aaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaa Aaaa Aaaaaaaaaaaaa Aaaaa Aaaaaaaa Aaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaa Aaa Aaaaaaaaaaaaaaaaa Aaaaaaaaa
a Aaaaaaaaaaaa Aaaaaaa Aaaaaaaaaa Aaaaaa Aaaa Aaaaa Aaaaa Aaa Aaaa Aaaaaaaa Aaaaa Aaa Aaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaa Aaaaaaa Aa a Aaaaaa Aaaaaaaaa Aaaa
a Aaaaaaaaaaaaaa Aaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaa Aaaaaaaaaa Aaa Aaaaaa Aa Aaaaaaa Aaaaa Aaa Aaaa Aa Aaaaa Aaaaaaaaa Aaaaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
🔒Multiple reusable audit patterns identified — covering chain-walking helpers, asymmetric validation lambdas, and absence-condition fast paths that re-enter JS via callbacks.
Subscribe to read more