[6] [JSC] Fix GC safety for sunk contiguous array materialization in FTL
Severity: High | Component: JSC FTL JIT | e638840
Rated High because the diff plugs a GC-liveness hole at FTL: contiguous cell pointers stored into an unrooted butterfly become invisible to both precise and conservative scans across allocateJSArray's slow path; the regression test demonstrates aliasing of the supposedly-stored cell with a later allocation.
compileMaterializeNewArrayWithButterfly wrote contiguous element cell pointers into a raw butterfly, then called allocateJSArray to allocate the JSArray header. B3 backward liveness considered the cell pointers dead after the store64; the GC slow path could collect them while the butterfly was still unowned.
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
ObjectMaterializationData& data = m_node->objectMaterializationData();
+ Vector<LValue> contiguousElementValues;
for (unsigned i = 0; i < data.m_properties.size(); ++i) {
...
- case ALL_INT32_INDEXING_TYPES:
+ case ALL_INT32_INDEXING_TYPES: {
+ LValue value = lowJSValue(edge, ManualOperandSpeculation);
+ m_out.store64(value, butterfly, m_heaps.forIndexingType(indexingType)->at(index));
+ break;
+ }
case ALL_CONTIGUOUS_INDEXING_TYPES: {
LValue value = lowJSValue(edge, ManualOperandSpeculation);
m_out.store64(value, butterfly, m_heaps.forIndexingType(indexingType)->at(index));
+ contiguousElementValues.append(value);
break;
}
...
LValue array = allocateJSArray(indexingType, publicLength, butterfly);
+ ensureStillAliveHere(contiguousElementValues);
Patch Details
Contiguous element LValues are collected into contiguousElementValues; after allocateJSArray, ensureStillAliveHere(contiguousElementValues) inserts a zero-instruction B3 patchpoint that takes each value as a ColdAny operand, extending backward liveness through the allocation. The INT32 arm is split out into its own case (Int32-tagged values are not cell pointers, no GC concern). A new ensureStillAliveHere(const Vector<LValue>&) overload appends every value to one patchpoint.
Premature liveness termination of GC cell pointers stored into an unrooted heap buffer before the owning object is allocated, leaving them invisible to both precise and conservative GC scans across an allocation slow path.
Background
Allocation sinking is a DFG optimization that defers object/array allocation; on materialization, the FTL emits element stores and the cell allocation as separate operations. For NewArrayWithButterfly, the butterfly is allocated and filled first, then allocateJSArray allocates the JSArray header. B3 computes liveness backward; a value with no further use after a point is considered dead. ensureStillAliveHere inserts a zero-instruction PatchpointValue whose only purpose is to be a formal use, keeping the register allocator from dropping it. The conservative stack scanner only sees values present in stack/registers; values dropped before a GC are not traced.
Analysis
After the store64, B3 considered each element value dead and was free to drop it from any callee-saved location. allocateJSArray's slow path can trigger GC; the conservative scanner could not find the cell pointers anywhere, and the precise scanner could not trace through the not-yet-attached butterfly. The cells were eligible for collection, leaving the butterfly holding dangling pointers.
Aaa Aaa Aaaaaaa a Aaaaaaaa Aaaa Aaaaaaaaa Aaaa Aaaaaaaaa Aaa Aaaaaaa Aaaaaa Aaaaaaaaa Aaaa Aaaaaaaa Aaa Aaa Aaaaaaaa Aa Aa Aaaaaaaaaaaa Aaaa Aaa Aaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaa Aaaaaaaa Aaa Aaaaaaaa Aaa Aaaaaaaaaaaaaaa Aa a Aaaaaaaaa Aaaaaaaaaa Aaaaaa Aa Aa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaa Aaaaaaa Aaaaaaaaa Aaaaaaa Aa Aaaaa Aaaa Aaaaaaaaaaa Aaa Aa Aaaaaaaa Aaaaa Aaaaaaaaaa Aaaaaaaaaaa Aaaaa Aaa Aaaaaa Aaaaaaa Aaa Aaaaaaa Aaaaaaaa Aaaaaaa a Aaaaaaaaaaaaaaaaa Aaaa Aaaaaa Aaaa Aaaaaaa Aaa Aaaaa Aaaaa Aa Aaaaaaaaaa Aaaaaaaaa Aa Aaaaaaaa Aaaaa Aaaaaaa Aaa Aaaaaaaaaaa Aaaa Aaaa Aaaaaaaaa Aaa Aaaaaaaaa Aaaa Aaaaaaa a Aaaaaaaa Aaaa Aaaaaaa Aa Aa Aaaaaaaaaaaaaaaaa Aaaaaaa Aaaaaaa Aa Aaa Aaaaaaaa Aaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaa Aaa Aaaaaaaaa Aaa Aa Aaa Aaaaaaaaaa Aaaaaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaa Aa Aaaaaaaaa Aaaa Aaaaa Aaaaaaaaa Aaaa Aaaaaaa Aa Aaaaaaa Aa Aaaaaa Aaa Aaaaaaa Aaaaaa Aa Aaa Aaaaaaaaaaaa Aaaaa Aaaaaaa Aaaaaa Aaaaaaaaaa a Aaa Aaa Aaaaaa Aaaaaaa Aaaaaaaaa Aaaaaaaaaa Aaa Aaaaaaa Aaaaaaaaaaa Aa Aaa Aaaaaaa Aa Aaaaaaaa
🔒Detailed GC liveness model walkthrough and an assessment of how a slow-path collection during sunken array materialization can escalate beyond a crash into something usable from JavaScript
Subscribe to read more
Audit directions
a Aaaaaaaaaaaaa Aaaa Aaaa Aaaaaa Aaaa Aaaaaaaa Aaaa a Aaaa Aaaaaa Aaaa Aa Aaa Aaa Aaaaaaaa Aa Aaa Aaaaaaaaa Aaaaaaa Aaaaaaaa Aa a Aaaa Aaaa Aaa Aaaaaaa Aaaaa Aaaaa Aaaaa Aaa Aaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaa Aaaaaaaa Aaa Aaa Aaaa Aaaaaa Aaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaa
a Aaaaaaaaaaaaaaaaaaa Aaaaaaaa Aaaaaaaaaa Aaaaaaaa Aaaa Aaaaaaaa Aaaaaa Aaaaaaaaaa Aaaa Aaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaa Aaaaaaaaaaa Aaaaaaa Aaaaaa Aaaa Aaa Aaa Aaaaaaaa Aaaaaaa Aaa Aaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaa
a Aaaaaaaa Aaaa Aaaaaaa Aaaa Aaaaa Aaa Aaa Aaaaaaaa Aaaa Aaaaaaaa Aaa Aaaaaaa Aaaa Aaaaaa Aaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaa
a Aaaaaaaaa Aaaaaaaaa Aaaa Aaaaa Aaaaaaaaaaaaaaaa Aa Aaa Aa Aaaaaaa Aaaa Aaaaa Aaaaaaaaaa Aaaaaaaa Aa Aaa Aaa Aaaaaaaa Aaa Aaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaa Aaaaaa Aaa Aaaaaaaaaa Aaaaaaaaaa Aa Aaaaaaa Aaaa Aaaa Aaa Aaa
🔒Four reusable audit patterns for finding sibling GC-liveness bugs across FTL materialization and allocation lowerings, each with concrete starting points
Subscribe to read more