The iPhone stayed locked. ImageIO processed the picture anyway.
A crafted animated image crashed the message-preview service on my locked test iPhone. I traced it to disagreement over frame metadata and a numerical check that accepted impossible geometry.
The receiving iPhone was locked, its screen off, when the image reached the message-preview machinery. I left it untouched. The system service processing the image crashed, and its working state contained a value I had selected. Opening the attachment was not required in that test; the work done before anyone read the message was enough.
That was the most consequential delivery result in my ImageIO submission. The underlying failure concerned an animated image’s frame boundaries, but it surfaced in a service that handled incoming message content automatically. A decoder does not know whether its input arrived through an app somebody opened or a preview the operating system prepared on its own.
Apple credits Chris Bailey - Short Circuit, Meta Red Team X, Niels Hofmans and Geonha Lee (@leegn4a) for CVE-2026-86869. Its public entry describes unexpected application termination when a crafted image is processed, corrected with improved bounds checking. My report documents the specific memory corruption and the conditions of the locked-phone test.
Apple’s ImageIO entry and shared researcher credit
The preview had already started reading
An image has to be interpreted before a device can display it. That includes the small previews and thumbnails that make an interface convenient, not only the full-size image a person deliberately opens. In the messaging test, a sandboxed service responsible for pre-processing content from an unknown sender reached the defective decoder while preparing the preview.
The sandbox is relevant. It separates untrusted message processing from other work on the device. The fact that the image reached a bug inside that service did not mean the sandbox had been escaped. It established that this particular parsing failure could be triggered without a tap in the tested environment. “Zero-click” describes that lack of interaction; it does not automatically describe everything an attacker can achieve afterward.
Other tests reached the shared decoder through browser rendering and thumbnail generation. Their delivery conditions differed. Loading a page, requesting a thumbnail and receiving a message are not interchangeable experiments merely because the same image-processing code eventually runs. The locked-device observation belongs to the messaging test.
The image readers disagreed about the frame
The file format was Animated PNG, which can describe successive frames inside an image canvas. A frame has both content and geometry: where it belongs and how much space it occupies. The decoder needs that information to assemble the animation without writing outside the memory reserved for the canvas.
ImageIO performed an initial pass over the animation metadata before the main PNG decoder processed the image. The initial pass recorded frame information without enforcing the declared structure’s length correctly. The later decoder had a stricter check and rejected the malformed frame description. Unfortunately, its rejection did not erase the geometry that the earlier pass had already retained.
During that initial pass, ImageIO also checked whether the frame fit inside the canvas. The horizontal and vertical calculations were similar, but they did not interpret their numbers the same way. The horizontal comparison treated the relevant value as unsigned. The vertical comparison treated it as signed, allowing a large value to be interpreted as negative.
A signed number can represent values below zero; an unsigned number cannot. Those interpretations give the same stored bits different meanings. Here, geometry that should have placed the frame beyond the canvas could look smaller than the limit when read as a negative number. The vertical check accepted it. The horizontal check was not a substitute for checking the other direction correctly.
The combination left the image compositor—the code that places frame pixels onto the canvas—using invalid geometry. Its writes went outside the canvas allocation. This was not simply a picture with an unusual shape: the metadata had changed where the program wrote in its own memory.
The value in the crash had to be the one I chose
A crash proves that something failed. It does not, by itself, prove useful control over the failure. My evidence included a value selected for the current device boot appearing intact in a processor register when the image-processing operation faulted. A register is temporary working storage used by the processor; its contents show part of the state the program was using at that moment.
The device state and the crash belonged to the same boot session. That association mattered because the reference value could change across reboots. A matching-looking crash from a different session would not have supported the comparison. The report also included a separate thumbnail test using a different chosen value, which appeared in the corresponding crash. That helped rule out an incidental constant being mistaken for control.
There was a measurement wrinkle in the checking tool. A process can have several threads doing different work, each with its own register state. The generic checker combined values across those threads and could lose the relevant value in its summary. The corrected browser-rendering result examined the thread that actually faulted. The evidence needed that thread’s state, not a convenient mixture of everybody’s.
A controlled crash was the demonstrated outcome
The selected register value did not give me control over the instructions the processor executed. I did not demonstrate code execution, arbitrary memory access, information disclosure or an escape from the message-processing sandbox. The write’s destination was relative to storage allocated by the decoder, and the experiment did not supply a general way to locate and alter any address I wanted.
The tested builds and Apple’s release list answer different questions
The research compared affected iOS and macOS 27 development builds with particular earlier 26-series builds whose bounds checks behaved differently. That supported the report’s description of a regression in the code I examined. It did not establish that every 26-series version, image-processing path or vulnerability grouped under this public entry was unaffected.
Apple’s published CVE coverage includes an iOS and iPadOS 26.7 correction as well as macOS Golden Gate 27. The public advisory is broader evidence about where Apple shipped a fix; it does not explain how every credited report maps onto every implementation. I therefore retain both observations: the build comparison from my research and the release coverage Apple publishes.
The September 14, 2026 advisory entries are:
Install the current supported update offered for the device. The test illustrated why waiting to open an unfamiliar attachment is not a complete protection against image-processing bugs: some of the parsing may already have happened to produce the preview.
Thank you to Apple for addressing the issue, and to Meta Red Team X, Niels Hofmans and Geonha Lee for the contributions recognized in the shared credit.
The five September Apple CVE credits
Related research: controlled corruption in a CoreMedia service
Apple’s advisories checked October 26, 2026.