CVE-2025-43536
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