Medium CVSS 6.5 webkit Logic Error 🔧 Commit mapped

Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing web content may lead to a denial-of-service
ComponentJSC Wasm
Bug ClassLogic Error
Tracker283889
Fix commit8964a07ea3fb (WebKit/WebKit)
CWECWE-79 (Cross-site scripting)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H
CISA KEVNot listed
CreditedQ1IQ (@q1iqF) of NUS CuriOSity and P1umer (@p1umer) of Imperial Global Singapore
Disclosed2025-01-27

Background

BBQ JIT
JavaScriptCore’s baseline optimizing wasm compiler with its own register allocator.
allocate vs loadIfNecessary
allocate() forces a fresh register for a value; loadIfNecessary() reuses the value’s existing location if it already has one.
shouldThrow value
A computed flag operand for bulk table/memory ops that must reside in a register for the trap check.
Register pressure
When few registers are free, forcing an allocation on an already-located value breaks allocator invariants.

Root Cause Analysis

JavaScriptCore’s Wasm BBQ JIT emits table and memory bulk operations (addTableSet, addTableInit, addTableFill, addTableCopy, addMemoryFill, addMemoryCopy, addMemoryInit) and some atomics/array-segment ops that materialize a ‘shouldThrow’/result value into a register. These call sites used allocate(shouldThrow) / allocate(result), which unconditionally allocates a fresh register location for the value. When the value is already materialized in a location (or when register pressure means allocate cannot proceed as assumed), forcing a new allocation drives the BBQ register allocator into an inconsistent/invalid state, hitting a release assertion or otherwise aborting – a reliably reachable crash (denial-of-service).

The fix replaces allocate(...) with loadIfNecessary(...) at these sites, which brings the value into a register only if it is not already in one, matching how the value is actually used.

The restored invariant is that these bulk table/memory operation operands are materialized with loadIfNecessary rather than a forced allocate, keeping the register allocator consistent and avoiding the crash.

Key insight
BBQ-JIT bulk table/memory ops called allocate() on values that were already located, driving the register allocator into an invalid state and crashing; using loadIfNecessary() restores consistency.

Attack Path

  1. Craft wasm with bulk table/memory ops Build a module whose functions use table.set/init/fill/copy or memory.fill/copy/init under register pressure.
  2. Compile via BBQ JIT Instantiation compiles the ops, which call allocate() on a value that is already located.
  3. Corrupt allocator state The forced allocate drives the register allocator into an invalid state.
  4. Denial-of-service A release assertion/abort crashes the WebContent process (reliable DoS).

Impact Assessment

A denial-of-service (reliable crash) from BBQ-JIT register mishandling in bulk table/memory ops, reachable from crafted wasm in the WebContent process. Per the advisory the impact is DoS rather than memory corruption; the fix restores allocator consistency. Confined to WebContent; rated medium.

Changed Functions

FunctionChangeNotes
BBQJIT::addTableSet/Init/Fill/Copy, addMemoryFill/Copy/Init, atomicWait/Notify, pushArrayNewFromSegment
Source/JavaScriptCore/wasm/WasmBBQJIT.cpp
modified Uses loadIfNecessary(shouldThrow)/loadIfNecessary(result) instead of allocate(...), materializing the value only when needed and keeping the register allocator consistent.

Audit Directions

  • Other allocate() misuse
    grep WasmBBQJIT for allocate(value) where the value may already be materialized and loadIfNecessary is the correct call.
  • Bulk op operand handling
    Audit table/memory/array bulk-op operand materialization for consistent load/allocate/consume discipline.
  • Register-exhaustion asserts
    Review RELEASE_ASSERTs in the BBQ allocator that a crafted module could deterministically trip.

Original Bug Report

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