Back Blogger A 0-click exploit chain for the Pixel 9 Part 1: Decoding Dolby
Over the past few years, several AI-powered features have been added to mobile phones that allow users to better and understand their messages. One effect of this change is increased 0-click attack surface, as efficient analysis often requires message media to be decoded before the message is opened by the user. One such feature is audio transcription. Incoming SMS and RCS audio attachments received by Google Messages are now automatically decoded with no user interaction. As a result, audio decoders are now in the 0-click attack surface of most Android phones.
I’ve spent a fair bit of time investigating these decoders, first reporting CVE-2025-49415 in the Monkey’s Audio codec on Samsung devices. Based on this research, the team reviewed the Dolby Unified Decoder, and Ivan Fratric and I reported CVE-2025-54957 . This vulnerability is likely in the 0-click attack surface of most Android devices in use today. In parallel, Seth Jenkins investigated a driver accessible from the sandbox the decoder runs in on a Pixel 9, and reported CVE-2025-36934.
As I’ve shared this research, vendors as well as members of the security community have questioned whether such vulnerabilities are exploitable, as well as whether 0-click exploits are possible for all but the most well-resourced attackers in the modern Android Security environment. We were also asked whether code execution in the context of a media decoder is practically useful to an attacker and how platforms can reduce the risks such a capability presents to users.
To answer these questions, Project Zero wrote a 0-click exploit chain targeting the Pixel 9. We hope this research will help defenders better understand how these attacks work in the wild, the strengths and weaknesses of Android’s security features with regards to preventing such attacks, and the importance of remediating media and driver vulnerabilities on mobile devices.
The exploit will be detailed in three blog posts.
Part 1 of this series will describe how we exploited CVE-2025-54957 to gain arbitrary code execution in the mediacodec context of a Google Pixel 9.
Part 2 of this series will describe how we exploited CVE-2025-36934 to escalate privileges from mediacodec to kernel on this device.
Part 3 will lessons learned and recommendations for preventing similar exploits on mobile devices.
The vulnerabilities discussed in these posts were fixed as of January 5, 2026.
The Dolby Unified Decoder component (UDC) is a library that provides support for the Dolby Digital (DD) and Dolby Digital Plus (DD+) audio formats. These formats are also known as AC-3 and EAC-3 respectively. A public specification is available for these formats. The UDC is integrated into a variety of hardware and platforms, including Android, iOS, Windows and media streaming devices. It is shipped to most OEMs as a binary ‘blob’ with limited symbols, which is then statically linked into a shared library. On the Pixel 9, the UDC is integrated into /vendor/lib64/libcodec2_soft_ddpdec.so .
DD+ audio is processed from a bitstream, which consists of independently decodable syncframes, each representing a series of audio samples. During normal operation, the UDC consecutively decodes each syncframe from the bitstream.
One element of a syncframe is the audio block which, according to the specification, can contain the following fields. A syncframe can contain up to 6 audio blocks.
This means the decoder can copy up to 0x1FF ( skipl ) bytes per audio block from the bitstream into a buffer we’ll call the ‘skip buffer’.
The skip buffer contains data in a format called Extensible Metadata Delivery Format (EMDF). This format is synchronized, meaning that the UDC looks for a specific series of bytes in the skip buffer, then processes the data afterwards as EMDF. The EMDF in a single syncframe is called an ‘EMDF container’. This is represented in the specifications as:
The EMDF syncword is ‘X8’.
An EMDF container is defined as follows:
variable_bits is defined as:
If you’ve spent time looking for vulnerabilities in this type of specification, a problem might already be apparent. There is no stated limit for the size of emdf_payload_size , meanwhile the output of variable_bits could be very large, essentially any numeric value.
Indeed, this is the root of the problem Ivan Fratric found while analyzing the Android UDC binary. In pseudo-code, it reads the EMDF payload into a custom ‘evo’ heap as follows:
So, memory is allocated, then the bytes of the payload are copied into the allocated memory. How does this allocation work?
The evo heap is a single slab, with a single tracking pointer that is incremented when memory is allocated. There is no way to free memory on the evo heap. It is only used to process EMDF payloads for a single syncframe (the specification provides no limit on the number of payloads a syncframe can contain, outside of limits on the size of the skip buffer), and once that frame is processed, the entire evo heap is cleared and re-used for the frame, with no persistence between syncframes.
While evo_malloc performs a fair number of length checks on allocations, this check is flawed, as it lacks an integer overflow check:
If total allocation size on a 64-bit platform is between 0xFFFFFFFFFFFFFFF9 and 0xFFFFFFFFFFFFFFFF, the value of total_size will wrap, leading to a small allocation, meanwhile, the loop that writes to the buffer uses the original payload_length as its bounds.
Integer overflow bugs are often challenging to exploit because they perform very large writes, but this code has a feature that makes this not the case. Each byte that is written is read from the skip buffer using ddp_udc_int_evo_brw_read , and that function checks read bounds based on emdf_container_length , which is also read from the skip buffer. If the read bounds check fails, the loop exits, and no more data is written to the buffer allocated by evo_malloc . This means that the size of the overflow is controllable, as are the values of the bytes written out of bounds, to the limit of the size of skipl (0x1FF * 6 audio blocks).
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
