High watch CVSS 5.3 chrome OOB 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
5.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds read in Media
DescriptionOut of bounds read in Media
ComponentMedia
Sub-componentMedia
Bug ClassOOB
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar
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.
Tracker495530312
Fix commit24e44607b2d6 (chromium/src) +9/-0
CWECWE-125 (Out-of-bounds read)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-03-23 (per vendor advisory)
Fix landed2026-03-24 (public git)
Disclosed2026-05-12
Patch gap49 days — fix public in source before the advisory

Background

JPEG SOF marker
The Start of Frame marker describes each image component, including a per-component quantization_table_selector that indexes which quantization table to use.
`quantization_table_selector`
A single byte read from attacker-controlled JPEG data that selects a quantization table; valid values are 0..3.
`kJpegMaxQuantizationTableNum`
The hard limit (4) on the number of quantization tables, i.e., the valid bound for the selector index.
VA-API hardware decode
The GPU process forwards the parsed selector to hardware decoders (e.g., VA-API), which use it directly as an array index.

Root Cause Analysis

Both the C++ parser (media/parsers/jpeg_parser.cc, enclosing function not named in the @@ -192 hunk) and the Rust parser (media/parsers/parse_jpeg.rs, enclosing function not named in the @@ -214 hunk) read quantization_table_selector from the SOF marker via READ_U8_OR_RETURN_FALSE / read_u8 but performed no range validation before storing it in the component/frame header. Because a u8 can be 0..255 while only 0..3 are valid, a malformed JPEG could carry a selector of, say, 200, which was then passed on to hardware decoders like VA-API that use it as an array index into a 4-entry table, causing an out-of-bounds read in the GPU process.

The fix adds, immediately after reading the byte, a check quantization_table_selector >= kJpegMaxQuantizationTableNum (C++) / >= K_JPEG_MAX_QUANTIZATION_TABLE_NUM (Rust) that returns failure (false / JpegParserError::InvalidQuantizationTableSelector) before the value is stored or forwarded. This re-establishes the invariant that any selector leaving the parser is within [0, 4), so the downstream array index is always in range.

Key insight
The single mistake was treating an attacker-controlled 8-bit selector as a trusted table index without bounding it against kJpegMaxQuantizationTableNum; the fix rejects any selector >= 4 at parse time, before it can reach the VA-API indexing.

Attack Path

  1. Serve malformed JPEG The attacker supplies a JPEG whose SOF marker sets a component’s quantization_table_selector to an out-of-range value (>= 4).
  2. Parser accepts it Pre-patch, jpeg_parser.cc/parse_jpeg.rs read the byte and stored it in the component without validation.
  3. Index with bad selector The unvalidated selector is handed to a hardware decoder (VA-API) that uses it as an array index into the 4-entry quantization-table array, reading out of bounds in the GPU process (per the commit message; the specific VA-API sink is outside the diff and thus (inferred) in exact form).
  4. Information disclosure / crash (inferred) The out-of-bounds read may leak adjacent GPU-process memory or crash the process; the diff itself demonstrates only the missing bounds check.

Impact Assessment

An attacker who gets a crafted JPEG decoded (e.g., via a web page using hardware/accelerated JPEG decode) can cause an out-of-bounds read in the GPU process, which the advisory classifies as “Out of bounds read in Media” at high severity. The precondition is that the image reach a hardware decoder that uses the selector as an index; any resulting memory disclosure beyond the bounded read, or escalation, is (inferred). The advisory states no exploited-in-the-wild status.

Changed Functions

FunctionChangeNotes
if
media/parsers/jpeg_parser.cc
modified

Files Changed

  • media/parsers/jpeg_parser.cc
  • media/parsers/parse_jpeg.rs

Audit Directions

  • Unvalidated selectors/indices from media bitstreams
    Audit all READ_U8/read_u8 reads whose results are later used as array indices (quantization, Huffman, component, scan selectors) for a missing >= kMax... bound.
  • Values forwarded to hardware/IPC decoders
    Any parser value passed to VA-API/GPU decoders should be range-checked in the parser, since the hardware/driver layer trusts it; look for indices crossing the parser→decoder boundary.
  • C++/Rust parser parity
    When two implementations parse the same format, confirm both enforce identical bounds; a check added to one but not the other leaves a gap.
From 24e44607b2d6dda11f794522fe5d93f5fd09c9b7 Mon Sep 17 00:00:00 2001
From: Eugene Zemtsov <[email protected]>
Date: Mon, 23 Mar 2026 19:35:02 -0700
Subject: [PATCH] media: Add bounds check for JPEG quantization table selector

The C++ and Rust JPEG parsers previously failed to validate the
`quantization_table_selector` found in the Start of Frame (SOF) marker.

This unvalidated value was passed to hardware decoders like VA-API, which
uses it as an array index, potentially leading to out-of-bounds memory
reads in the GPU process.

This CL adds bounds checking against `kJpegMaxQuantizationTableNum` (4)
in `jpeg_parser.cc` and `parse_jpeg.rs`.

