Skip to content

fix(Video): bounds-check bitstream indices in Vc1/Mpegv/Mpeg4v Streams_Fill - #2679

Merged
JeromeMartinez merged 1 commit into
MediaArea:masterfrom
AetherAI3:fix/2611-2612-2608-video-index-bounds
Sep 24, 2026
Merged

JeromeMartinez merged 1 commit into
MediaArea:masterfrom
AetherAI3:fix/2611-2612-2608-video-index-bounds

Conversation

@AetherAI3

Copy link
Copy Markdown

Closes #2611.
Closes #2612.
Closes #2608.

Three video parsers use int8u bitstream fields as direct indices into small static tables in Streams_Fill(), guarded only by an (int8u)-1 "unset" sentinel (0xFF) or nothing at all. @sigdevel's PoCs (mediainfo/{8,9,5}) supply 190 in each field — slips past the sentinel and reads garbage:

  • File_Vc1.cpp:293 — Vc1_ChromaSubsamplingFormat[colordiff_format] (table has 4 entries; reporter also notes unchecked accesses at lines 279 Vc1_PixelAspectRatio[16] and 287 Vc1_Profile[4])
  • File_Mpegv.cpp:1277-1278 — Mpegv_chroma_format[chroma_format] and Mpegv_chroma_format_Colorspace[chroma_format] (both char *[4])
  • File_Mpeg4v.cpp:422 — Mpegv_frame_rate[frame_rate_code] (const float64[16])

The garbage const char * read from the OOB slot is a near-zero pointer that later faults inside strlen() on the way through Fill() → From_UTF8() (VC-1, MPEG video), or ASan reports the global-buffer-overflow directly on the float64 table (MPEG-4 Visual).

Add idx < sizeof(table)/sizeof(*table) bounds checks at each site — compile-time constant, tautology on well-formed inputs (valid indices are strictly less than the table size), safe reject on malformed. Every existing semantic guard (AspectRatio != 0x0F "custom AR", profile_and_level_indication_escape, the (int8u)-1 sentinels) stays as it was; only the missing size check is added.

While in File_Mpegv::Streams_Fill I applied the same class of guard to the neighbouring aspect_ratio_information and frame_rate_code accesses (Mpegv_aspect_ratio1/2 and Mpegv_frame_rate) and to profile_and_level_indication_profile/_level. These are all the same "bitstream field used as raw index" pattern in the same function, and guarding them consistently avoids a follow-up on the same class in the same code.

The same PoC input still trips separate, pre-existing UBSan warnings elsewhere in these parsers (bool fields loaded with non-{0,1} values, plus a small handful of other unchecked table accesses further down Streams_Fill for MPEG-2). Those are a separate class from the reported memory-safety bugs and are out of scope here.

Reported-by: @sigdevel — thanks for the report and reproducers.

…s_Fill

Three video parsers use bitstream-decoded 8-bit fields as direct indices
into fixed-size static tables in Streams_Fill(), guarded only by their
'unset' sentinel (0xFF) or nothing at all. A crafted stream can supply
any value up to 0xFE and slip past the sentinel into a small table:

  File_Vc1.cpp:293 (Vc1_ChromaSubsamplingFormat[4], gh#2611)
    also lines 279 (Vc1_PixelAspectRatio[16]) and 287 (Vc1_Profile[4])
  File_Mpegv.cpp:1277-1278 (Mpegv_chroma_format[4] and
    Mpegv_chroma_format_Colorspace[4], gh#2612)
  File_Mpeg4v.cpp:422 (Mpegv_frame_rate[16], gh#2608)

The reporter's PoCs (sigdevel/pocs mediainfo/{5,8,9}) supply 190 in each
field. On the OOB reads, the value that comes back is either a garbage
'const char*' near-zero pointer that later faults inside strlen() during
Fill()/From_UTF8() (Vc1, Mpegv chroma), or a global-buffer-overflow
picked up by ASan for the float64 table (Mpeg4v frame_rate).

Guard each access with 'idx < sizeof(table)/sizeof(*table)' before use.
Where the existing sentinel guard was semantically meaningful (e.g. 0x0F
'custom AR' in Vc1) it stays; only the missing size check is added.

While in File_Mpegv.cpp:Streams_Fill I applied the same class of guard
to the neighbouring aspect_ratio_information and frame_rate_code accesses
(Mpegv_aspect_ratio1/2 and Mpegv_frame_rate) and to
profile_and_level_indication_profile/_level - these are all the same
'bitstream field used as raw index' pattern, and defending against them
consistently avoids a follow-up on the same class in the same function.

The same PoC input still trips separate, pre-existing UBSan warnings
elsewhere in these parsers (bool fields loaded with non-{0,1} values,
plus a small handful of other unchecked table accesses further down
Streams_Fill for MPEG-2). Those are a separate class from the reported
memory-safety bugs and are out of scope here.

Reported-by: Alexander Shvedov <sigdevel>
Closes MediaArea#2611
Closes MediaArea#2612
Closes MediaArea#2608

Signed-off-by: Brandon Barrante <aetherai@aethersystems.net>
@JeromeMartinez
JeromeMartinez merged commit adb4a09 into MediaArea:master Sep 24, 2026
34 of 40 checks passed
@JeromeMartinez

Copy link
Copy Markdown
Member

Thank you for the fixes, I definitely need to improve the handling of such cases.

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