AppleJPEGDriver startDecoder Timeout Use-After-Free (CVE-2026-20687)
by Enfilade (enfilade.io / enfilade-labs) · 2026-07-05
- Severity
- High
- CVE
- CVE-2026-20687
- Category
- binary
- Affected product
- AppleJPEGDriver kernel extension
- Affected versions
- iOS/iPadOS 26.3 (tested on iPhone 18,2 / iPhone 17 Pro Max, A19 Pro); fixed in iOS/iPadOS 26.4
- Disclosed
- 2026-07-05
- Patch status
- patched
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-05 |
| Last Updated | 2026-04 |
| Author / Researcher | Enfilade (enfilade.io / enfilade-labs) |
| CVE / Advisory | CVE-2026-20687 |
| Category | binary |
| Severity | High |
| CVSS Score | Not specified in source |
| Status | PoC |
| Tags | ios, kernel, applejpegdriver, use-after-free, mte, iokit, kernel-panic, camera |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | AppleJPEGDriver kernel extension |
| Versions Affected | iOS/iPadOS 26.3 (tested on iPhone 18,2 / iPhone 17 Pro Max, A19 Pro); fixed in iOS/iPadOS 26.4 |
| Language / Platform | Objective-C, IOKit userspace client calling into a kernel JPEG decode driver |
| Authentication Required | No |
| Network Access Required | No (local app, physical device required) |
Summary
AppleJPEGDriver’s synchronous decode path (startDecoder_sync) can time out while a decode request is still referenced by a per-codec queue-node vector; on timeout the driver frees the request object but fails to remove its embedded queue-node pointer from that vector, leaving a dangling reference. A later queue walk (finish_io_gated / fullSpeedRequestExist) dereferences the stale node, triggering a use-after-free that is caught by ARM Memory Tagging Extension (MTE) enforcement and panics the kernel. The PoC primes the driver with a burst of asynchronous decode requests and closes the connection to induce the timeout condition; the panic itself is typically deferred until the next synchronous JPEG decode occurs, which on iPhone reliably happens when the Camera app is opened.
Vulnerability Details
Root Cause
The timeout-handling path in startDecoder_sync frees a decode request object without dequeuing its embedded node pointer from the per-codec queue vector, leaving a stale/dangling reference that is later dereferenced.
Attack Vector
- Open the
AppleJPEGDriverIOKit service from an unprivileged app. - Issue a burst of asynchronous
startDecoder_asynccalls, each of which pushes a queue node (req + 0x78) onto the driver’s per-codec vector. - Close the connection (
IOServiceClose), leaving queued state behind. - At some later point a synchronous decode (
startDecoder_sync) times out (~10s) waiting on the queue and frees the associated request — but does not remove its stale node pointer from the vector. - The next queue walk (triggered, in practice, by opening the Camera app, which performs its own synchronous JPEG decode) dereferences the stale node pointer, reading from freed memory and tripping an MTE tag-check fault that panics the kernel.
Impact
Reliable local denial of service (kernel panic / device reboot) triggerable by an unprivileged local app, with a deferred trigger condition (opening Camera) that can make the crash appear unrelated to the initiating app; the underlying use-after-free is also a plausible kernel memory-corruption primitive.
Environment / Lab Setup
Target: iOS/iPadOS 26.3 on a physical device (e.g. iPhone 17 Pro Max, A19 Pro)
Attacker: Xcode, ability to build and run a native Objective-C IOKit-calling app on the physical device
Proof of Concept
PoC Script
See
ViewController.min this folder — the on-device trigger logic meant to be built as part of an Xcode iOS app target.
| |
The button handler opens AppleJPEGDriver, issues a burst of asynchronous decode requests to populate the stale queue-node vector, then closes the connection; opening Camera afterward performs a synchronous decode that walks the corrupted queue and panics.
Detection & Indicators of Compromise
Signs of compromise:
- Kernel panic logs showing an MTE tag-check fault inside JPEG decode / queue-walk code paths (
finish_io_gated,fullSpeedRequestExist). - Panics that appear correlated with opening Camera shortly after running a third-party app with no obvious camera/JPEG functionality.
- Repeated
IOServiceOpen/startDecoder_asyncbursts againstAppleJPEGDriverfrom an unprivileged process.
Remediation
| Action | Detail |
|---|---|
| Primary fix | Update to iOS 26.4 / iPadOS 26.4 or later, which Apple states addresses this use-after-free with improved memory management |
| Interim mitigation | Avoid running untrusted third-party apps on affected builds; monitor for apps making unusual IOKit calls to AppleJPEGDriver |
References
Notes
Mirrored from https://github.com/enfilade-labs/CVE-2026-20687-AppleJPEGDriver-UAF on 2026-07-05.