CVE-2025-43535
Overview
Background
- BBQ JIT register model
- Tracks which registers hold live operand values; every used operand must be consume()d to free its register.
- array.set
- Wasm GC instruction storing a value at an index in a typed array; takes array, index, and value operands.
- consume()
- Releases a value’s register binding after use; omitting it leaves the register wrongly marked occupied.
- Register pressure
- When many values compete for few registers, a leaked binding leads to incorrect reuse/spills.
Root Cause Analysis
In JavaScriptCore’s Wasm BBQ JIT (64-bit), BBQJIT::addArraySet compiles the array.set instruction, which takes an array reference, an index, and a value. The BBQ register allocator requires every operand value’s register to be released with consume() once it has been used, so the allocator’s model of free/occupied registers stays accurate.
Pre-patch, addArraySet failed to consume() the index value after using it, so the index’s register remained marked as bound to that value even though array.set had finished with it. Subsequent codegen could then reuse or spill that register under the wrong assumption, corrupting the register-allocation state — a JIT miscompilation.
The fix adds the missing consume(index) so the index operand’s register is released like the array and value operands, keeping the allocator consistent. The crafted wasm module in the test drives array.set with register pressure that exposes the leaked binding.
The restored invariant is that all operands of array.set are consumed, so the BBQ allocator’s register model matches the emitted code.
Attack Path
- Craft a wasm module using array.set Build a module (GC arrays) whose function performs array.set under register pressure so the index operand’s register matters.
- Compile via BBQ JIT Instantiation compiles addArraySet, which uses the index but does not consume its register binding.
- Corrupt allocator state The index’s register stays wrongly bound, so later codegen reuses/spills it inconsistently.
- Miscompile / crash The mismatched register state produces incorrect code, crashing or corrupting memory in the WebContent process.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
BBQJIT::addArraySetSource/JavaScriptCore/wasm/WasmBBQJIT64.cpp |
modified | Adds the missing consume(index) so the array.set index operand's register is released, keeping the BBQ register allocator consistent. |
Files Changed
JSTests/wasm/stress/bbq-array-set-consume.jsSource/JavaScriptCore/wasm/WasmBBQJIT64.cpp
Audit Directions
- Missing consume() on operandsAudit all BBQ addX handlers (array/struct/table/memory ops) to confirm every operand fetched is consume()d exactly once.
- array/struct GC opsReview other GC instruction handlers (arrayGet/arrayNew/structSet) for the same operand-consume completeness.
- 32-bit vs 64-bit parityCheck WasmBBQJIT.cpp/WasmBBQJIT32 for the same array.set consume symmetry as WasmBBQJIT64.