[4] [JSC] Add write barrier to op_del_by_id/op_del_by_val in baseline JIT
Severity: High | Component: JSC Baseline JIT | 1cdc540
Rated High because the diff closes a missing generational write barrier in baseline op_del_by_* whose inline cache transitions the base cell's Structure without barriering; an eden GC after the bytecode runs can sweep the freshly stored Structure, producing a dangling Structure* reachable from a live JSCell.
The barrier emission is moved before the result write to dst, and the ShouldFilterBase mode is replaced with WriteBarrierForCellState so the barrier reads the cell from the IC slot rather than re-reading base from the (now-overwritten) virtual register.
Source/JavaScriptCore/jit/JITPropertyAccess.cpp
Missing generational write barrier on a JSCell whose Structure pointer was just transitioned by the property-deletion inline cache.
Patch Details
In both emit_op_del_by_id and emit_op_del_by_val, the barrier is emitted before the result is boxed into dst. The owner is the base cell register from the IC, not the virtual register base, which on o = delete o.x aliases dst.
Background
JSC's generational GC requires every old-gen → new-gen pointer write to be remembered. The inline cache emitted by op_del_by_* may transition the base object's Structure (the comment in source confirms "IC can write new Structure without write-barrier if a base is cell"); the JIT is then responsible for the barrier. emitWriteBarrier(base, ShouldFilterBase) reads the base virtual register, checks it is a cell, then performs the barrier.
Analysis
The pre-fix sequence for o = delete o.x was: (1) IC fast path that may transition the Structure of o; (2) boxBoolean(resultJSR); (3) emitPutVirtualRegister(dst, resultJSR) overwriting o with a boolean; (4) emitWriteBarrier(base, ShouldFilterBase) re-reading the virtual register. Because base and dst refer to the same local register holding o, step (4) reads the boolean. ShouldFilterBase's branchIfNotCell early-exit then skips the barrier entirely.