Medium CVSS 8.8 webkit UAF 🔧 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 ClassUAF
Tracker298194
Fix commitd4b9e6993567 (WebKit/WebKit) +2557/-13
CWECWE-787, CWE-119 (Out-of-bounds write, 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
CreditedGoogle Big Sleep
Disclosed2025-11-03

Background

Wasm GC Subtype
A non-final struct/array type that wraps an underlying Struct/Array TypeDefinition.
Expanded vs unexpanded type
m_type stores the expanded underlying type; FieldTypes hold raw pointers into the original unexpanded Subtype.
TypeInformation::tryCleanup()
GC-time cleanup that can free TypeDefinitions no longer strongly referenced.

Root Cause Analysis

This fixes a use-after-free of a WebAssembly GC type definition caused by only retaining the expanded type. A struct/array type can be a Subtype that wraps an underlying Struct/Array TypeDefinition; WebAssemblyGCStructure stored only m_type, set to the expanded underlying type (typeSignatures[i]->expand()). But, as the added comment states, the original unexpanded Subtype ‘may be the target of raw pointers in Wasm::Types of FieldTypes’ — field types hold raw pointers into the unexpanded Subtype. Because nothing kept the unexpanded Subtype alive, TypeInformation::tryCleanup() could free that TypeDefinition during GC while those raw FieldType pointers still referenced it, giving a dangling pointer and memory corruption.

The fix threads the unexpanded type through: JSWebAssemblyInstance::finishCreation now passes typeSignatures[i] (unexpanded) rather than ->expand(); JSWebAssemblyArray/Struct::createStructure expand internally and pass BOTH the unexpanded and expanded types; and WebAssemblyGCStructure gains a Ref<const Wasm::TypeDefinition> m_unexpandedType member (copied on structure transition) that keeps the Subtype alive for the structure’s lifetime.

The restored invariant is that any TypeDefinition referenced by raw pointers from FieldTypes remains owned as long as the structure exists. The regression test builds structs using a Subtype (isFinal=false), drops references, and GCs repeatedly to drive tryCleanup() into freeing the type. Established by the diff (the wasm-module-builder-gc.js harness is omitted but only provides module-building helpers).

Key insight
If any object is referenced by raw pointers (FieldTypes into a Subtype), that object must be strongly retained; retaining only the expanded type left the unexpanded Subtype collectable.

Attack Path

  1. Instantiate a Wasm GC module using subtypes The page builds a module whose struct/array types are non-final (Subtypes) and instantiates it.
  2. Create GC objects and drop references It creates struct/array objects, then releases the references that would keep the underlying Subtype TypeDefinition alive.
  3. Force type cleanup via GC Repeated gc() runs TypeInformation::tryCleanup(), freeing the unexpanded Subtype TypeDefinition that FieldType raw pointers still reference.
  4. Dangling-pointer memory corruption Subsequent use of the structure dereferences the freed TypeDefinition through the FieldType raw pointers, corrupting memory / crashing.

Impact Assessment

A dangling-pointer memory corruption when the unexpanded Subtype is freed while FieldType raw pointers still reference it; medium but a genuine UAF in the Wasm GC type system.

Changed Functions

FunctionChangeNotes
JSWebAssemblyInstance::finishCreation
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp
modified Passes the unexpanded typeSignatures[i] to JSWebAssemblyArray/Struct::createStructure instead of typeSignatures[i]->expand().
WebAssemblyGCStructure::WebAssemblyGCStructure / create
Source/JavaScriptCore/wasm/js/WebAssemblyGCStructure.cpp
modified Takes both unexpandedType and expanded type and stores m_unexpandedType (also copied in the structure-transition constructor).
JSWebAssemblyArray::createStructure
Source/JavaScriptCore/wasm/js/JSWebAssemblyArrayInlines.h
modified Expands the type locally, asserts on the expanded ArrayType, and forwards both unexpanded and expanded types to WebAssemblyGCStructure::create.
JSWebAssemblyStruct::createStructure
Source/JavaScriptCore/wasm/js/JSWebAssemblyStruct.h
modified Same pattern for structs: expands locally and forwards both unexpanded and expanded types.
WebAssemblyGCStructure (class)
Source/JavaScriptCore/wasm/js/WebAssemblyGCStructure.h
modified Adds Ref<const Wasm::TypeDefinition> m_unexpandedType so the original Subtype (referenced by FieldType raw pointers) stays alive for the structure's lifetime.

Files Changed

  • JSTests/wasm/regress/298194.js
  • JSTests/wasm/spec-harness/wasm-module-builder-gc.js
  • Source/JavaScriptCore/wasm/js/JSWebAssemblyArrayInlines.h
  • Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp
  • Source/JavaScriptCore/wasm/js/JSWebAssemblyStruct.h
  • Source/JavaScriptCore/wasm/js/WebAssemblyGCStructure.cpp
  • Source/JavaScriptCore/wasm/js/WebAssemblyGCStructure.h

Audit Directions

  • Raw pointers into TypeDefinitions
    Audit Wasm::Type/FieldType raw pointers and confirm the pointed-to TypeDefinition (expanded and unexpanded) is owned for the referrer’s lifetime.
  • expand() call sites
    Find places that store only expand()ed types and check whether the pre-expansion Subtype must also be retained.

Original Bug Report

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