CVE-2026-78900
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifmedia/parsers/h264_parser.cc |
modified | |
ifmedia/parsers/h264_parser_unittest.cc |
modified |
Files Changed
media/parsers/h264_parser.ccmedia/parsers/h264_parser_unittest.cc
Patch
From ad64b2c8fdc096eb8ce2e555b70a7f44bee0e207 Mon Sep 17 00:00:00 2001 From: Dale Curtis <[email protected]> Date: Tue, 21 Jul 2026 15:07:38 -0700 Subject: [PATCH] Fix a few different validation issues with h264 parsing This adds range validation for a few different fields to avoid blindly passing potentially bad data out to drivers. Fixed: 536428615 Change-Id: I8d945cc477ddca47b57dc28df7f66beb4442b106 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8128120 Commit-Queue: Dale Curtis <[email protected]> Reviewed-by: Eugene Zemtsov <[email protected]> Cr-Commit-Position: refs/heads/main@{#1665828} --- diff --git a/media/parsers/h264_parser.cc b/media/parsers/h264_parser.cc index 2dd3ffd0..8e2ce92 100644 --- a/media/parsers/h264_parser.cc +++ b/media/parsers/h264_parser.cc @@ -1200,10 +1200,18 @@ case 0: case 1: READ_UE_OR_RETURN(&pic_num_mod->abs_diff_pic_num_minus1); + // H.264 Section 7.4.3.1: abs_diff_pic_num_minus1 shall be in + // [0, MaxPicNum - 1], MaxPicNum <= 65536. + IN_RANGE_IF_OR_RETURN(pic_num_mod->abs_diff_pic_num_minus1, 0, 65535, + validate_extended_bitstream_); break; case 2: READ_UE_OR_RETURN(&pic_num_mod->long_term_pic_num); + // H.264 Section 7.4.3.1: long_term_pic_num in [0, MaxLongTermFrameIdx], + // MaxLongTermFrameIdx <= max_num_ref_frames - 1 <= 15. + IN_RANGE_IF_OR_RETURN(pic_num_mod->long_term_pic_num, 0, 15, + validate_extended_bitstream_); break; case 3: @@ -1345,18 +1353,40 @@ break; if (marking->memory_mgmnt_control_operation == 1 || - marking->memory_mgmnt_control_operation == 3) + marking->memory_mgmnt_control_operation == 3) { READ_UE_OR_RETURN(&marking->difference_of_pic_nums_minus1); + // H.264 Section 7.4.3.3: difference_of_pic_nums_minus1 shall be in + // [0, MaxPicNum - 1], MaxPicNum <= 65536. + IN_RANGE_IF_OR_RETURN(marking->difference_of_pic_nums_minus1, 0, + 65535, validate_extended_bitstream_); + } - if (marking->memory_mgmnt_control_operation == 2) + if (marking->memory_mgmnt_control_operation == 2) { READ_UE_OR_RETURN(&marking->long_term_pic_num); + // H.264 Section 7.4.3.3: long_term_pic_num in [0, + // MaxLongTermFrameIdx], MaxLongTermFrameIdx <= max_num_ref_frames - 1 + // <= 15. + IN_RANGE_IF_OR_RETURN(marking->long_term_pic_num, 0, 15, + validate_extended_bitstream_); + } if (marking->memory_mgmnt_control_operation == 3 || - marking->memory_mgmnt_control_operation == 6) + marking->memory_mgmnt_control_operation == 6) { READ_UE_OR_RETURN(&marking->long_term_frame_idx); + // H.264 Section 7.4.3.3: long_term_frame_idx in [0, + // MaxLongTermFrameIdx], MaxLongTermFrameIdx <= max_num_ref_frames - 1 + // <= 15. + IN_RANGE_IF_OR_RETURN(marking->long_term_frame_idx, 0, 15, + validate_extended_bitstream_); + } - if (marking->memory_mgmnt_control_operation == 4) + if (marking->memory_mgmnt_control_operation == 4) { READ_UE_OR_RETURN(&marking->max_long_term_frame_idx_plus1); + // H.264 Section 7.4.3.3: max_long_term_frame_idx_plus1 in [0, + // max_num_ref_frames], max_num_ref_frames <= 16. + IN_RANGE_IF_OR_RETURN(marking->max_long_term_frame_idx_plus1, 0, 16, + validate_extended_bitstream_); + } if (marking->memory_mgmnt_control_operation > 6) return kInvalidStream; diff --git a/media/parsers/h264_parser_unittest.cc b/media/parsers/h264_parser_unittest.cc index 59364e7..1f37f51be 100644 --- a/media/parsers/h264_parser_unittest.cc +++ b/media/parsers/h264_parser_unittest.cc @@ -536,6 +536,118 @@ EXPECT_EQ(H264Parser::kInvalidStream, parser.ParseSEI(&sei)); } + + // SliceHeader: DecRefPicMarking and RefPicListModification range checks. + { + H264SPS sps; + sps.profile_idc = 66; // Baseline + sps.level_idc = 10; + sps.log2_max_frame_num_minus4 = 0; + sps.pic_order_cnt_type = 0; + sps.log2_max_pic_order_cnt_lsb_minus4 = 0; + sps.max_num_ref_frames = 4; + sps.pic_width_in_mbs_minus1 = 1; + sps.pic_height_in_map_units_minus1 = 1; + sps.frame_mbs_only_flag = true; + + H264PPS pps; + pps.pic_parameter_set_id = 0; + pps.seq_parameter_set_id = 0; + + // Helper lambda to build a slice header with custom modification and + // marking ops. + auto build_slice = [&](uint32_t ref_mod_idc, uint32_t ref_mod_val, + uint32_t mmco, uint32_t mmco_val1, + uint32_t mmco_val2 = 0) { + H26xAnnexBBitstreamBuilder builder( + /*insert_emulation_prevention_bytes=*/true); + BuildPackedH264SPS(builder, sps); + BuildPackedH264PPS(builder, sps, pps); + + builder.BeginNALU(H264NALU::kNonIDRSlice, 1); + builder.AppendUE(0); // first_mb_in_slice + builder.AppendUE(0); // slice_type (P slice) + builder.AppendUE(0); // pic_parameter_set_id + builder.AppendBits(4, 0); // frame_num + builder.AppendBits(4, 0); // pic_order_cnt_lsb + + builder.AppendBool(false); // num_ref_idx_active_override_flag + if (ref_mod_idc != 3) { + builder.AppendBool(true); // ref_pic_list_modification_flag_l0 + builder.AppendUE(ref_mod_idc); + builder.AppendUE(ref_mod_val); + builder.AppendUE(3); // end of modifications + } else { + builder.AppendBool(false); // ref_pic_list_modification_flag_l0 + } + + if (mmco != 0) { + builder.AppendBool(true); // adaptive_ref_pic_marking_mode_flag + builder.AppendUE(mmco); + if (mmco == 1 || mmco == 3) { + builder.AppendUE(mmco_val1); + } + if (mmco == 2) { + builder.AppendUE(mmco_val1); + } + if (mmco == 3 || mmco == 6) { + builder.AppendUE(mmco == 3 ? mmco_val2 : mmco_val1); + } + if (mmco == 4) { + builder.AppendUE(mmco_val1); + } + builder.AppendUE(0); // end of MMCO + } else { + builder.AppendBool(false); // adaptive_ref_pic_marking_mode_flag + } + + builder.AppendSE(0); // slice_qp_delta + builder.FinishNALU(); + return std::vector<uint8_t>(builder.data().begin(), builder.data().end()); + }; + + auto parse_slice = [&](const std::vector<uint8_t>& stream_data) { + H264Parser p; + p.SetStream(stream_data); + H264NALU n; + int sps_id_out, pps_id_out; + EXPECT_EQ(H264Parser::kOk, p.AdvanceToNextNALU(&n)); // SPS + EXPECT_EQ(H264Parser::kOk, p.ParseSPS(&sps_id_out)); + EXPECT_EQ(H264Parser::kOk, p.AdvanceToNextNALU(&n)); // PPS + EXPECT_EQ(H264Parser::kOk, p.ParsePPS(&pps_id_out)); + EXPECT_EQ(H264Parser::kOk, p.AdvanceToNextNALU(&n)); // Slice + H264SliceHeader sh; + return p.ParseSliceHeader(n, &sh); + }; + + // Valid slice header. + EXPECT_EQ(H264Parser::kOk, parse_slice(build_slice(3, 0, 0, 0))); + + // MMCO 6: long_term_frame_idx = 16 (invalid, max 15). + EXPECT_EQ(H264Parser::kInvalidStream, + parse_slice(build_slice(3, 0, 6, 16))); + + // MMCO 2: long_term_pic_num = 16 (invalid, max 15). + EXPECT_EQ(H264Parser::kInvalidStream, + parse_slice(build_slice(3, 0, 2, 16))); + + // MMCO 1: difference_of_pic_nums_minus1 = 65536 (invalid, max 65535). + EXPECT_EQ(H264Parser::kInvalidStream, + parse_slice(build_slice(3, 0, 1, 65536))); + + // MMCO 4: max_long_term_frame_idx_plus1 = 17 (invalid, max 16). + EXPECT_EQ(H264Parser::kInvalidStream, + parse_slice(build_slice(3, 0, 4, 17))); + + // RefPicListModification 2: long_term_pic_num = 16 (invalid, max 15). + EXPECT_EQ(H264Parser::kInvalidStream, + parse_slice(build_slice(2, 16, 0, 0))); + + // RefPicListModification 0: abs_diff_pic_num_minus1 = 65536 (invalid, max
Regression Test / PoC
diff --git a/media/parsers/h264_parser_unittest.cc b/media/parsers/h264_parser_unittest.cc
index 59364e7..1f37f51be 100644
--- a/media/parsers/h264_parser_unittest.cc
+++ b/media/parsers/h264_parser_unittest.cc
@@ -536,6 +536,118 @@
EXPECT_EQ(H264Parser::kInvalidStream, parser.ParseSEI(&sei));
}
+
+ // SliceHeader: DecRefPicMarking and RefPicListModification range checks.
+ {
+ H264SPS sps;
+ sps.profile_idc = 66; // Baseline
+ sps.level_idc = 10;
+ sps.log2_max_frame_num_minus4 = 0;
+ sps.pic_order_cnt_type = 0;
+ sps.log2_max_pic_order_cnt_lsb_minus4 = 0;
+ sps.max_num_ref_frames = 4;
+ sps.pic_width_in_mbs_minus1 = 1;
+ sps.pic_height_in_map_units_minus1 = 1;
+ sps.frame_mbs_only_flag = true;
+
+ H264PPS pps;
+ pps.pic_parameter_set_id = 0;
+ pps.seq_parameter_set_id = 0;
+
+ // Helper lambda to build a slice header with custom modification and
+ // marking ops.
+ auto build_slice = [&](uint32_t ref_mod_idc, uint32_t ref_mod_val,
+ uint32_t mmco, uint32_t mmco_val1,
+ uint32_t mmco_val2 = 0) {
+ H26xAnnexBBitstreamBuilder builder(
+ /*insert_emulation_prevention_bytes=*/true);
+ BuildPackedH264SPS(builder, sps);
+ BuildPackedH264PPS(builder, sps, pps);
+
+ builder.BeginNALU(H264NALU::kNonIDRSlice, 1);
+ builder.AppendUE(0); // first_mb_in_slice
+ builder.AppendUE(0); // slice_type (P slice)
+ builder.AppendUE(0); // pic_parameter_set_id
+ builder.AppendBits(4, 0); // frame_num
+ builder.AppendBits(4, 0); // pic_order_cnt_lsb
+
+ builder.AppendBool(false); // num_ref_idx_active_override_flag
+ if (ref_mod_idc != 3) {
+ builder.AppendBool(true); // ref_pic_list_modification_flag_l0
+ builder.AppendUE(ref_mod_idc);
+ builder.AppendUE(ref_mod_val);
+ builder.AppendUE(3); // end of modifications
+ } else {
+ builder.AppendBool(false); // ref_pic_list_modification_flag_l0
+ }
+
+ if (mmco != 0) {
+ builder.AppendBool(true); // adaptive_ref_pic_marking_mode_flag
+ builder.AppendUE(mmco);
+ if (mmco == 1 || mmco == 3) {
+ builder.AppendUE(mmco_val1);
+ }
+ if (mmco == 2) {
+ builder.AppendUE(mmco_val1);
+ }
+ if (mmco == 3 || mmco == 6) {
+ builder.AppendUE(mmco == 3 ? mmco_val2 : mmco_val1);
+ }
+ if (mmco == 4) {
+ builder.AppendUE(mmco_val1);
+ }
+ builder.AppendUE(0); // end of MMCO
+ } else {
+ builder.AppendBool(false); // adaptive_ref_pic_marking_mode_flag
+ }
+
+ builder.AppendSE(0); // slice_qp_delta
+ builder.FinishNALU();
+ return std::vector<uint8_t>(builder.data().begin(), builder.data().end());
+ };
+
+ auto parse_slice = [&](const std::vector<uint8_t>& stream_data) {
+ H264Parser p;
+ p.SetStream(stream_data);
+ H264NALU n;
+ int sps_id_out, pps_id_out;
+ EXPECT_EQ(H264Parser::kOk, p.AdvanceToNextNALU(&n)); // SPS
+ EXPECT_EQ(H264Parser::kOk, p.ParseSPS(&sps_id_out));
+ EXPECT_EQ(H264Parser::kOk, p.AdvanceToNextNALU(&n)); // PPS
+ EXPECT_EQ(H264Parser::kOk, p.ParsePPS(&pps_id_out));
+ EXPECT_EQ(H264Parser::kOk, p.AdvanceToNextNALU(&n)); // Slice
+ H264SliceHeader sh;
+ return p.ParseSliceHeader(n, &sh);
+ };
+
+ // Valid slice header.
+ EXPECT_EQ(H264Parser::kOk, parse_slice(build_slice(3, 0, 0, 0)));
+
+ // MMCO 6: long_term_frame_idx = 16 (invalid, max 15).
+ EXPECT_EQ(H264Parser::kInvalidStream,
+ parse_slice(build_slice(3, 0, 6, 16)));
+
+ // MMCO 2: long_term_pic_num = 16 (invalid, max 15).
+ EXPECT_EQ(H264Parser::kInvalidStream,
+ parse_slice(build_slice(3, 0, 2, 16)));
+
+ // MMCO 1: difference_of_pic_nums_minus1 = 65536 (invalid, max 65535).
+ EXPECT_EQ(H264Parser::kInvalidStream,
+ parse_slice(build_slice(3, 0, 1, 65536)));
+
+ // MMCO 4: max_long_term_frame_idx_plus1 = 17 (invalid, max 16).
+ EXPECT_EQ(H264Parser::kInvalidStream,
+ parse_slice(build_slice(3, 0, 4, 17)));
+
+ // RefPicListModification 2: long_term_pic_num = 16 (invalid, max 15).
+ EXPECT_EQ(H264Parser::kInvalidStream,
+ parse_slice(build_slice(2, 16, 0, 0)));
+
+ // RefPicListModification 0: abs_diff_pic_num_minus1 = 65536 (invalid, max
+ // 65535).
+ EXPECT_EQ(H264Parser::kInvalidStream,
+ parse_slice(build_slice(0, 65536, 0, 0)));
+ }
}
TEST(H264ParserTest, SpsOverwritesInvalidatesPps) {
Original Bug Report
Potential missing bounds check for H.264 long_term_frame_idx leads to driver OOB
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: The H.264 bitstream parser lacks a bounds check for the long_term_frame_idx field when parsing memory management control operations. This allows an attacker-controlled, unvalidated large integer to propagate directly into bounds-bearing hardware-decode driver structures. Processing these structures can result in potential out-of-bounds memory accesses in the sandboxed GPU process or kernel-level drivers.
Affected files:
media/parsers/h264_parser.ccmedia/gpu/h264_decoder.ccmedia/gpu/windows/d3d11_h264_accelerator.ccmedia/gpu/v4l2/v4l2_video_decoder_delegate_h264.cc
Estimated timestamp from git blame: 2012-05-14
1. Summary of the Issue (Meant for Human Triage)
A potential high-severity security vulnerability has been identified in Chromium’s H.264 bitstream parser and hardware accelerator decoder delegates. An unvalidated bitstream field is propagated directly to hardware-accelerated video decoding driver APIs without bounds checking.
Specifically, the H.264 slice-header field dec_ref_pic_marking().long_term_frame_idx (per ITU-T H.264 §7.4.3.3) is parsed using READ_UE_OR_RETURN without any subsequent range-bounding check (such as IN_RANGE_OR_RETURN). Under the H.264 specification, this field must be bounded within [0, MaxLongTermFrameIdx], where MaxLongTermFrameIdx is at most 15. However, the parser accepts values up to 0x7FFFFFFF.
This unvalidated value is stored verbatim in the reference H264Picture objects inside the Decoded Picture Buffer (DPB). During frame submission, the accelerator delegates read this value and pass it directly to the OS-level hardware video decoding APIs:
- On Windows (D3D11 / DXVA): It is narrow-cast to a
USHORT(allowing control up to0xFFFF) and copied into theDXVA_PicParams_H264.FrameNumListarray, which is then submitted viaID3D11VideoContext::SubmitDecoderBuffers. - On ChromeOS (V4L2): The full, un-narrowed 31-bit value is stored as a
__u32insidev4l2_h264_dpb_entry.pic_numand sent to stateless hardware video decoding kernel drivers viaioctl(VIDIOC_S_EXT_CTRLS, V4L2_CID_STATELESS_H264_DECODE_PARAMS).
Because the downstream proprietary GPU and kernel drivers rely on these fields for indexing internally managed reference picture tables, passing unchecked, out-of-bounds indices from sandboxed user-space processes exposes a severe risk of driver-level or kernel-level memory corruption.
Note: The steps provided below are potential suggested steps; our tooling agent does not yet have the ability to run code or execute a live proof of concept.
2. Proof-of-Concept & Detailed Execution Flow
Below is the suggested step-by-step trace of how an attacker could potentially trigger this vulnerability, detailing the exact data flow from untrusted input to the bounds-bearing driver sink:
- An attacker crafts a malicious Annex-B H.264 video bitstream containing an unvalidated
long_term_frame_idxvalue (up to0x7FFFFFFF). - The attacker delivers this video to a victim using an HTML
<video>tag or the WebCodecs API (VideoDecoder.decode()). - The Chromium browser or renderer process routes this bitstream to the
H264Parser. - In
media/parsers/h264_parser.cc, the parser parses a memory management control operation (MMCO) viaParseDecRefPicMarking. - If the MMCO is 3 or 6,
READ_UE_OR_RETURN(&marking->long_term_frame_idx)is called (line 1356). - The
READ_UE_OR_RETURNmacro processes an unsigned exponential-Golomb value. It limits the number of leading zeros to 31, allowing values up to0x7FFFFFFF(media/parsers/bit_reader_macros.h). - Unlike other H.264 parser fields (e.g.,
first_mb_in_sliceat line 1433), there is noIN_RANGE_OR_RETURNmacro boundinglong_term_frame_idxto the specification-mandated range[0, MaxLongTermFrameIdx]. - This parsed slice header is passed to
H264Decoder::HandleMemoryManagementOpsinmedia/gpu/h264_decoder.cc. - If MMCO is 3 (line 895) or 6 (line 943), the unvalidated
ref_pic_marking->long_term_frame_idxis copied verbatim directly to theH264Picture::long_term_frame_idxfield (e.g.,pic->long_term_frame_idx = ref_pic_marking->long_term_frame_idx). - The
H264Pictureobject containing this tainted value resides in the Decoded Picture Buffer (dpb_). - Before submitting the frame metadata to the accelerator,
H264Decoder::UpdatePicNums(line 423) is called. - At line 431,
pic->long_term_pic_numis assigned the tainted value frompic->long_term_frame_idx. - The decoder then calls
SubmitFrameMetadataon the active hardware accelerator delegate. - On ChromeOS (V4L2): The function
V4L2VideoDecoderDelegateH264::SubmitFrameMetadatatranslates the DPB to the V4L2 representation (H264DPBToV4L2DPB). - In
media/gpu/v4l2/v4l2_video_decoder_delegate_h264.cc(lines 204-205),entry.frame_num = pic->long_term_pic_num;(narrowed to__u16) andentry.pic_num = pic->long_term_frame_idx;(preserved as a full0x7FFFFFFF32-bit__u32value) are copied into thev4l2_h264_dpb_entrystruct. - At line 611, the
priv_->v4l2_decode_paramstructure containing the tainted DPB is submitted to the kernel driver viaioctl(VIDIOC_S_EXT_CTRLS, V4L2_CID_STATELESS_H264_DECODE_PARAMS). - The
V4L2_CID_STATELESS_H264_DECODE_PARAMSbounds-bearing structure usesdpb[i].pic_numandframe_numfor driver indexing, resulting in an out-of-bounds access within the kernel-space V4L2 driver. - On Windows (D3D11/DXVA): The function
D3D11H264Accelerator::SubmitFrameMetadatamaps the DPB into the DXVA structure. - In
media/gpu/windows/d3d11_h264_accelerator.cc(lines 122-125),frame_num_list_[i]is set toour_ref_pic->long_term_pic_num, casting the large integer to aUSHORT(up to0xFFFF). - At lines 291-292,
frame_num_list_is copied intoDXVA_PicParams_H264.FrameNumListviamemcpy. - At line 306,
params_buffer.Commit()pushes the D3D11 buffer description containing the taintedFrameNumListonto thevideo_buffers_queue (ScopedD3D11DecoderBuffer::Commit). - Later, when
SubmitSliceis executed, theD3D11VideoDecoderWrapperImpl::SubmitDecoderBuffers()API is called (media/gpu/windows/d3d11_video_decoder_wrapper.cc:188-218). - The
video_context_->SubmitDecoderBuffers(...)call passes the tainted structure to the D3D11 hardware driver in the GPU process. - The D3D11 hardware driver uses the out-of-bounds
FrameNumList[i]value, leading to out-of-bounds memory access within the driver/GPU process.
Suggested Fix
Apply an IN_RANGE_OR_RETURN check immediately after parsing the field in media/parsers/h264_parser.cc (similar to the fix applied in b/500028989 for first_mb_in_slice). The parser should ensure the parsed long_term_frame_idx strictly bounded.
if (marking->memory_mgmnt_control_operation == 3 ||
marking->memory_mgmnt_control_operation == 6) {
READ_UE_OR_RETURN(&marking->long_term_frame_idx);
// Add appropriate bounds check based on max_long_term_frame_idx_plus1
// or absolute H.264 specification limits (e.g., 0 to 15).
// IN_RANGE_OR_RETURN(marking->long_term_frame_idx, 0, ...);
}
3. Technical Verification Details (Automated Audit Logs - Reviewers may skip this section)
> Critic model verdict (2026-07-18 05:02:51):
> * Severity: High (S1)
> * Brief Notes / Reasoning:
> The report accurately identifies a missing bounds check in H.264 bitstream parsing that leads to unvalidated data reaching hardware video decode drivers.
>
> 1. Root Cause: In media/parsers/h264_parser.cc, the dec_ref_pic_marking().long_term_frame_idx field is read via READ_UE_OR_RETURN but lacks an IN_RANGE_OR_RETURN check, allowing an attacker to supply values up to 0x7FFFFFFF. The H.264 spec max is 15.
> 2. Trigger Path: H264Decoder::HandleMemoryManagementOps (media/gpu/h264_decoder.cc) copies this unvalidated value directly to H264Picture::long_term_frame_idx. UpdatePicNums() assigns it to long_term_pic_num. When SubmitFrameMetadata() is called, accelerator delegates read this tainted DPB state. In D3D11H264Accelerator, the value is cast to USHORT (giving control over the lower 16 bits) and copied into DXVA_PicParams_H264.FrameNumList. In V4L2VideoDecoderDelegateH264, it is placed into v4l2_h264_dpb_entry.pic_num as a __u32. Both are submitted directly to the OS/kernel drivers.
> 3. Severity Justification (S1 / High): The knowledge base explicitly defines this exact pattern as High (S1): ‘Bitstream field passed unvalidated to a HW-decode driver struct: a media/parsers/h26x_parser.cc field read via READ_UE_OR_RETURN with no IN_RANGE_OR_RETURN, copied verbatim by an accelerator delegate… into DXVA_PicParams_* / V4L2_CID_*’. The KB also explicitly notes: ‘Do NOT reject for no ASAN report — the corruption is driver-side. PoC = the bad value at the delegate copy step.’
> 4. Ceilings/Modifiers: The GPU process is sandboxed on Windows and ChromeOS. Android uses MediaCodecVideoDecoder and does not instantiate H264Decoder, so it is not unsandboxed (S0). Therefore, this is memory corruption in a sandboxed process, confirming the High (S1) rating despite the report’s self-rating of Medium.
Prior Automated Validation Assertions:
-
Parser (no semantic bound):
// media/parsers/h264_parser.cc:1354-1356 if (marking->memory_mgmnt_control_operation == 3 || marking->memory_mgmnt_control_operation == 6) READ_UE_OR_RETURN(&marking->long_term_frame_idx);READ_UE_OR_RETURNaccepts up to_num_bits_processed == 31→*out = (1u<<31)-1 = 0x7FFFFFFF. Per ITU-T H.264 §7.4.3.3 / §8.2.5.4,long_term_frame_idxmust be bounded. -
Decoder (verbatim copy, no re-check):
// media/gpu/h264_decoder.cc:925-943 (MMCO=6) case 6: { ... pic->ref = true; pic->long_term = true; pic->long_term_frame_idx = ref_pic_marking->long_term_frame_idx; // :942 — no bound break; } -
D3D11 sink (§M4.8 verbatim copy):
// media/gpu/windows/d3d11_h264_accelerator.cc:117-125 ref_frame_list_[i].AssociatedFlag = our_ref_pic->long_term; // = 1 ... frame_num_list_[i] = ref_frame_list_[i].AssociatedFlag ? our_ref_pic->long_term_pic_num // 0x7FFFFFFF → USHORT narrow → 0xFFFF : our_ref_pic->frame_num;frame_num_list_isUSHORT[16]. -
V4L2 sink (no narrowing, full 31-bit value):
// media/gpu/v4l2/v4l2_video_decoder_delegate_h264.cc:200-205 struct v4l2_h264_dpb_entry& entry = priv_->v4l2_decode_param.dpb[i++]; ... if (pic->long_term) { entry.frame_num = pic->long_term_pic_num; // __u16 ← 0x7FFFFFFF (narrows) entry.pic_num = pic->long_term_frame_idx; // __u32 ← 0x7FFFFFFF (NO narrowing) }
Evaluated with Chrome root at commit: b96d2ec58f4f5f92b540a723966b199d6e9951b4
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.