Medium CVSS 8.8 webkit OOB 🔧 Commit mapped

Overview

Medium
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may lead to memory corruption
ComponentJSC Wasm
Bug ClassOOB
Tracker291506
Fix commit265dbd5abf60 (WebKit/WebKit) +12/-0
CWECWE-119 (Buffer bounds error)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedNan Wang(@eternalsakura13)
Disclosed2025-05-12

Background

BBQ JIT
JavaScriptCore’s baseline WebAssembly JIT; the 64-bit variant keeps values in 64-bit registers.
branch32 bounds check
A 32-bit comparison; if the index register holds a 64-bit value, only its low 32 bits are validated.
zeroExtend32ToWord
Clears the upper 32 bits of a register so a subsequent 64-bit address computation matches the checked 32-bit value.

Root Cause Analysis

This fixes an out-of-bounds access in the WebAssembly BBQ JIT (64-bit) because the array bounds check compared only the low 32 bits of the index while the element address used the full 64-bit register. In BBQJIT::addArrayGet and addArraySet, the index is bounds-checked with a 32-bit compare — branch32(AboveOrEqual, indexGPR, array->size) — but the index register can hold a 64-bit value whose upper 32 bits are non-zero. Pre-patch the code proceeded to compute the element address from the full 64-bit register, so an index such as 0x7fffffff00000000 passes the 32-bit check (low 32 bits = 0 < size) yet the actual access uses the full 64-bit value as the offset, reading or writing far outside the array — an out-of-bounds memory access.

The fix inserts m_jit.zeroExtend32ToWord(indexGPR, indexGPR) immediately after the bounds check in both addArrayGet and addArraySet, clearing the upper 32 bits so the access uses only the validated low 32 bits.

The restored invariant is that the index used for the element address matches the 32-bit value that was bounds-checked. The regression test passes 0x7fffffff00000000n to a wasm array access.

Key insight
The array bounds check was 32-bit but the element address used the full 64-bit index register, so a 64-bit index with in-bounds low bits bypassed the check; zero-extending the index after the check ties the access to the validated value.

Attack Path

  1. Reach the BBQ JIT Run a WebAssembly module with array get/set so it is compiled by the BBQ (64-bit) tier.
  2. Supply a 64-bit index with high bits set Provide an index like 0x7fffffff00000000 whose low 32 bits are in-bounds but upper bits are non-zero.
  3. Pass the 32-bit check The branch32 bounds check only inspects the low 32 bits and succeeds.
  4. Out-of-bounds access The element address is computed from the full 64-bit register, reading/writing far outside the array — a controllable OOB in WebContent.

Impact Assessment

A controllable out-of-bounds read/write in the WebContent process via WebAssembly array access — the attacker chooses the high bits of the index, giving a strong, wide-reaching corruption primitive and a well-known route toward arbitrary read/write and code execution.

Changed Functions

FunctionChangeNotes
BBQJIT::addArrayGet
Source/JavaScriptCore/wasm/WasmBBQJIT64.cpp
modified Zero-extends the index to the full word after the 32-bit bounds check so the element address uses only the validated low 32 bits.
BBQJIT::addArraySet
Source/JavaScriptCore/wasm/WasmBBQJIT64.cpp
modified Same zeroExtend32ToWord after the bounds check to prevent a 64-bit index bypassing the 32-bit check.

Files Changed

  • JSTests/wasm/stress/array-get-large-i64-index.js
  • Source/JavaScriptCore/wasm/WasmBBQJIT64.cpp

Audit Directions

  • Same file: 32-bit checks on 64-bit values
    Audit WasmBBQJIT64 for other branch32 bounds/type checks on index/length registers that are later used as 64-bit addresses without zero-extension.
  • Index provenance
    Grep for loadIfNecessary(index) uses where an i32 index may carry garbage/high bits from an i64 source before an address computation.

Original Bug Report

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