CVE-2026-86924: A Bluetooth Heap Overflow in uarpd
A malformed Bluetooth accessory update made uarpd copy five times more data than it allocated. Apple fixed the heap overflow and assigned CVE-2026-86924.
In June, I found a heap overflow in Apple's accessory update path. A connected Bluetooth accessory could send a malformed update asset that caused uarpd to write past the end of a heap allocation.
The payload I used made the daemon reserve 64 bytes and then copy 320 bytes into that allocation. The remaining 256 bytes crossed the heap boundary, and every one of them came from data supplied by the accessory.
Apple assigned CVE-2026-86924 and credited me under MobileAccessoryUpdater. The fixes shipped on September 14, 2026. Apple's macOS advisory documents the issue and credit.
A Bluetooth path into uarpd
The input was a dynamic update asset delivered through Apple's accessory update protocol. On the Mac, it reached /usr/libexec/uarpd, the daemon responsible for processing that data.
I used a software accessory I controlled to offer the malformed asset over Bluetooth. The vulnerable operation ran after the Mac connected and accepted the update data. I reproduced it on an Apple silicon Mac running the current macOS release at the time of testing.
The asset reached the parser through the normal accessory path, not through a direct function call or debugger modification. That established the real attack path from a connected accessory into the system daemon.
Where the bounds check failed
The asset described the destination size in its outer metadata. Data chunks inside the asset carried a second length describing how much output the parser should produce. uarpd allocated memory from the outer value, then trusted the inner value when it copied the chunk.
I traced that relationship to -[UARPSuperBinaryPayloadLayer3 decompressPayload]. The relevant logic reduces to this reconstructed pseudocode:
size_t capacity = outer_metadata.chunk_size;
void *dst = calloc(capacity, 1);
size_t output_length = inner_chunk.output_size;
// Missing: output_length <= capacity
copy_or_decompress(dst, chunk_data, output_length);
The names are simplified here. The missing comparison is the part that matters.
The parser never checked that the second length fit the first allocation. In my proof, that disagreement turned a 64-byte destination into a 320-byte copy. The copy completed with 256 bytes of accessory-controlled data beyond the allocated buffer.
The same missing capacity check affected the parser's copy and decompression paths. I used the simplest path needed to establish the bug and reported the broader boundary issue to Apple without publishing the asset format or a working trigger.
Proving the corruption
A process crash demonstrates memory corruption, but it does not show how much control the input provides. After the overflow, uarpd later consumed metadata that had been damaged by the extra bytes. That gave me a way to show that the corruption affected a pointer used for a subsequent read.
I used Apple's Commpage Target Flag test for the final proof. The submitted run had System Integrity Protection enabled and no debugger attached. Instead of only producing a random fault, the daemon read from the per-boot address selected for Apple's test. Apple's Target Flag documentation describes what that result establishes.
The proof demonstrated a controlled memory read inside the userspace daemon. It did not demonstrate code execution, root access, a sandbox escape, or the return of memory contents to the accessory. Those are separate outcomes, and I did not claim them in the report.
Scope and impact
The evidence establishes delivery from a connected Bluetooth accessory. In my recorded setup, the Mac initiated the connection. I did not establish that an arbitrary, never-paired Mac would connect to a malicious accessory without user action.
Apple published the following impact statement:
Connecting a malicious accessory may cause unexpected system termination.
The underlying issue was an out-of-bounds heap write in the accessory update service. Apple listed the fix across macOS, iOS, and iPadOS. My detailed reproduction was on macOS, so the other affected platforms reflect Apple's published coverage rather than separate testing I performed.
The fix
Apple described the correction as improved input validation. The parser must reject any chunk whose claimed output is larger than the destination allocated for it. That check has to happen before either a copy or decompression begins.
Apple's advisories list these fixed releases:
| Platform | Fixed release | Release date |
|---|---|---|
| iOS and iPadOS | iOS 27 and iPadOS 27 | September 14, 2026 |
| macOS | macOS Golden Gate 27 | September 14, 2026 |
| iOS and iPadOS | iOS 26.7 and iPadOS 26.7 | September 14, 2026 |
| macOS | macOS Tahoe 26.7 | September 14, 2026 |
Timeline
| Date | Event |
|---|---|
| June 7, 2026 | Recorded the original finding in my research notes. |
| June 9, 2026 | Demonstrated controlled memory access and submitted the issue privately to Apple. |
| September 14, 2026 | Apple published fixes and credited Matthew Zamat for CVE-2026-86924. |