Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient data validation in Media
DescriptionInsufficient data validation in Media
ComponentMedia
Bug ClassLogic Error
Tracker484665123
Fix commit77af3dfce928 (chromium/src) +12/-1
CISA KEVNot listed
CreditedLuke Francis
Disclosed2026-04-07

Changed Functions

FunctionChangeNotes
if
media/parsers/h264_parser.cc
modified

Files Changed

  • media/parsers/h264_parser.cc
From 77af3dfce928a72a2836e1232cb7ba62054e3027 Mon Sep 17 00:00:00 2001
From: Eugene Zemtsov <eugene@chromium.org>
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 <tmathmeyer@chromium.org>
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
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) {
Loading diff…

Original Bug Report

reported by lu...@icloud.com

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 → -12
  • slice_qp_delta=100 → survives truncation as 100, producing SliceQPY=126 — an OOB index into the 52-entry QP table
  • slice_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:

  1. Parser accepts extreme values (500, -500, 1000, -1000) — kOk
  2. int→int8_t truncation: 500 → -12
  3. Values just outside spec range (26, -27) accepted
  4. 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

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

View on issue tracker