CVE-2025-43434
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.hSource/JavaScriptCore/wasm/WasmTable.cppSource/JavaScriptCore/wasm/WasmTable.hSource/JavaScriptCore/wasm/WasmTypeDefinition.hSource/JavaScriptCore/wasm/WasmTypeDefinitionInlines.h