Low watch CVSS 8.1 chrome Input Validation 📄 Reporter bug report 🔧 Commit mapped

Overview

Low
Severity
8.1
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient data validation in Media
DescriptionInsufficient data validation in Media
ComponentMedia
Sub-componentMedia
Bug ClassInput Validation
Action tierwatch — ub-class only — no demonstrated corruption path
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker484665123
Fix commit77af3dfce928 (chromium/src) +12/-1
CWECWE-125 (Out-of-bounds read)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:H
CISA KEVNot listed
CreditedLuke Francis
Reported2026-02-15 (per vendor advisory)
Fix landed2026-02-18 (public git)
Disclosed2026-04-07
Patch gap48 days — fix public in source before the advisory

Background

H.264 slice header
the per-slice metadata parsed by H264Parser that carries coding parameters including slice_qp_delta and (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) and SliceQSY = 26 + pic_init_qs_minus26 + slice_qs_delta (spec range 0..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.

Key insight
The core mistake was parsing 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

  1. Craft a malformed H.264 stream The attacker produces a slice header whose slice_qp_delta (or slice_qs_delta for an SP/SI slice) encodes a SliceQPY/SliceQSY outside the spec range.
  2. Deliver to a victim The stream is loaded via any surface that feeds H264Parser (e.g. a <video> element, MSE, or a media file).
  3. Parser accepts out-of-range QP (pre-patch) READ_SE_OR_RETURN stores the delta with no bounds check, so the out-of-range quantization parameter passes slice-header parsing.
  4. 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

An attacker who controls a decoded H.264 bitstream can pass spec-violating quantization parameters through the parser to downstream (accelerated) decoders; the precondition is only that the victim decodes attacker-supplied media. The advisory rates this low severity (“Insufficient data validation in Media”) with no reported in-the-wild exploitation. The patch itself demonstrates only insufficient input validation — any decoder misbehavior, corruption, or escalation from the out-of-range value is (inferred) and not established by the diff.

Changed Functions

FunctionChangeNotes
if
media/parsers/h264_parser.cc
modified

Files Changed

  • media/parsers/h264_parser.cc

Audit Directions

  • Read-without-validate
    Audit other READ_SE_OR_RETURN/READ_UE_OR_RETURN fields in H264Parser (and sibling parsers) whose spec sections mandate value ranges but lack a following IN_RANGE_OR_RETURN.
  • Derived-quantity bounds
    Where 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 boundary
    Review which parsed media fields flow unchecked into hardware/accelerated decode paths, treating those paths as consumers that assume spec-compliant input.
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