Skip to content

[Bug]Undefined behavior on malformed MP4: null pointer passed to strcmp in MP4File::GenerateTracks (and misaligned load in STRTOINT32) (CWE-476) #99

Description

@1820893135-pixel

Describe the bug

A malformed MP4 triggers undefined behavior inside the mp4v2 library during track setup:

  • Null pointer to strcmpMP4File::GenerateTracks (src/mp4file.cpp:472) calls strcmp(handlerType, MP4_HINT_TRACK_TYPE) without checking for NULL. When a trak/mdia/hdlr is malformed such that the handler type string is null, strcmp(NULL, ...) is UB. UBSan reports null pointer passed as argument 1, which is declared to never be null.
  • Misaligned loadSTRTOINT32 (src/mp4util.cpp:339) dereferences *(uint32_t*)s when MP4V2_INTSTRING_ALIGNMENT is not defined; atom-type bytes reached through ATOMID() in MP4Atom::ReadChildAtoms are not guaranteed 4-byte aligned, a misaligned-load UB.

Under a trap-mode build (-fno-sanitize-recover=all) the first UB aborts the process (SIGILL / exit 132); with a recoverable UBSan build the null pointer runtime error is printed.

To Reproduce

  1. Build mp4v2 with ASan + UBSan (trap mode):

    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
  2. Use the same memory-backed harness as issue #XXX (save as fuzz.cpp — feeds the MP4 bytes through MP4FileProvider into MP4Read):

    #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;
    }
  3. Compile and write the exact 575-byte crash input (a malformed MP4 with a soun track whose hdlr/track setup drives GenerateTracks into strcmp(NULL, ...)). The base64 string below round-trips to the byte-identical crash input:

    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 'AAAAFGZ0eXBpc29tAAACAGlzb20AAAIBbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAAAAA4wAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAY10cmFrAAAAXHRraGQAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAEAAAAAAAAAAAD////hAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAEpbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAAD6AAAAABVxAAAAAAADGhkbHIAAAAAAAAAAHNvdW4AAAAAAAAAAAAAAAAAAAAA4G1pbmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAKBzdGJsAAAANHN0c2QAAAAAAAAAAQAAACR0d29zAAAAAAAAAAEAAAAAAAAAAAACABAAAAAAAACsRAAAABhzdHRzAAAAAAAAAAEAAAACAAAEAAAAABxzdHN6AAAAAAAAAAAAAAACAAAABAAAAAQAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAFHN0YwABbwAAAAAAAAABAAACHQAAAChtZGF0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkAAAAAAAAAAA=' | base64 -d > poc.m4a
  4. Run it:

    ./mp4v2_read_fuzzer poc.m4a

    UBSan output (trap mode aborts with SIGILL, exit 132; recoverable mode prints):

    /src/workspace/src/mp4file.cpp:472:28: runtime error: null pointer passed as argument 1, which is declared to never be null
    SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ... mp4file.cpp:472:28
    

    In our build the process exits with code 1 (UBSan trap).

Expected behavior

GenerateTracks should check the handler type for NULL before strcmp, and STRTOINT32 should load the 32-bit value byte-by-byte (or via memcpy) instead of dereferencing a potentially misaligned pointer. Malformed MP4s should be rejected with an error rather than triggering undefined behavior.

Screenshots

Image

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 575-byte input always triggers the UBSan report (exit 1 in our trap-mode build).
  • Root cause locations: strcmp in MP4File::GenerateTracks (src/mp4file.cpp:472) and misaligned uint32_t load in STRTOINT32 (src/mp4util.cpp:339, reached via ATOMID() in MP4Atom::ReadChildAtoms).
  • Reachability: any MP4 with a track whose handler setup reaches GenerateTracks through the public MP4Read API.
  • Severity: medium. Null-pointer UB and misaligned-load UB are undefined behavior; a crash is deterministic under sanitizers, and the misaligned load may also misbehave on strict-alignment architectures.
  • CWE-476 (NULL Pointer Dereference).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions