Released December 12, 2025

CVE-2025-43536

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 process crash

Researchers: Nan Wang (@eternalsakura13)

Key insight. Jettison was resetting each inline-cache stub — re-entering CodeBlock/structure state that is already being destroyed — when it should simply release the stub’s reference; reset-at-teardown was the use-after-free.

Patch Details

diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index fd1dd5c7e09b..919f203ef225 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -2285,7 +2285,7 @@ void CodeBlock::jettison(Profiler::JettisonReason reason, ReoptimizationMode mod
     {
         ConcurrentJSLocker locker(m_lock);
         forEachStructureStubInfo([&](StructureStubInfo& stubInfo) {
-            stubInfo.reset(locker, this);
+            stubInfo.deref();
             return IterationStatus::Continue;
         });
     }

Background

Advisory: Apple Security Advisory

Attribute Value
CVE CVE-2025-43536
Bugzilla 301726
Component JSC
Bug Class UAF
Severity medium
Commit 1025bd18c9c7bd3d…
Advisory Apple Advisory

Terminology

  • CodeBlock jettison — JSC’s action of discarding optimized bytecode/JIT code when it is invalidated or reoptimized, tearing down associated metadata.
  • StructureStubInfo — Per-inline-cache metadata for property access; reset() returns it to an uncached state, deref() releases a reference to it.
  • ConcurrentJSLocker — The lock guarding concurrent access to CodeBlock state between the mutator and compiler threads.

Root Cause Analysis

This fixes a use-after-free during CodeBlock jettison in JavaScriptCore’s inline-cache teardown. CodeBlock::jettison() throws away optimized/older code; under the ConcurrentJSLocker it iterated every StructureStubInfo (the per-inline-cache metadata for get/put/in operations) and called stubInfo.reset(locker, this). reset() re-initializes the stub back to its uncached state, which touches structures, watchpoints and the owning CodeBlock (passed as this) — objects that during jettison may already be undergoing teardown, so re-initializing the stub there dereferences state that is being destroyed. The one-line fix replaces stubInfo.reset(locker, this) with stubInfo.deref(), so jettison simply releases the stub’s reference (it is reference-counted) rather than resetting and re-touching now-fragile CodeBlock/structure state. The restored invariant is that jettison only drops ownership of its inline-cache stubs and does not walk into their reset path during code-block teardown. Because the diff is a single call-site change, the exact object whose lifetime was violated is inferred from the reset(locker, this) signature (it re-enters CodeBlock/structure state); the patch clearly establishes that resetting stubs at jettison time was unsafe and dereferencing/releasing is the correct action.

Attack Path

1. Trigger tiered compilation

Run JS that builds inline caches (StructureStubInfos) in an optimized CodeBlock.

2. Force a jettison

Cause reoptimization/invalidation so CodeBlock::jettison() runs and iterates the stub infos.

3. Re-enter fragile state

reset(locker, this) re-initializes each stub, touching CodeBlock/structure state already being torn down — a use-after-free.

4. Crash or corrupt

The dangling access crashes or corrupts JSC heap state in the WebContent process.

Impact Assessment

A use-after-free confined to the WebContent process, reachable by driving inline-cache creation and forcing jettison. Teardown-time UAFs in JSC metadata are exploitable with grooming, though this particular site (releasing vs resetting a stub during jettison) is a narrower primitive than a direct object-reuse bug; the advisory rates it a process crash.

Audit Directions

Other jettison/teardown loops

Grep CodeBlock.cpp and JIT teardown for reset(locker, this) or similar re-initialization calls during destruction/jettison where a deref/clear is the safe action.

StructureStubInfo lifetime

Review all StructureStubInfo::reset callers to ensure the owning CodeBlock and referenced structures are fully live at reset time.

Changed Functions

Function File Change Note
CodeBlock::jettison Source/JavaScriptCore/bytecode/CodeBlock.cpp modified In the forEachStructureStubInfo loop, replaces stubInfo.reset(locker, this) with stubInfo.deref(), releasing the stub reference instead of re-initializing it during jettison.

Files Changed

Source Files

  • Source/JavaScriptCore/bytecode/CodeBlock.cpp