Medium CVSS 4.3 webkit Type Confusion 🔧 Commit mapped

Overview

Medium
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may lead to an unexpected process crash
ComponentJSC Wasm
Bug ClassType Confusion
Tracker300774
Fix commit26d50a4d4b2d (WebKit/WebKit) +14/-0
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L
CISA KEVNot listed
CreditedGoogle Big Sleep, Nan Wang (@eternalsakura13)
Disclosed2025-12-12

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.

Key insight
BBQ-JIT addArraySet used the index operand but never consume()d its register, leaking a binding and corrupting the allocator’s register model; adding consume(index) fixes the miscompile.

Attack Path

  1. 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.
  2. Compile via BBQ JIT Instantiation compiles addArraySet, which uses the index but does not consume its register binding.
  3. Corrupt allocator state The index’s register stays wrongly bound, so later codegen reuses/spills it inconsistently.
  4. Miscompile / crash The mismatched register state produces incorrect code, crashing or corrupting memory in the WebContent process.

Impact Assessment

A BBQ-JIT register-accounting bug (advisory class LogicError): a missing consume() on the array.set index leaks a register binding and miscompiles code, reachable from crafted wasm in the WebContent process. JIT miscompiles are a corruption-class primitive; building a controlled outcome needs register shaping, and the observable is a crash. Confined to WebContent; rated medium.

Changed Functions

FunctionChangeNotes
BBQJIT::addArraySet
Source/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.js
  • Source/JavaScriptCore/wasm/WasmBBQJIT64.cpp

Audit Directions

  • Missing consume() on operands
    Audit all BBQ addX handlers (array/struct/table/memory ops) to confirm every operand fetched is consume()d exactly once.
  • array/struct GC ops
    Review other GC instruction handlers (arrayGet/arrayNew/structSet) for the same operand-consume completeness.
  • 32-bit vs 64-bit parity
    Check WasmBBQJIT.cpp/WasmBBQJIT32 for the same array.set consume symmetry as WasmBBQJIT64.

Original Bug Report

The reporter's bug is still restricted on the tracker.