How an unsigned integer wrap during H.264 NAL unit length decoding in libstagefright enabled zero-click remote code execution via video files.
Imagine a video projector that reads the size of each video slide from the film itself. A film has a label: 'This slide is 4 billion bytes long'. The projector calculates the size using a small pocket calculator that wraps back around to 12. It creates a tiny frame for 12 bytes, and then the film feeds 4 billion bytes into it, jamming the projector and taking over the movie theater.
The attacker sends a crafted MP4 video via chat or MMS.
Android's media scanner automatically demuxes the video without user interaction.
libstagefright adds the NAL unit size to an offset, wrapping past 32 bits.
The decoder writes video data into an undersized heap buffer, achieving code execution.
Disajikan dalam bahasa pemrograman tingkat tinggi yang mudah dibaca (tanpa assembly rumit).
// VULNERABLE: frameworks/av/media/libstagefright/MPEG4Extractor.cpp
status_t MPEG4Extractor::parseChunk(off64_t *offset) {
uint32_t chunk_size = readU32();
// ROOT CAUSE:
// Raw unsigned 32-bit addition wraps around!
// If chunk_size is 0xFFFFFFF0, adding header (0x20) results in 0x10 (16 bytes!)
uint32_t alloc_size = chunk_size + sizeof(ChunkHeader);
uint8_t *buffer = (uint8_t *)malloc(alloc_size);
// Copies full chunk_size, overflowing buffer!
mDataSource->readAt(*offset, buffer, chunk_size);
return OK;
}
// SECURE: frameworks/av/media/libstagefright/MPEG4Extractor.cpp patch
status_t MPEG4Extractor::parseChunk(off64_t *offset) {
uint32_t chunk_size = readU32();
// 1. Enforce safe integer arithmetic check using __builtin_add_overflow
uint32_t alloc_size;
if (__builtin_add_overflow(chunk_size, sizeof(ChunkHeader), &alloc_size)) {
return ERROR_MALFORMED; // Abort on integer overflow
}
// 2. Impose strict maximum upper bound ceiling on media chunks
if (alloc_size > MAX_MEDIA_CHUNK_SIZE) {
return ERROR_MALFORMED;
}
uint8_t *buffer = (uint8_t *)malloc(alloc_size);
mDataSource->readAt(*offset, buffer, chunk_size);
return OK;
}