Low chrome Integer Overflow 📄 Reporter bug report 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInteger overflow in Media
DescriptionInteger overflow in Media
ComponentMedia
Sub-componentInternals>Media
Bug ClassInteger Overflow
Tracker536471438
Fix commit0c78c9631b62 (chromium/src) +37/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Files Changed

  • media/base/limit_checks.h
  • media/gpu/vp9_decoder.cc
  • media/base/BUILD.gn
From 0c78c9631b62d8c1932bb701c628ea85ed50b9ed Mon Sep 17 00:00:00 2001
From: Ted Meyer <[email protected]>
Date: Wed, 09 Sep 2026 18:07:54 -0700
Subject: [PATCH] Perform bounds check on web-controlled dimensions

Fixed: 536471438
Change-Id: Idb269799e4d174165cfe18fc244f41a4f6368154
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8383109
Reviewed-by: Syed AbuTalib <[email protected]>
Commit-Queue: Ted (Chromium) Meyer <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1695069}
---
diff --git a/media/base/limit_checks.h b/media/base/limit_checks.h
new file mode 100644
index 0000000..c4ed699
--- /dev/null
+++ b/media/base/limit_checks.h
@@ -0,0 +1,26 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_LIMIT_CHECKS_H_
+#define MEDIA_BASE_LIMIT_CHECKS_H_
+
+#include "media/base/limits.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+
+// Checks whether a gfx::Size is within the valid bounds of kMaxDimension and
+// kMaxCanvas as defined in media::limits.
+constexpr bool ValidMediaSize(const gfx::Size& size) {
+  // We use uint64_t to prevent overflow when calculating the area.
+  return size.width() >= 0 && size.width() <= limits::kMaxDimension &&
+         size.height() >= 0 && size.height() <= limits::kMaxDimension &&
+         (static_cast<uint64_t>(size.width()) *
+          static_cast<uint64_t>(size.height())) <=
+             static_cast<uint64_t>(limits::kMaxCanvas);
+}
+
+}  // namespace media
+
+#endif  // MEDIA_BASE_LIMIT_CHECKS_H_
diff --git a/media/gpu/vp9_decoder.cc b/media/gpu/vp9_decoder.cc
index 74a7ec34..d464772 100644
--- a/media/gpu/vp9_decoder.cc
+++ b/media/gpu/vp9_decoder.cc
@@ -10,6 +10,7 @@
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "build/build_config.h"
+#include "media/base/limit_checks.h"
 #include "media/base/limits.h"
 #include "media/base/media_switches.h"
 #include "media/base/platform_features.h"
@@ -299,6 +300,15 @@
 
     DCHECK(!new_pic_size.IsEmpty());
 
