mp4box.js — 20-byte `sgpd` box with large `entry_count` causes unbounded loop and heap OOM abort

Open Beginner friendly
#581 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
77/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
node.js, typescript
Domain
backend, security

Research direction

Start in src/boxes/sgpd.ts at sgpdBox.parse() and trace how SampleGroupEntry.prototype.parse reads the entry payload. Reproduce with the 20-byte buffer from the issue and check that parsing stops or reports an error when entry_count exceeds the bytes remaining in the box. Done means this input returns or invokes onError without unbounded allocation or process failure.

Written by the indexing model from the issue text.

Description

In [email protected] (and main at d91bce4, tested on Node.js v24.14.1), calling MP4BoxFile.appendBuffer() on a 20-byte sgpd (SampleGroupDescriptionBox) with version 0/2, an unknown grouping_type, and entry_count = 0xFFFFFFFF loops up to $2^{32} - 1$ times without advancing the stream, exhausting the V8 heap and aborting the process.

const MP4Box = require('mp4box');
// size=20, 'sgpd', version 0, flags 0, grouping_type 'xxxx', entry_count 0xFFFFFFFF
const b = Buffer.from('00000014' + '73677064' + '00000000' + '78787878' + 'ffffffff', 'hex');
const ab = b.buffer.slice(b.byteOffset, b.byteOffset + b.length);
ab.fileStart = 0;
const f = MP4Box.createFile();
f.onError = () => {};
f.appendBuffer(ab);
f.flush();

Expected: Parsing stops at the end of the sgpd box (or rejects entry_count exceeding the remaining bytes in the box) and returns or invokes onError without exhausting memory.

Actual: The loop allocates SampleGroupEntry objects until the V8 heap limit is reached and crashes the process:

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

ISO/IEC 14496-12 §8.9.3 (SampleGroupDescriptionBox): version 0 omits default_length and per-entry description_length, and requires all SampleGroupDescriptionEntry payloads to reside within the declared box size.

In src/boxes/sgpd.ts (sgpdBox.parse()), when this.version !== 1 (so this.default_length = 0) and this.grouping_type is not in BoxRegistry.sampleGroupEntry, entry.description_length is set to 0 and SampleGroupEntry.prototype.parse calls stream.readUint8Array(0). Each loop iteration consumes 0 bytes from stream, so DataStream never reaches the end of the buffer to throw a RangeError; bounding the loop by checking stream.getPosition() < this.start + this.size (or requiring entry.description_length > 0 / remaining bytes >= entry_count) prevents the unbounded allocation.

Credit: https://ucsbplse.github.io/

Dominant language
TypeScript
Stars
2.5k
Forks
390
Avg merge
1d 17h
Merged PRs (30d)
8

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from gpac/mp4box.js

All issues in gpac/mp4box.js

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.