Released November 3, 2025

CVE-2025-43434

CVSS 4.3 severity medium CWE-416 vector CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L

Overview

Impact:

Description: Processing maliciously crafted web content may lead to an unexpected Safari crash

Researchers: Google Big Sleep

Key insight. WasmTable/WasmGlobal stored a reference type by TypeIndex without retaining its TypeDefinition, so the definition could be freed while the object lived on — a dangling-TypeIndex UAF; retaining it via TypeInformation::getRef fixes it.

Patch Details

diff --git a/Source/JavaScriptCore/wasm/WasmGlobal.h b/Source/JavaScriptCore/wasm/WasmGlobal.h
index d6e7029f402a..970a04f88133 100644
--- a/Source/JavaScriptCore/wasm/WasmGlobal.h
+++ b/Source/JavaScriptCore/wasm/WasmGlobal.h
@@ -97,6 +97,7 @@ public:
 private:
     Global(Wasm::Type type, Wasm::Mutability mutability, uint64_t initialValue)
         : m_type(type)
+        , m_typeDefinition(TypeInformation::getRef(type.index))
         , m_mutability(mutability)
     {
         ASSERT(m_type != Types::V128);
@@ -105,6 +106,7 @@ private:
 
     Global(Wasm::Type type, Wasm::Mutability mutability, v128_t initialValue)
         : m_type(type)
+        , m_typeDefinition(TypeInformation::getRef(type.index))
         , m_mutability(mutability)
     {
         ASSERT(m_type == Types::V128);
@@ -112,6 +114,8 @@ private:
     }
 
     Wasm::Type m_type;
+    // If m_type came from a TypeDefinition, the following retains the definition to prevent a dangling m_type.
+    RefPtr<const Wasm::TypeDefinition> m_typeDefinition;
     Wasm::Mutability m_mutability;
     JSWebAssemblyGlobal* m_owner { nullptr };
     Value m_value;
diff --git a/Source/JavaScriptCore/wasm/WasmTable.cpp b/Source/JavaScriptCore/wasm/WasmTable.cpp
index 535b107053f9..be6b6df2e054 100644
--- a/Source/JavaScriptCore/wasm/WasmTable.cpp
+++ b/Source/JavaScriptCore/wasm/WasmTable.cpp
@@ -86,6 +86,7 @@ Table::Table(uint32_t initial, std::optional<uint32_t> maximum, Type wasmType, T
     : m_maximum(maximum)
     , m_type(type)
     , m_wasmType(wasmType)
+    , m_wasmTypeDefinition(TypeInformation::getRef(wasmType.index))
     , m_isFixedSized(maximum && maximum.value() == initial)
     , m_owner(nullptr)
 {
diff --git a/Source/JavaScriptCore/wasm/WasmTable.h b/Source/JavaScriptCore/wasm/WasmTable.h
index 9ab585feebde..813319e03a19 100644
--- a/Source/JavaScriptCore/wasm/WasmTable.h
+++ b/Source/JavaScriptCore/wasm/WasmTable.h
@@ -104,6 +104,8 @@ protected:
     NO_UNIQUE_ADDRESS const std::optional<uint32_t> m_maximum;
     const TableElementType m_type;
     Type m_wasmType;
+    // If m_wasmType came from a TypeDefinition, the following retains the definition to prevent a dangling m_wasmType.
+    RefPtr<const TypeDefinition> m_wasmTypeDefinition;
     bool m_isFixedSized { false };
     JSWebAssemblyTable* m_owner;
 };
diff --git a/Source/JavaScriptCore/wasm/WasmTypeDefinition.h b/Source/JavaScriptCore/wasm/WasmTypeDefinition.h
index 09328c19e57b..409fd2803524 100644
--- a/Source/JavaScriptCore/wasm/WasmTypeDefinition.h
+++ b/Source/JavaScriptCore/wasm/WasmTypeDefinition.h
@@ -958,6 +958,8 @@ public:
 
     static const TypeDefinition& get(TypeIndex);
     static TypeIndex get(const TypeDefinition&);
+    // Unlike with `get`, the index passed to `getRef` may be a type or invalid, in which case a nullptr is returned.
+    static RefPtr<const TypeDefinition> getRef(TypeIndex);
 
     inline static const FunctionSignature& getFunctionSignature(TypeIndex);
     inline static std::optional<const FunctionSignature*> tryGetFunctionSignature(TypeIndex);
diff --git a/Source/JavaScriptCore/wasm/WasmTypeDefinitionInlines.h b/Source/JavaScriptCore/wasm/WasmTypeDefinitionInlines.h
index 1c8329d17afd..672a8201c5af 100644
--- a/Source/JavaScriptCore/wasm/WasmTypeDefinitionInlines.h
+++ b/Source/JavaScriptCore/wasm/WasmTypeDefinitionInlines.h
@@ -83,6 +83,13 @@ inline TypeIndex TypeInformation::get(const TypeDefinition& type)
     return type.index();
 }
 
+inline RefPtr<const TypeDefinition> TypeInformation::getRef(TypeIndex typeIndex)
+{
+    if (typeIndexIsType(typeIndex) || typeIndex == TypeDefinition::invalidIndex)
+        return nullptr;
+    return TypeInformation::get(typeIndex);
+}
+
 } } // namespace JSC::Wasm
 
 #endif // ENABLE(WEBASSEMBLY)

Background

Advisory: Apple Security Advisory

Attribute Value
CVE CVE-2025-43434
Bugzilla 297958
Component JSC
Bug Class UAF
Severity medium
Commit ef9304e0e82bf304…
Advisory Apple Advisory

Terminology

  • Wasm reference type / TypeIndex — A reference-typed value carries a TypeIndex into the interned TypeDefinition registry describing its type.
  • TypeDefinition lifetime — TypeDefinitions are ref-counted and freed when nothing retains them; a bare TypeIndex does not keep one alive.
  • WasmTable / WasmGlobal — Long-lived wasm objects that can outlive the module which defined their element/value type.
  • getRef — The new accessor that returns a strong reference to a TypeIndex’s definition, or nullptr for primitives/invalid indices.

Root Cause Analysis

JavaScriptCore’s WebAssembly Table and Global store their element/value type as a Wasm::Type that, for reference types, carries a TypeIndex into the interned TypeInformation registry. Pre-patch, WasmTable and WasmGlobal held that Wasm::Type (m_wasmType / m_type) WITHOUT retaining the underlying TypeDefinition. When the module that defined the type went away, the TypeDefinition the TypeIndex refers to could be freed while the Table/Global lived on, leaving m_type’s TypeIndex dangling; a later type-directed operation on the table/global dereferences the freed TypeDefinition — a use-after-free. The fix retains the definition: WasmTable gains RefPtr<const TypeDefinition> m_wasmTypeDefinition and WasmGlobal gains RefPtr<const Wasm::TypeDefinition> m_typeDefinition, both initialized with a new TypeInformation::getRef(type.index) that returns the interned TypeDefinition (or nullptr when the index is a primitive type or invalid). Holding that RefPtr keeps the definition alive for the object’s lifetime, so the TypeIndex cannot dangle. The restored invariant is that a Table/Global naming a reference type retains that type’s definition. (Note: this fix retains the directly-named definition; a later follow-up extended retention to the whole transitive type closure after finding nested types could still dangle.)

Attack Path

1. Create a typed table/global

Build a wasm module with a table or global whose element/value type is a reference to a module-defined type (carrying a TypeIndex).

2. Outlive the module

Keep the exported table/global reachable while dropping references to the defining module so its TypeDefinitions can be freed.

3. Free the definition

Garbage collection reclaims the TypeDefinition the table/global’s Wasm::Type still indexes.

4. Use-after-free

A type-directed access to the table/global dereferences the dangling TypeIndex, corrupting memory in WebContent.

Impact Assessment

A use-after-free of a wasm TypeDefinition retained only by index in a Table/Global that outlives its module, reachable from crafted wasm in the WebContent process. GC timing is attacker-influenced, giving a groomable UAF; the observable is a crash. Confined to WebContent; rated medium. This is the root fix of a class later hardened (transitive retention) in a follow-up.

Audit Directions

Other bare TypeIndex holders

grep wasm/ for Wasm::Type/TypeIndex members not paired with a retained TypeDefinition (tags, exceptions, caches).

Transitive retention

Confirm follow-up work retains the full transitive type closure, since a directly-retained definition can still reference unretained nested types.

getRef adoption

Ensure every place that stores a reference-typed Wasm::Type long-term uses getRef to retain it.

Changed Functions

Function File Change Note
Table::Table / m_wasmTypeDefinition Source/JavaScriptCore/wasm/WasmTable.cpp modified Retains the table element type’s TypeDefinition via RefPtr m_wasmTypeDefinition = TypeInformation::getRef(wasmType.index).
Global::Global / m_typeDefinition Source/JavaScriptCore/wasm/WasmGlobal.h modified Retains the global value type’s TypeDefinition via RefPtr m_typeDefinition for both constructors.
TypeInformation::getRef Source/JavaScriptCore/wasm/WasmTypeDefinitionInlines.h added Returns a RefPtr<const TypeDefinition> for a TypeIndex (nullptr for primitive/invalid indices), used to retain reference-type definitions.

Files Changed

Source Files

  • Source/JavaScriptCore/wasm/WasmGlobal.h
  • Source/JavaScriptCore/wasm/WasmTable.cpp
  • Source/JavaScriptCore/wasm/WasmTable.h
  • Source/JavaScriptCore/wasm/WasmTypeDefinition.h
  • Source/JavaScriptCore/wasm/WasmTypeDefinitionInlines.h