CVE-2026-5907
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifmedia/parsers/h264_parser.cc |
modified |
Files Changed
media/parsers/h264_parser.cc
Patch
From 77af3dfce928a72a2836e1232cb7ba62054e3027 Mon Sep 17 00:00:00 2001 From: Eugene Zemtsov <[email protected]> Date: Tue, 17 Feb 2026 19:05:24 -0800 Subject: [PATCH] media: Validate slice_qp_delta and slice_qs_delta in H264Parser This CL adds range validation for slice_qp_delta and slice_qs_delta during slice header parsing to ensure compliance with the H.264 spec. According to Section 7.4.3 "Slice header semantics": For `slice_qp_delta`: "The variable SliceQPY is derived as SliceQPY = 26 + pic_init_qp_minus26 + slice_qp_delta The value of SliceQPY shall be in the range of -QpBdOffset_Y to 51, inclusive." where QpBdOffset_Y = 6 * bit_depth_luma_minus8. For `slice_qs_delta`: "The variable SliceQSY is derived as SliceQSY = 26 + pic_init_qs_minus26 + slice_qs_delta The value of SliceQSY shall be in the range of 0 to 51, inclusive." Adding these checks ensures that malformed or malicious streams are rejected early in the parsing stage, preventing out-of-range values from reaching accelerated decoders. Bug: 484665123 Change-Id: I68e789b477ce24ac97be7c2b5701ebdcc3e229bc Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7586715 Reviewed-by: Ted (Chromium) Meyer <[email protected]> Commit-Queue: Eugene Zemtsov <[email protected]> Cr-Commit-Position: refs/heads/main@{#1586159} --- diff --git a/media/parsers/h264_parser.cc b/media/parsers/h264_parser.cc index cc4714c..d0814220 100644 --- a/media/parsers/h264_parser.cc +++ b/media/parsers/h264_parser.cc @@ -1487,11 +1487,22 @@ } READ_SE_OR_RETURN(&shdr->slice_qp_delta); + // SliceQPY = 26 + pic_init_qp_minus26 + slice_qp_delta + // -QpBdOffset_Y <= SliceQPY <= 51 + int qp_bd_offset_y = 6 * sps->bit_depth_luma_minus8; + int base_qp = 26 + pps->pic_init_qp_minus26; + IN_RANGE_OR_RETURN(shdr->slice_qp_delta, -qp_bd_offset_y - base_qp, + 51 - base_qp); if (shdr->IsSPSlice() || shdr->IsSISlice()) { - if (shdr->IsSPSlice()) + if (shdr->IsSPSlice()) { READ_BOOL_OR_RETURN(&shdr->sp_for_switch_flag); + } READ_SE_OR_RETURN(&shdr->slice_qs_delta); + // SliceQSY = 26 + pic_init_qs_minus26 + slice_qs_delta + // 0 <= SliceQSY <= 51 + int base_qs = 26 + pps->pic_init_qs_minus26; + IN_RANGE_OR_RETURN(shdr->slice_qs_delta, -base_qs, 51 - base_qs); } if (pps->deblocking_filter_control_present_flag) {
Original Bug Report
Missing range validation on slice_qp_delta
Report description
Missing range validation on slice_qp_delta
Bug location
Where do you want to report your vulnerability?
Chrome VRP – Report security issues affecting the Chrome browser. See program rules
Which URL (or repository) have you found the vulnerability in?
https://chromium.googlesource.com/chromium/src/+/main/media/parsers/h264_parser.cc
The problem
Please describe the technical details of the vulnerability
At h264_parser.cc:1485, slice_qp_delta is read via READ_SE_OR_RETURN with no range check. The H.264 spec (ITU-T H.264 §7.4.3) requires SliceQPY = 26 + pic_init_qp_minus26 + slice_qp_delta to be in [0, 51 + 6*bit_depth_luma_minus8]. The sibling fields slice_alpha_c0_offset_div2 and slice_beta_offset_div2 ARE validated 14 lines later at lines 1499 and 1502:
// Line 1485: not validated:
READ_SE_OR_RETURN(&shdr->slice_qp_delta);
// Missing: range check on SliceQPY
// Lines 1499/1502: validated:
READ_SE_OR_RETURN(&shdr->slice_alpha_c0_offset_div2);
IN_RANGE_OR_RETURN(shdr->slice_alpha_c0_offset_div2, -6, 6);
READ_SE_OR_RETURN(&shdr->slice_beta_offset_div2);
IN_RANGE_OR_RETURN(shdr->slice_beta_offset_div2, -6, 6);
READ_SE_OR_RETURN accepts exp-Golomb coded values up to approximately ±2³⁰. The unvalidated int value flows from the parser through H264Decoder to the VA-API delegate at h264_vaapi_video_decoder_delegate.cc:504, where SHDRToSP(slice_qp_delta) assigns it to VASliceParameterBufferH264.slice_qp_delta (int8_t), causing int→int8_t narrowing. For example:
slice_qp_delta=500→ int8_t truncation →-12slice_qp_delta=100→ survives truncation as100, producingSliceQPY=126— an OOB index into the 52-entry QP tableslice_qp_delta=128→ int8_t truncation →-128(sign flip)
This is the same bug class as Issue 482862710 (second_chroma_qp_index_offset, P1/S1), and was not addressed by the follow-up CLs:
- CL 7572949: fixed
second_chroma_qp_index_offset - CL 7574217: fixed
pic_parameter_set_id,idr_pic_id,changing_slice_group_idc - CL 7577373: fixed H.265 parser fields
None of these CLs addressed slice_qp_delta.
Platform scope: VA-API only (Linux x86, ChromeOS x86). D3D11 passes raw bitstream to the DXVA decoder and does not extract slice_qp_delta into separate struct fields. Confirmed via grep of media/gpu/windows/ showing zero matches for slice_qp_delta. V4L2 similarly does not extract it into V4L2 control structs, also confirmed via grep of media/gpu/v4l2/ showing zero matches. The parser-level spec violation affects all platforms, but only VA-API has the int→int8_t narrowing as a concrete exploitation vector.
Fix: Add range validation matching the spec constraint on SliceQPY. A conservative static bound:
IN_RANGE_OR_RETURN(shdr->slice_qp_delta, -51, 51);
Or the precise spec-derived bound:
int slice_qp_y = 26 + pps->pic_init_qp_minus26 + shdr->slice_qp_delta;
TRUE_OR_RETURN(slice_qp_y >= 0 && slice_qp_y <= 51 + 6 * sps->bit_depth_luma_minus8);
Reproduction: I have a standalone C++ PoC (28-byte Annex-B bitstream with slice_qp_delta=500) and a Chromium-style unit test with some test cases that demonstrate:
- Parser accepts extreme values (500, -500, 1000, -1000) —
kOk - int→int8_t truncation: 500 → -12
- Values just outside spec range (26, -27) accepted
- int8_t sign flip: 128 → -128
Impact analysis
Same trust boundary analysis as Issue 482862710. The unvalidated slice_qp_delta reaches kernel GPU drivers via the VA-API path when a user visits a page containing a malicious <video> element. The GPU sandbox does not mitigate this as the GPU process is designed to make VA-API calls, so malformed parameters reach the kernel through the sandbox’s allowed syscall surface.
The out-of-range SliceQPY value is used by drivers for QP-dependent operations (quantization parameter lookup, deblocking filter strength). With slice_qp_delta=100 (survives int8_t truncation intact), SliceQPY = 26 + 0 + 100 = 126, overshooting the 52-entry QP table by 74 entries. If the driver uses this for array indexing without clamping, this is a kernel OOB read.
CVE-2022-21813 and CVE-2022-21814 (NVIDIA) were this exact bug class. The parser must enforce spec compliance regardless of individual driver behavior.
The cause
What version of Chrome have you found the security issue in?
Current main branch
Is the security issue related to a crash?
No, it is not related to a crash.
Choose the type of vulnerability
Memory Corruption (in a non-sandboxed process)
How would you like to be publicly acknowledged for your report?
Luke Francis