← All issues

[8] GetByStatus walks prototype chain for direct/private property opcodes

Severity: Medium | Component: JSC bytecode IC status + DFG | b07dca9

Rated Medium because the diff repairs a JIT IC status helper that allowed DFG to specialise GetByIdDirect/GetPrivateNameById under prototype-walk assumptions; whether this becomes a controlled property fetch from a non-own slot depends on downstream lowering that the diff does not establish in isolation.

When computing the GetByStatus, the property lookup should be checked for direct property access before doing a prototype walk, since direct accesses are not supposed to consult the prototype.

Source/JavaScriptCore/bytecode/GetByStatus.cpp

-GetByStatus GetByStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, CacheableIdentifier identifier)
+GetByStatus GetByStatus::computeFor(JSGlobalObject* globalObject, const StructureSet& set, CacheableIdentifier identifier, GetByStatus::LookupMode mode)
{
- // Note that this code is also used for GetByIdDirect since this function only looks
- // into direct properties. When supporting prototype chains, we should split this for
- // GetById and GetByIdDirect.
...
- if (auto result = attempToFold())
- return result.value();
+ // We should do a prototype walk searching for the property only if this
+ // is a normal access. Don't consult proto for for direct accesses.
+ if (mode == GetByStatus::LookupMode::Normal) {
+ if (auto result = attempToFold())
+ return result.value();
+ }

Source/JavaScriptCore/dfg/DFGNode.h

+ GetByStatus::LookupMode propertyLookupMode()
+ {
+ switch (op()) {
+ case GetByIdDirect:
+ case GetByIdDirectFlush:
+ case GetPrivateNameById:
+ return GetByStatus::LookupMode::Direct;
+ case GetById:
+ case GetByIdFlush:
+ case GetByIdMegamorphic:
+ return GetByStatus::LookupMode::Normal;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ }

A new LookupMode enum (Normal/Direct) is added to GetByStatus and threaded through computeFor. The prototype-walk inside computeFor (the attempToFold() block) is now gated on mode == LookupMode::Normal. Node::propertyLookupMode() maps DFG opcodes to the correct mode. DFGAbstractInterpreterInlines.h::executeEffects and DFGConstantFoldingPhase::foldConstants now derive the mode from the node. op_get_from_scope in DFGByteCodeParser is explicitly pinned to Normal because global property lookup is supposed to walk the global object's prototype chain. The stale comment claiming the function only looks at direct properties is removed.

Speculation-soundness violation in the JIT IC status computation: a shared helper applies prototype-chain semantics to opcodes whose runtime semantics forbid prototype traversal.

GetByStatus is JSC's summary of what a GetById-family bytecode has done at runtime: observed structures, offsets, and prototype walks, so the DFG can decide whether to inline a load, constant-fold a result, or fall back to a generic IC. GetById performs a full ECMAScript [[Get]] which walks the prototype chain. GetByIdDirect performs an own-property-only access; it must return undefined if the property is not own. GetPrivateNameById reads a private class field, keyed by a brand on the receiver, strictly own-property by spec. attempToFold inside computeFor is the proto-walk helper that searches the chain for the property and folds it into a Simple variant when found. The DFG's AbstractInterpreter and ConstantFoldingPhase consume the returned GetByStatus to specialise the IR.

GetByStatus::computeFor always invoked attempToFold(), which walked the prototype chain. It was called from DFG passes for every GetBy* node, including GetByIdDirect, GetByIdDirectFlush, and GetPrivateNameById — opcodes whose runtime semantics require own-property-only access. The stale comment claimed the function only looked at direct properties, which was true when written but became false once attempToFold was added.

🔒

The speculation-soundness implications of conflating own-property and prototype-walking semantics in a shared JIT helper, and the conditions under which this can escalate beyond a pure correctness bug

Subscribe to read more

🔒

Several reusable audit patterns identified across JSC's IC status computation layer, with concrete starting points for finding variants in sibling status helpers

Subscribe to read more