Critical CVSS 8.8 webkit OOB CISA KEV 🔧 Commit mapped

Overview

Critical
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may lead to arbitrary code execution. Apple is aware of a report that this issue may have been actively exploited on Intel-based Mac systems.
ComponentJSC DFG
Bug ClassOOB
Tracker283063
Fix commit82abacffb221 (WebKit/WebKit)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVListed
CreditedClément Lecigne and Benoît Sevens of Google's Threat Analysis Group
Disclosed2024-11-19

Background

DFG SpeculativeJIT
JavaScriptCore’s speculative optimizing JIT that emits machine code for typed-array stores.
Scratch register (scratch2)
A temporary GPR the emitted code needs; it must be reserved before other operands claim registers so it cannot alias.
Resizable/growable typed array
Typed arrays over resizable ArrayBuffers / growable SharedArrayBuffers needing extra length handling.

Root Cause Analysis

This fixes a DFG JIT register-allocation ordering bug in JavaScriptCore’s typed-array store fast path. In SpeculativeJIT::compilePutByValForIntTypedArray, on JSVALUE64 a scratch2 GPR is reserved only when the array mode may be a resizable-or-growable-shared typed array (mayBeResizableOrGrowableSharedTypedArray()), where it is needed for the extra length/bounds handling those arrays require.

Before the fix, scratch2 was allocated AFTER getIntTypedArrayStoreOperand(…) had already assigned value/property/scratch registers; the fix moves the scratch2 reservation to BEFORE that call. The violated invariant is that all scratch registers a code path needs must be reserved before other operands claim registers, so they cannot alias. Allocating scratch2 late means it could collide with a register already in use for the store, so for resizable/growable typed arrays the generated code used a clobbered register in the bounds check or store-address computation, producing an out-of-bounds write in the JIT-compiled store. That is a powerful memory-corruption primitive, consistent with the critical ‘arbitrary code execution’ impact and Apple’s note of active exploitation on Intel Macs. Reordering the allocation guarantees scratch2 is a distinct register. INFERENCE: the exact clobbered register/instruction is not printed by the diff; the commit establishes the allocation-order change on the resizable-typed-array path.

Key insight
All scratch registers a JIT path needs must be reserved before operand setup; reserving scratch2 after getIntTypedArrayStoreOperand let it alias a live register and mis-compile the store.

Attack Path

  1. Create a resizable/growable typed array JS allocates a typed array backed by a resizable ArrayBuffer or growable SharedArrayBuffer so arrayMode().mayBeResizableOrGrowableSharedTypedArray() holds.
  2. Trigger DFG compilation of a store Hot code repeatedly writes into the typed array so the DFG compiles compilePutByValForIntTypedArray for it.
  3. Miscompiled store uses a clobbered register With scratch2 reserved too late, the emitted bounds/address computation aliases a live register on the resizable path.
  4. Out-of-bounds write The store writes outside the typed array’s backing store, corrupting adjacent heap memory.
  5. Escalate to code execution With the heap groomed, the OOB write corrupts an adjacent object to build an exploitation primitive and achieve arbitrary code execution (standard escalation; actively exploited on Intel Macs per the advisory).

Impact Assessment

An out-of-bounds write from a JIT register-allocation ordering bug on the resizable-typed-array path; critical and actively exploited on Intel Macs. A classic strong primitive that groom-and-corrupt escalates to arbitrary code execution in WebContent.

Changed Functions

FunctionChangeNotes
SpeculativeJIT::compilePutByValForIntTypedArray
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
modified Moves the JSVALUE64 scratch2 GPR reservation for resizable/growable-shared typed arrays to before getIntTypedArrayStoreOperand(), preventing the scratch register from aliasing operands and mis-computing the store on that path.

Audit Directions

  • Scratch allocation ordering in the JITs
    Audit DFG/FTL emitters where scratch/tmp registers are reserved after operand materialization; look for early-return paths between reservation and use.
  • Resizable/growable typed-array code paths
    Review mayBeResizableOrGrowableSharedTypedArray branches for correct bounds/length register usage.

Original Bug Report

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