+    // Ensure that the `new_pic_size` that comes directly from web-controlled
+    // contents is bounds checked before passing off the the HW decoder.
+    if (!ValidMediaSize(new_pic_size)) [[unlikely]] {
+      DVLOG(1) << "Frame size: " << new_pic_size.ToString()
+               << " exceeds maximum video size limits.";
+      SetError();
+      return kDecodeError;
+    }
+
     bool is_color_space_change = false;
     if (base::FeatureList::IsEnabled(kAVDColorSpaceChanges)) {
       is_color_space_change = new_color_space.IsSpecified() &&
diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn
index 2a47c10..fa6844f 100644
--- a/media/base/BUILD.gn
+++ b/media/base/BUILD.gn
@@ -217,6 +217,7 @@
     "key_systems_impl.h",
     "key_systems_support_registration.cc",
     "key_systems_support_registration.h",
+    "limit_checks.h",
     "limiting_audio_queue.cc",
     "limiting_audio_queue.h",
     "limits.h",
Loading diff…

Original Bug Report

reported by [email protected]

Lack of resolution bounds check in VP9Decoder before driver context creation

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: The Chromium VP9 parser and decoder do not restrict frame_width and frame_height against maximum limits (e.g., limits::kMaxDimension). These unvalidated dimensions (up to 65536) are passed directly to hardware-decoding drivers upon a config change. This potentially allows a malicious bitstream to trigger driver-side integer overflows in sandboxed GPU/OOP-VD processes.

Affected files:

  • media/gpu/vp9_decoder.cc
  • media/parsers/vp9_uncompressed_header_parser.cc
  • media/gpu/vaapi/vaapi_video_decoder.cc
  • media/gpu/windows/d3d11_video_decoder.cc
  • media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
  • media/gpu/mac/video_toolbox_vp9_accelerator.cc

Estimated timestamp from git blame: Unknown (Google3 checkout)

1. Summary of the Issue (Meant for Human Triage)

A potential validation gap exists in the Chromium VP9 video-decoding pipeline due to the omission of a frame-size bounding rule, equivalent to the H.264 §M3.1 checks found elsewhere in the codebase. When decoding a VP9 stream, VP9Decoder::pic_size_ is populated directly from the untrusted, attacker-controlled VP9 uncompressed-header fields frame_width and frame_height. These fields can specify dimensions in the range of 1 to 65536 without any enforcement of core Chromium resolution limits like limits::kMaxDimension (32,767) or limits::kMaxCanvas (2²⁹ − 1).

Upon encountering a resolution change during stream processing, the backend video decoders fetch this unvalidated size via VP9Decoder::GetPicSize() and pass it directly to driver-level context-creation APIs. This includes calls to vaCreateContext (VA-API on Linux/ChromeOS), ID3D11VideoDevice::CreateVideoDecoder (D3D11 on Windows), VIDIOC_S_FMT (V4L2 on ChromeOS-ARM), and CMVideoFormatDescriptionCreate (macOS VideoToolbox).

Passing extremely large frame dimensions (e.g., 65,535 × 65,535, representing an area roughly 8 times the maximum canvas limit) directly to GPU hardware drivers crosses a security trust boundary. Depending on the specific vendor’s driver implementation, this can result in driver-side 32-bit integer overflows during internal buffer sizing calculations (e.g., width * height * bytes_per_pixel). An overflow produces an undersized backing buffer, which can subsequently lead to out-of-bounds (OOB) memory writes when the hardware decodes the frame, compromising the integrity of the sandboxed GPU or OOP-VD process.


2. Proof-of-Concept & Detailed Execution Flow

Note: The following steps are theoretical/potential as our automated tooling agent does not currently have the capability to execute code to verify the driver-side crash on live hardware.

Potential Attacker Steps:

  1. An attacker controls a malicious website and delivers a crafted VP9 bitstream using WebCodecs (EncodedVideoChunk), Media Source Extensions (MSE), or a standard <video> tag.
  2. The malicious bitstream contains a VP9 keyframe. In the uncompressed header (per VP9 spec §6.2), the 16-bit fields frame_width_minus_1 and frame_height_minus_1 are set to 0xFFFE, resulting in a decoded dimension of 65535 × 65535.
  3. The browser process forwards this encoded video chunk via Mojo IPC to the isolated video decoding process (sandboxed GPU process on Windows/macOS, or sandboxed OOP-VD process on Linux/ChromeOS).

Code Execution Trace:

Step 1: Parsing without Semantic Bounds When decoding starts, the VP9 bitstream is parsed. In media/parsers/vp9_uncompressed_header_parser.cc, the frame size is read:

// media/parsers/vp9_uncompressed_header_parser.cc:702-706
void Vp9UncompressedHeaderParser::ReadFrameSize(Vp9FrameHeader* fhdr) {
  fhdr->frame_width = reader_.ReadLiteral(16) + 1;    // Range [1, 65536] — no semantic bound
  fhdr->frame_height = reader_.ReadLiteral(16) + 1;   // Range [1, 65536] — no semantic bound
}

Unlike H.264, there are no checks against limits::kMaxDimension. The parsed dimensions propagate to the allocate_size parameter in media/parsers/vp9_parser.cc:

// media/parsers/vp9_parser.cc:612-616
if (frame_info.allocate_size.IsEmpty()) {
  allocate_size->SetSize(fhdr->frame_width, fhdr->frame_height);  // Can be {65535, 65535}
} else {
  *allocate_size = frame_info.allocate_size;
}

Step 2: Decoder Size Tracking In media/gpu/vp9_decoder.cc, the decoder parses the frame and updates its state to {65535, 65535} without validation checks:

// media/gpu/vp9_decoder.cc:190-197
gfx::Size allocate_size;
...
Vp9Parser::Result res = parser_.ParseNextFrame(hdr.get(), &allocate_size, &decrypt_config_);
if (res == Vp9Parser::kOk) {
  curr_frame_hdr_ = std::move(hdr);
  curr_frame_size_ = allocate_size;  // Set to {65535, 65535}
}

Recognizing a config change, the decoder sets pic_size_ = new_pic_size; and returns kConfigChange (media/gpu/vp9_decoder.cc:349, 355).

Step 3: Sinking into Driver APIs Upon kConfigChange, the unvalidated dimensions reach hardware backends:

VA-API (Linux/ChromeOS OOP-VD):

// media/gpu/vaapi/vaapi_video_decoder.cc:698
const gfx::Size decoder_pic_size = decoder_->GetPicSize();  // {65535, 65535}
...
if (!vaapi_wrapper_->CreateContext(decoder_pic_size)) { ... }

// media/gpu/vaapi/vaapi_wrapper.cc:2560-2563
VAStatus va_res = vaCreateContext(
    va_display_, va_config_id_, picture_size.width(), picture_size.height(), // 65535, 65535 passed directly
    flag, empty_va_surfaces_ids_pointer, empty_va_surfaces_ids_size,
    &va_context_id_);

D3D11 (Windows GPU Process):

// media/gpu/windows/d3d11_decoder_configurator.cc:218-225
void D3D11DecoderConfigurator::SetUpDecoderDescriptor(const gfx::Size& coded_size) {
  ...
  decoder_desc_.SampleWidth = coded_size.width();    // 65535
  decoder_desc_.SampleHeight = coded_size.height();  // 65535
}

// media/gpu/windows/d3d11_video_decoder_wrapper.cc:381-382
// Directly calls the User Mode Driver (UMD)
hr = video_device->CreateVideoDecoder(
    decoder_configurator->DecoderDescriptor(), &dec_config, &video_decoder);

V4L2 (ChromeOS OOP-VD Stateless Backend):

// media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc:612-614
format.fmt.pix_mp.width = pic_size.width();        // 65535
format.fmt.pix_mp.height = pic_size.height();
if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0) { ... }

