Dev.to Security 🔐 Cybersecurity 👁 0 📖 4 min read

CVE-2026-58868: A HEIF Grid Mismatch in Android's Media Decoder

Hey everyone, this is my first post, so if you spot any mistakes, please let me know. I don't usually write about my CVEs, but Harry Kane finally winning the Ballon d'Or felt like a good excuse to do something special. B

Hey everyone, this is my first post, so if you spot any mistakes, please let me know. I don't usually write about my CVEs, but Harry Kane finally winning the Ballon d'Or felt like a good excuse to do something special. Before getting into the bug, here's how I ended up looking at Android in the first place.

Most of my time goes into watching football and researching Windows. One day, there were no matches to watch, and I was bored of staring at Windows disassembly. Then my old Oneplus android phone caught my eye, and I thought, might as well tinker with it.

I wasn't very familiar with Android, so I used an LLM to help me understand how things fit together. I ended up liking it for a pretty obvious reason: I could just read the source code. After poking around for a while, I found a zero click Dos bug in system_server involving mDNS.

That made me want to keep digging. Media parsers seemed like an interesting place to look, especially given how often they show up for zero click exploit. So I started reading the media code, with some help from AI to make sense of the unfamiliar parts. I found the bug through static analysis, which is how I do my Windows research.

Summary

CVE-2026-58868 is an out-of-bounds write in Android's HEIF image-decoding path. A crafted image can make the decoder place tiles outside the allocated output image, causing mediaserver to write converted pixels out of bounds. Google classifies it as a Critical RCE affecting Android 14, 15, 16, 16 QPR2, and 17. (Android bulletin)
But I have reproduced this also in an Android 12 device (moto g31).

In Gmail, opening the email was enough: thumbnail generation decoded the image without me tapping it or manually downloading the attachment. I also reproduced the issue through Google Files and Google Photos. The crash below is from Gmail on my OnePlus CPH2467.

Root cause

A HEIF grid image is assembled from smaller encoded tiles. The output buffer is sized for the overall image, while decoded tile dimensions determine where each tile is placed. In the reproducer, the tiles decode to 1024 x 1024, but the declared output dimensions do not accommodate that tile layout.

StagefrightMetadataRetriever reads the tile dimensions and uses them to select a suitable codec:

if (!thumbnail && isHeif && format != NULL) {
    int32_t tileWidth, tileHeight;
    if (trackMeta->findInt32(kKeyTileWidth, &tileWidth) && tileWidth > 0
            && trackMeta->findInt32(kKeyTileHeight, &tileHeight) && tileHeight > 0) {
        format->setInt32("height", tileHeight);
        format->setInt32("width", tileWidth);
    }
}

Here, thumbnail means a thumbnail stored inside the file; an app can generate its preview by decoding the main image.

The bug is in MediaImageDecoder::onOutputReceived(). It calculates each tile's destination using the decoded crop dimensions. It clips the right and bottom edges to the output size, but does not first reject a tile whose starting position is already outside the image. A tile starting below the canvas can therefore produce a negative crop height.

ColorConverter checks that the source and destination crop dimensions agree:

src.cropWidth() == dst.cropWidth() &&
src.cropHeight() == dst.cropHeight()

Both rectangles can be invalid in the same way and still pass these comparisons. The other stride and alignment checks do not establish that the complete rectangles are within bounds.

The semiplanar conversion wrapper then passes the crop dimensions to libyuv. Abbreviating the chroma and matrix selection:

libyuvFunc(src_y, src_stride_y, src_uv, src_stride_uv,
           dst_ptr, dst.mStride, matrix,
           src.cropWidth(), src.cropHeight());

libyuv takes the height as a signed integer. The dump is consistent with an invalid crop reaching this call as height -1096. In NV21ToARGBMatrix, a negative height requests vertically inverted output, so libyuv adjusts the destination pointer before converting pixels. An invalid crop has now become a write outside the output buffer.

The Gmail crash

The crashing process was the 32-bit ARM mediaserver, in the ImageDecoderOut thread.

Process: /system/bin/mediaserver (pid 18621, uid 1013)
Thread:  ImageDecoderOut (tid 27553)
Signal:  SIGSEGV (SEGV_MAPERR)
Fault:   0xe246e5c0

#00 NV21ToARGBRow_NEON.cfi +142
#01 NV21ToARGBMatrix.cfi +170
#02 std::function::operator() +82
#03 ColorConverter::convertYUV420SemiPlanarUseLibYUV +808
#04 ColorConverter::convertYUVMediaImage +236
#05 ColorConverter::convert +780
#06 MediaImageDecoder::onOutputReceived +1668
#07 MediaImageDecoder::outputLoop +1794

r2   e246e5c0
r10  00000400
r11  00000400

The instruction bytes at the fault decode to:

0xf03c60e8:  vst4.8  {d0, d2, d4, d6}, [r2]!

This attempts to store 32 bytes of converted pixel data. r2 contains the exact fault address, which falls in an unmapped gap. The immediate failure is a destination write.

r10 and r11 still contain 0x400 (1024), matching the controlled tile dimension.

Impact

The result is an attacker influenced out of bounds write through a remotely supplied image The Gmail reproduction required opening the email, after which thumbnail generation triggered the bug.

There was not a flaw in the code itself everypart worked as intended the issue was missing validation.

My apologies for not releasing the poc or sharing much more details for now as the CVE was released just a few days back and some Android versions still are not getting an update.

Timeline

Reported - Feb 22, 2026
Validated by Android Security Team - Mar 12, 2026
Some back and forth
Final Severity rating - May 7, 2026
Bounty - May 14, 2026
Patch Release - Nov 2, 2026

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.