CVE-2026-12019
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
formedia/gpu/vaapi/vaapi_video_encode_accelerator.cc |
modified | |
ifmedia/gpu/vaapi/vaapi_video_encode_accelerator.cc |
modified |
Files Changed
media/gpu/vaapi/vaapi_video_encode_accelerator.cc
Patch
From f9ef98d31c1a32833b75d830e2865cd233e017f2 Mon Sep 17 00:00:00 2001 From: Hirokazu Honda <[email protected]> Date: Mon, 22 Jun 2026 18:29:02 -0700 Subject: [PATCH] media/gpu/vaapiVEA: Remove scale size check There are some cases that the input video frame is less than the encode size. Allowing up-scaling in vaapi VEA has no problem as the VASurface for input video frame is created dynamically. Bug: 516872067, 525450909 Test: ui.MeetCUJ.docs Change-Id: I4c93d121cfd78d370123ef0210d93a95261bb36f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7981578 Reviewed-by: Nathan Hebert <[email protected]> Commit-Queue: Hirokazu Honda <[email protected]> Cr-Commit-Position: refs/heads/main@{#1650712} --- diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc index 0356228..7665e38 100644 --- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc +++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc @@ -9,6 +9,7 @@ #include <algorithm> #include <memory> +#include <sstream> #include <type_traits> #include <utility> #include <variant> @@ -78,6 +79,17 @@ return surfaces.empty() ? nullptr : std::move(surfaces.front()); } +std::string SpatialLayersToString( + const std::vector<gfx::Size>& spatial_layer_resolutions) { + std::stringstream ss; + ss << "{"; + for (const gfx::Size& s : spatial_layer_resolutions) { + ss << s.ToString() << ", "; + } + ss << "}"; + return ss.str(); +} + } // namespace struct VaapiVideoEncodeAccelerator::InputFrameRef { @@ -642,8 +654,8 @@ } } - // The downscaling for-loop below relies on |spatial_layer_resolutions| - // ordered from small to larger ones. It cannot contain duplicates. + // The scaling for-loop below relies on |spatial_layer_resolutions| ordered + // from small to larger ones. It cannot contain duplicates. // TODO(crbug.com/40172317): Consider supporting multiple layers with the // same resolution. CHECK(std::ranges::is_sorted(spatial_layer_resolutions, @@ -656,18 +668,23 @@ TRACE_EVENT1("media,gpu", "VAVEA::ConstructSurfaces", "layers", spatial_layer_resolutions.size()); auto source_rect = frame.visible_rect(); - for (const gfx::Size& encode_size : spatial_layer_resolutions) { - if (encode_size.width() > source_rect.width() || - encode_size.height() > source_rect.height()) { - NotifyError({EncoderStatus::Codes::kInvalidInputFrame, - "Only down scaling is supported in spatial layer encoding"}); - return false; - } + for (size_t i = 0; i < spatial_layer_resolutions.size(); ++i) { + const gfx::Size& encode_size = spatial_layer_resolutions[i]; + const bool is_last_layer = (i == spatial_layer_resolutions.size() - 1); const bool engage_vpp = source_rect != gfx::Rect(encode_size); // Crop and scale |source_surface| to a surface whose size is |encode_size|. // The size of a reconstructed surface is also |encode_size|. - CHECK(source_surface); - if (engage_vpp) { + if (!source_surface) { + NotifyError( + {EncoderStatus::Codes::kInvalidInputFrame, + base::StrCat( + {"Assumption failure: at most one same resolution spatial layer " + "and it is top: source_rect: ", + source_rect.ToString(), ", spatial_layer_resolutions: ", + SpatialLayersToString(spatial_layer_resolutions)})}); + return false; + } + if (engage_vpp || !is_last_layer) { input_surfaces->push_back( ExecuteBlitSurface(source_surface.get(), source_rect, encode_size)); } else {
Original Bug Report
Potential GPU Process Heap OOB Write via Mismatched VP9 Spatial Layer Resolutions in VA-API
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: An incomplete dimension check in MojoVideoEncodeAcceleratorService allows a compromised renderer to configure spatial layers with resolutions far exceeding the input visible size. This mismatch causes the GPU process to allocate an undersized VA-API encoder context and output bitstream buffer while executing the encode operation using the massive spatial layer resolutions, potentially leading to a heap out-of-bounds write inside the GPU process.
Affected files:
media/mojo/services/mojo_video_encode_accelerator_service.ccmedia/gpu/vaapi/vaapi_video_encode_accelerator.ccmedia/gpu/vaapi/vp9_vaapi_video_encoder_delegate.cc
Estimated timestamp from git blame: 2021-06-25
Summary
A potential heap out-of-bounds (OOB) write vulnerability has been identified in the GPU process’s VA-API video encoder implementation. Due to missing validation checking that configured spatial layer resolutions are bounded by the input visible size (input_visible_size), an attacker in control of a compromised renderer process could supply mismatched configurations. This results in the allocation of an undersized VA-API encoder context and coded buffer while submitting frame encoding parameters using the much larger spatial layer resolutions, leading to potential driver-level heap corruption.
Root Cause Analysis
In media/mojo/services/mojo_video_encode_accelerator_service.cc, MojoVideoEncodeAcceleratorService::Initialize performs validation on the configured spatial layers:
for (const auto& spatial_layer : config.spatial_layers) {
if (spatial_layer.width > limits::kMaxDimension ||
spatial_layer.height > limits::kMaxDimension ||
base::CheckMul<uint64_t>(spatial_layer.width, spatial_layer.height)
.ValueOrDefault(...) > limits::kMaxCanvas) {
// Reject
}
}
While this validates spatial layers against absolute global bounds, it fails to enforce that each spatial layer’s resolution is bounded by config.input_visible_size (or the profile’s maximum resolution).
When a VP9 k-SVC configuration is initialized:
- Context and Buffer Sizing:
VaapiVideoEncodeAccelerator::InitializeTaskinmedia/gpu/vaapi/vaapi_video_encode_accelerator.cccreates the VA-API context viavaapi_wrapper_->CreateContext(encoder_->GetCodedSize())(line 436). The coded size is determined based on the smallinput_visible_size(e.g.,320x240), leading to an undersized VA-API context. The output coded buffer (VAEncCodedBuffer) size is similarly determined based on this small resolution. - Parameter Submission: In contrast, during the per-frame encoding loop,
VP9VaapiVideoEncoderDelegate::SubmitFrameParameters(inmedia/gpu/vaapi/vp9_vaapi_video_encoder_delegate.cc) populatesVAEncPictureParameterBufferVP9with source and destination dimensions from the spatial layer (frame_width_srcandframe_height_src), which can take unvalidated massive values up to16384x16384(copied from the configuration config viaSVCLayers). - Mismatched Execution: The hardware-accelerated VA-API graphics driver is then requested to perform an encode operation with massive dimensions on an undersized context and output buffer, resulting in a potential heap out-of-bounds write on the driver heap inside the sandboxed GPU process.
Suggested / Potential Trigger Steps
Note: These are potential steps and have not been executed, as our tooling agent does not have code execution capabilities.
- From a compromised renderer, bind the
media.mojom.VideoEncodeAcceleratorinterface. - Call
Initializewith the following parameters:input_format:PIXEL_FORMAT_NV12input_visible_size:320x240output_profile:VP9PROFILE_PROFILE0storage_type:kGpuMemoryBufferinter_layer_pred:kOnKeyPicspatial_layers:[{160x120, ...}, {320x240, ...}, {16384x16384, ...}]
- The initialization succeeds. The GPU process allocates a VA context for
320x256and an output coded buffer based on that resolution. - Call
Encodewith an NV12 GpuMemoryBuffer-backedVideoFrameof320x240. - The GPU process executes
SubmitFrameParameterswithframe_width_srcandframe_height_srcset to16384against the small context and output buffer. - This causes the underlying VA-API driver to write past the allocated buffer bounds.
Suggested Fix
To remediate this issue, add validation in MojoVideoEncodeAcceleratorService::Initialize or VaapiVideoEncodeAccelerator::Initialize to explicitly reject configurations where any spatial layer resolution exceeds config.input_visible_size or the supported profile’s maximum resolution limit.
For example, in VaapiVideoEncodeAccelerator::Initialize:
for (const auto& spatial_layer : config.spatial_layers) {
if (spatial_layer.width > config.input_visible_size.width() ||
spatial_layer.height > config.input_visible_size.height()) {
MEDIA_LOG(ERROR, media_log.get()) << "Spatial layer resolution exceeds input_visible_size";
return {EncoderStatus::Codes::kEncoderInitializationError};
}
}
Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8
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.