Skip to content

Harden EditListBox ('elst') box size and entry parsing - #3370

Open
filtede98 wants to merge 1 commit into
AOMediaCodec:mainfrom
filtede98:fix-edit-list-box-parsing
Open

filtede98 wants to merge 1 commit into
AOMediaCodec:mainfrom
filtede98:fix-edit-list-box-parsing

Conversation

@filtede98

Copy link
Copy Markdown

Summary

This PR hardens EditListBox (elst) parsing in src/read.c to fully conform to ISO/IEC 14496-12 and ISO/IEC 23008-12 (MIAF/HEIF):

  1. Version Validation: Validates �ersion is either 0 or 1 regardless of whether lags & 1 is set (previously, unsupported versions like 255 were accepted without error if lags & 1 == 0).
  2. Unconditional Entry Parsing: Parses entry_count and the edit list fields even when lags & 1 == 0. In ISO/IEC 14496-12 Section 8.6.6, EditListBox unconditionally contains an entry list. �vifenc emits valid full entries with lags = 0 for non-repeating animations; previously,
    ead.c terminated parsing early after reading only the 4-byte FullBox header, leaving all entry bytes unparsed and unverified.
  3. Complete Field Consumption: Reads media_time (int64 for v1, int32 for v0), media_rate_integer (int16), and media_rate_fraction (int16). Previously, reading stopped after segment_duration, leaving 12 bytes (v1) or 8 bytes (v0) unread.
  4. Strict Box Size & Zero Trailing Bytes: Enforces �vifROStreamRemainingBytes(&s) == 0. Truncated boxes (where trailing entry fields are omitted) and oversized boxes (containing unexpected trailing bytes) are now strictly rejected with AVIF_RESULT_BMFF_PARSE_FAILED.
  5. Regression Tests: Adds comprehensive GoogleTest coverage in ests/gtest/avifchildboxsizetest.cc for both v1 and v0 elst boxes (valid files, size deltas -1/+1, unsupported version, zero segment duration, and invalid entry count).

- Validate that the EditListBox ('elst') version is 0 or 1 regardless
  of whether flags & 1 is set.
- Do not skip entry parsing when flags & 1 is 0; ISO/IEC 14496-12 defines
  the entry structure unconditionally.
- Fully read media_time, media_rate_integer, and media_rate_fraction
  fields according to the box version.
- Enforce that no unexpected trailing bytes remain in the box.
- Add regression tests covering valid files, size delta errors, invalid
  version, zero segment duration, and invalid entry count in
  avifchildboxsizetest.cc.
Comment thread src/read.c
if (version != 0 && version != 1) {
// Unsupported version
avifDiagnosticsPrintf(diag, "Box[elst] has an unsupported version [%u]", version);
return AVIF_FALSE;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not such a big deal, but AVIF_FALSE leads to AVIF_RESULT_BMFF_PARSE_FAILED in avifParseTrackBox(). Maybe avifParseEditListBox() should return an avifResult to distinguish with NOT_IMPLEMENTED. Could be another PR to split the refactoring out of this change.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I would guess it is inconsistent across libavif but spec says:

FullBoxes with an unrecognized version shall be ignored and skipped.

Here it returns an error instead.

Comment thread src/read.c

if ((flags & 1) == 0) {
track->isRepeating = AVIF_FALSE;
return AVIF_TRUE;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering why there was an early return here. Spec says:

When an EditListBox indicates the playback of zero samples or one sample, RepeatEdits shall be equal to 0.

And below we return false if entryCount != 1.

Comment thread src/read.c
uint64_t ignored64;
uint32_t ignored32;
if (version == 1) {
AVIF_CHECK(avifROStreamReadU64(&s, &track->segmentDuration)); // unsigned int(64) segment_duration;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9th ed seems to call that field "edit_duration", maybe it changed recently

Comment thread src/read.c
Comment on lines +4025 to +4026
AVIF_CHECK(avifROStreamReadU16(&s, &ignored16)); // int(16) media_rate_integer;
AVIF_CHECK(avifROStreamReadU16(&s, &ignored16)); // int(16) media_rate_fraction = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Format check will likely complain here

Comment thread src/read.c
Comment on lines +4028 to +4031
if (avifROStreamRemainingBytes(&s) != 0) {
avifDiagnosticsPrintf(diag, "Box[elst] has %zu unexpected trailing bytes", avifROStreamRemainingBytes(&s));
return AVIF_FALSE;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is invalid. Spec says:

When a parser has not reached the end of a content box as defined by the values of the size or largesize field (as appropriate) but does not recognize the remaining syntax elements, it shall ignore and skip the remaining of the content box.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants