Medium CVSS 4.3 webkit UAF 🔧 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 Runtime
Bug ClassUAF
Tracker306136
Fix commit6b357f32c607 (WebKit/WebKit) +54/-1
CWECWE-787, CWE-120 (Out-of-bounds write, Buffer overflow)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L
CISA KEVNot listed
CreditedYeonghyeon Choi, Daniel Rhea, Söhnke Benedikt Fischedick (Tripton), Emrovsky & Switch3301, Yevhen Pervushyn
Disclosed2026-03-24

Background

Resizable ArrayBuffer over wasm memory
WebAssembly.Memory.toResizableBuffer() exposes growable wasm memory as an ArrayBuffer whose backing can move when the memory grows.
Typed-array m_vector cache
A JSArrayBufferView caches its buffer’s data pointer (plus byteOffset) in m_vector for fast indexed access.
Incoming references
An ArrayBuffer tracks the views referencing it; the fix uses this list to update each view after a grow.
Memory grow / reallocation
Growing wasm memory can move the backing store to a new base address, invalidating any cached pointer into the old store.

Root Cause Analysis

A WebAssembly.Memory can be exposed to JS as a resizable ArrayBuffer (toResizableBuffer), and typed-array views (JSArrayBufferView) over an ArrayBuffer cache the buffer’s backing data pointer in their m_vector field for fast element access. When the underlying wasm memory grows, its base data pointer can move (the memory is reallocated/remapped), and ArrayBufferContents is updated to the new base.

Pre-patch, growing the wasm memory did not refresh the cached m_vector pointers of the views attached to that buffer, so every typed array over the resizable buffer kept pointing at the OLD backing store – a stale/dangling data pointer. Reading or writing such a view after the grow accesses freed or moved memory: a use-after-free / out-of-bounds access.

The fix adds JSArrayBufferView::refreshVector(newData), which (when the view still hasVector()) recomputes m_vector as newData + byteOffsetRaw() and stores it without a write barrier; ArrayBuffer, after a wasm-memory grow, walks its incoming references (numberOfIncomingReferences/incomingReferenceAt), downcasts each to JSArrayBufferView, and calls refreshVector(newData) so every view is repointed at the new base, while ArrayBufferContents::refreshAfterWasmMemoryGrow sets m_data = memory->basePointer().

The restored invariant is that after a wasm memory grows, all typed-array views over its resizable buffer observe the buffer’s new base pointer rather than a stale one.

Key insight
Growing a WebAssembly.Memory exposed as a resizable ArrayBuffer moved the backing store but did not refresh the data pointers cached in its typed-array views, leaving them dangling; refreshing every view’s m_vector to the new base after a grow fixes it.

Attack Path

  1. Get a resizable buffer over wasm memory Create a WebAssembly.Memory with room to grow and obtain memory.toResizableBuffer(); allocate a typed array (e.g. Uint32Array) over it, caching the current data pointer.
  2. Grow the memory Call memory.grow(n), which moves the backing store to a new base pointer and frees/unmaps the old one.
  3. Leave views stale Pre-patch, the typed array’s cached m_vector still points at the old (freed/moved) backing store.
  4. Access via the stale view Read/write ta[i]; the access dereferences the stale data pointer – a use-after-free / out-of-bounds in WebContent, groomable by allocating over the freed region.

Impact Assessment

A stale-pointer use-after-free / out-of-bounds: growing a wasm memory left typed-array views pointing at the old backing store, reachable from ordinary JS over a resizable buffer in the WebContent process. The attacker controls the grow and can reallocate the freed region, making this a strong, groomable read/write primitive rather than a mere crash. Confined to WebContent; rated medium.

Changed Functions

FunctionChangeNotes
ArrayBuffer::setAssociatedWasmMemory / grow-refresh path
Source/JavaScriptCore/runtime/ArrayBuffer.cpp
modified After a wasm memory grow changes the data pointer, iterates incoming references and calls JSArrayBufferView::refreshVector(newData) on each view so cached vectors are repointed.
JSArrayBufferView::refreshVector
Source/JavaScriptCore/runtime/JSArrayBufferViewInlines.h
added Recomputes m_vector as newData + byteOffsetRaw() (guarded by hasVector()) and stores it, so a view tracks the buffer's new backing base.
ArrayBufferContents::refreshAfterWasmMemoryGrow
Source/JavaScriptCore/runtime/ArrayBuffer.cpp
modified Sets m_data = memory->basePointer() so the contents reflect the post-grow base.
JSArrayBufferView::refreshVector (decl)
Source/JavaScriptCore/runtime/JSArrayBufferView.h
modified Declares the new refresh entry point.

Files Changed

  • JSTests/wasm/stress/resizable-buffer-grow-view-refresh.js
  • Source/JavaScriptCore/runtime/ArrayBuffer.cpp
  • Source/JavaScriptCore/runtime/JSArrayBufferView.h
  • Source/JavaScriptCore/runtime/JSArrayBufferViewInlines.h

Audit Directions

  • Other buffer-base changes
    grep JSC runtime for places that change ArrayBufferContents::m_data / basePointer (detach, transfer, resize, shared-memory remap) and confirm all incoming views are refreshed.
  • Cached m_vector consumers
    Audit JSArrayBufferView fast paths that read m_vector without revalidating against the buffer after operations that can move backing store.
  • Resizable/growable buffer APIs
    Review ArrayBuffer.prototype.resize, SharedArrayBuffer.grow, and toResizableBuffer paths for the same view-refresh requirement.

Original Bug Report

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