VideoToolbox (macOS GPU Process): By looping past kConfigChange, VT ultimately uses the unvalidated sizes:

// media/gpu/mac/video_toolbox_vp9_accelerator.cc:154-155, 177-180
gfx::Size coded_size(static_cast<int>(pic->frame_hdr->frame_width),   // 65535
                     static_cast<int>(pic->frame_hdr->frame_height));
...
OSStatus status = CMVideoFormatDescriptionCreate(
    kCFAllocatorDefault, kCMVideoCodecType_VP9, coded_size.width(),
    coded_size.height(), format_config.get(), ...);

Suggested Fix

Implement a safe bounds check in the VP9 parser logic similar to what H.264 uses (IN_RANGE_OR_RETURN). Specifically, check that frame_width and frame_height do not exceed limits::kMaxDimension (32,767) in media/parsers/vp9_uncompressed_header_parser.cc inside ReadFrameSize, or in media/parsers/vp9_parser.cc during allocate_size construction.


3. Technical Verification Details (Automated Audit Logs - Reviewers may skip this section)

> Verbatim Critic Verdict (2026-07-18 05:54:03): > * Severity: Medium (S2) > * Brief Notes / Reasoning: > The report accurately describes a validation gap in VP9Decoder, where bitstream frame_width and frame_height fields (up to 65536) are parsed and passed to driver APIs without a limits::kMaxDimension check. >
> Per the security_faq.md rubric: > - GPU validating-layer gap: The unvalidated size reaches driver context creation calls (vaCreateContext, CreateVideoDecoder, VIDIOC_S_FMT), crossing the trust boundary. This starts at High (S1). > - S2 Downgrade: Because no specific driver OOB write is demonstrated, and the majority of drivers are expected to cleanly reject the extreme dimensions (e.g., E_INVALIDARG, VA_STATUS_ERROR_RESOLUTION_NOT_SUPPORTED), the severity drops to Medium (S2) per the rule: ‘A GPU validating-layer gap (per the S1 row) where the spec-invalid value reaches the driver but the shown consequence is… majority of drivers GL_INVALID_*-tolerate it.’ > - S1 Bitstream Delegate Rule: The report notes the unvalidated field is also copied verbatim into HW-decode driver structs (e.g., DXVA_PicParams_VP9.width), which typically warrants S1. However, since the driver context creation fires on the kConfigChange path before the SubmitDecode delegate copy, drivers that reject the context creation will halt decoding, preventing the value from reaching the S1 delegate sink. > - Process Context: The vulnerable code runs in sandboxed GPU/OOP-VD processes. The unsandboxed Android GPU exception does not apply, as Android uses MediaCodecVideoDecoder rather than VP9Decoder.

Evidence Ledger

  • Target Platforms: Windows (D3D11), macOS (VideoToolbox), Linux/ChromeOS (VA-API / V4L2).
  • Android Status: Android builds do not instantiate VP9Decoder in media/gpu/android/ (they use MediaCodecVideoDecoder). Confirmed unreachable.
  • Process Isolation: The relevant sinks execute inside the sandboxed GPU process (Windows, macOS) or the out-of-process video decoding (OOP-VD) utility process (Linux, ChromeOS).
  • Code Path Validation: Traces confirmed that limits::kMaxDimension (32,767) is omitted in Vp9UncompressedHeaderParser::ReadFrameSize and VP9Decoder::Decode. The only existing guard is DCHECK(!new_pic_size.IsEmpty()) which is non-load-bearing in release builds.
  • Mitigating Factors Checked: checked_cast<uint16_t> inside the VA-API delegate crashes on precisely 65,536 but passes 65,535. However, vaCreateContext executes before delegate submission regardless. Similarly, downstream NativePixmapFrameResource::Create / D3D11 CreateTexture2D allocations are constrained (e.g., capped at 16,384), but these limits are checked after the driver API context-creation sinks are called with the unvalidated values.

Evaluated with Chrome root at commit: b96d2ec58f4f5f92b540a723966b199d6e9951b4


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

Data from false positives will be used to improve 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