CVE-2026-5907
Overview
Background
- H.264 slice header
- the per-slice metadata parsed by
H264Parserthat carries coding parameters includingslice_qp_deltaand (for SP/SI slices)slice_qs_delta. - `SliceQPY` / `SliceQSY`
- derived quantization parameters:
SliceQPY = 26 + pic_init_qp_minus26 + slice_qp_delta(spec range-QpBdOffset_Y..51) andSliceQSY = 26 + pic_init_qs_minus26 + slice_qs_delta(spec range0..51). - `READ_SE_OR_RETURN`
- macro that reads a signed Exp-Golomb value from the bitstream into a field, with no range enforcement of its own.
- `IN_RANGE_OR_RETURN`
- macro that rejects the stream (returns a parse failure) when a value falls outside the given inclusive bounds.
Root Cause Analysis
During slice-header parsing, slice_qp_delta (and, for SP/SI slices, slice_qs_delta) was read via READ_SE_OR_RETURN but never validated, so the derived SliceQPY/SliceQSY could fall outside the H.264-mandated ranges. This violated the spec invariant that SliceQPY lie in [-QpBdOffset_Y, 51] and SliceQSY in [0, 51], allowing a malformed or malicious stream to carry out-of-range quantization parameters past the parser. The commit message states the risk explicitly: out-of-range values could reach “accelerated decoders” (hardware/GPU decode paths) that may assume spec-compliant inputs.
The fix adds IN_RANGE_OR_RETURN checks that reject the stream at parse time: it computes qp_bd_offset_y = 6 * sps->bit_depth_luma_minus8 and base_qp = 26 + pps->pic_init_qp_minus26, then requires slice_qp_delta in [-qp_bd_offset_y - base_qp, 51 - base_qp] (algebraically equivalent to SliceQPY in [-QpBdOffset_Y, 51]), and similarly bounds slice_qs_delta using base_qs = 26 + pps->pic_init_qs_minus26. This is a validation tightening; the checks run immediately after the read and before the values propagate further.
slice_qp_delta/slice_qs_delta without enforcing the spec-required bounds on the derived SliceQPY/SliceQSY; the fix adds IN_RANGE_OR_RETURN range checks (rewritten to bound the delta so no out-of-range intermediate is computed) so non-compliant values are rejected before reaching accelerated decoders.Attack Path
- Craft a malformed H.264 stream
The attacker produces a slice header whose
slice_qp_delta(orslice_qs_deltafor an SP/SI slice) encodes aSliceQPY/SliceQSYoutside the spec range. - Deliver to a victim
The stream is loaded via any surface that feeds
H264Parser(e.g. a<video>element, MSE, or a media file). - Parser accepts out-of-range QP (pre-patch)
READ_SE_OR_RETURNstores the delta with no bounds check, so the out-of-range quantization parameter passes slice-header parsing. - Value reaches an accelerated decoder The out-of-range QP is forwarded to a hardware/accelerated decode path that may not defend against non-compliant inputs. (inferred — the patch demonstrates only the missing validation, not a specific downstream sink or memory-corruption primitive)
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifmedia/parsers/h264_parser.cc |
modified |
Files Changed
media/parsers/h264_parser.cc
Audit Directions
- Read-without-validateAudit other
READ_SE_OR_RETURN/READ_UE_OR_RETURNfields inH264Parser(and sibling parsers) whose spec sections mandate value ranges but lack a followingIN_RANGE_OR_RETURN. - Derived-quantity boundsWhere a spec bound applies to a derived quantity (
base + delta), check that validation bounds the raw delta so the intermediate cannot itself overflow or escape range before the check. - Parser-to-accelerator trust boundaryReview which parsed media fields flow unchecked into hardware/accelerated decode paths, treating those paths as consumers that assume spec-compliant input.
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