Bug: 495530312
Change-Id: I776b72e44b8ea73c52b80d93c6a9598bc0f800d3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7693890
Commit-Queue: Jordan Bayles <[email protected]>
Reviewed-by: Jordan Bayles <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1603862}
---

diff --git a/media/parsers/jpeg_parser.cc b/media/parsers/jpeg_parser.cc
index 426c922..000267c74 100644
--- a/media/parsers/jpeg_parser.cc
+++ b/media/parsers/jpeg_parser.cc
@@ -192,6 +192,11 @@
       return false;
     }
     READ_U8_OR_RETURN_FALSE(component.quantization_table_selector);
+    if (component.quantization_table_selector >= kJpegMaxQuantizationTableNum) {
+      DVLOG(1) << "Invalid quantization table selector "
+               << static_cast<int>(component.quantization_table_selector);
+      return false;
+    }
   }
 
   // The size of data unit is 8*8 and the coded size should be extended
diff --git a/media/parsers/parse_jpeg.rs b/media/parsers/parse_jpeg.rs
index 9013299..5da7be90 100644
--- a/media/parsers/parse_jpeg.rs
+++ b/media/parsers/parse_jpeg.rs
@@ -83,6 +83,7 @@
         ExpectedMarkerPrefix = 25,
         OnlySof0Supported = 26,
         DqtMarkerNotFound = 27,
+        InvalidQuantizationTableSelector = 28,
     }
 
     struct JpegParseResult {
@@ -214,6 +215,9 @@
         }
 
         let quantization_table_selector = read_u8(&mut cursor)?;
+        if quantization_table_selector as usize >= K_JPEG_MAX_QUANTIZATION_TABLE_NUM {
+            return Err(JpegParserError::InvalidQuantizationTableSelector);
+        }
 
         frame_header.components.push(JpegComponent {
             id,
Loading diff…

Original Bug Report

reported by [email protected]

Potential OOB Read in VA-API Driver via Unvalidated JPEG Quantization Table Selector

Flapjack (go/flapjack), an LLM-powered static analysis tool, has identified the following potential security issue.

Overview: A compromised renderer process can potentially trigger an out-of-bounds read in the GPU process by supplying a malicious JPEG to the hardware decoder. The JPEG parser fails to validate the quantization table selector, passing an arbitrary index to the VA-API driver. This can leak GPU memory contents back to the renderer through the decoded image.

Affected files:

  • media/parsers/jpeg_parser.cc
  • media/gpu/vaapi/vaapi_jpeg_decoder.cc

Estimated timestamp from git blame: 2024-04-12

This issue allows a compromised renderer to potentially leak GPU process memory via an out-of-bounds read in the VA-API hardware decoder, aiding in a sandbox escape.

Technical Details

Standard Mojo IPC setup from a compromised renderer establishes a MjpegDecodeAccelerator session. The vulnerability occurs during the parsing of the JPEG bitstream.

When parsing the JPEG_SOF0 marker, media/parsers/jpeg_parser.cc reads the quantization_table_selector for each image component but does not enforce the 0-3 baseline limit:

// media/parsers/jpeg_parser.cc : ParseSOF()
READ_U8_OR_RETURN_FALSE(component.quantization_table_selector);

This unvalidated selector (which can be up to 255) is mapped directly to pic_param_components[i].quantiser_table_selector in media/gpu/vaapi/vaapi_jpeg_decoder.cc and passed to the third-party VA-API driver in the GPU process.

The VA-API driver uses this value to index the 4-element quantization table array (e.g., VAIQMatrixBufferJPEGBaseline). Providing an index > 3 results in a direct OOB read. The OOB memory is treated as quantization matrix data, mathematically embedding the leaked memory into the output decoded image pixels.

Potential Attacker Steps

(Note: These are suggested/potential steps, as the Flapjack LLM agent does not have the ability to run code to verify a working exploit).

  1. Gain code execution in a sandboxed renderer process.
  2. Bind the viz::mojom::Gpu interface and call CreateJpegDecodeAccelerator() to reach the GPU process.
  3. Send a decode request containing a malicious JPEG with the quantization_table_selector set to an out-of-bounds value (e.g., 255) in the SOF0 marker.
  4. Read the resulting decoded image buffer and reverse the IDCT/dequantization math to infer the OOB memory contents from the GPU process.

Suggested Fix

Add a bounds check in media/parsers/jpeg_parser.cc within the ParseSOF function to ensure the selector does not exceed the maximum allowed tables:

READ_U8_OR_RETURN_FALSE(component.quantization_table_selector);
if (component.quantization_table_selector >= kJpegMaxQuantizationTableNum) {
  DVLOG(1) << "Invalid quantization table selector";
  return false;
}

Evaluated with Chrome root at commit: 9760e6c70cd33a320713361f17c6dcca85648c0f


Results from Flapjack so far have been promising, but it can be wrong in its deductions. At this time, it does not produce proof of concepts or fuzzer tests. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve Flapjack’s accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker