Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactImproper input validation in Media
DescriptionImproper input validation in Media
ComponentMedia
Bug ClassLogic Error
Tracker532917452
Fix commit76e56a93297a (chromium/src) +11/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Files Changed

  • media/gpu/windows/d3d12_video_encode_h264_delegate.cc
  • media/gpu/windows/d3d12_video_encode_h264_delegate_unittest.cc
  • media/gpu/windows/d3d12_video_encode_h265_delegate.cc
  • media/gpu/windows/d3d12_video_encode_h265_delegate_unittest.cc
From 76e56a93297aff4d06e8fd4b9cb2f8df1f61b7d3 Mon Sep 17 00:00:00 2001
From: Qiu Jianlin <jianlin.qiu@intel.com>
Date: Fri, 10 Jul 2026 16:30:13 -0700
Subject: [PATCH] [D3D12 VEA] Bound max manual reference to driver L0 ref limit.

Make sure maximum allowed manual references do not exceed L0 p frame
references allowed by driver.

Bug: 532917452
Change-Id: I7afd6efeac27398643b2c0d53111aac17d676304
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8074938
Auto-Submit: Qiu, Jianlin <jianlin.qiu@intel.com>
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
Reviewed-by: Eugene Zemtsov <eugene@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1660596}
---

diff --git a/media/gpu/windows/d3d12_video_encode_h264_delegate.cc b/media/gpu/windows/d3d12_video_encode_h264_delegate.cc
index 2f554e89..bab79517 100644
--- a/media/gpu/windows/d3d12_video_encode_h264_delegate.cc
+++ b/media/gpu/windows/d3d12_video_encode_h264_delegate.cc
@@ -657,6 +657,12 @@
     max_num_ref_frames_ = std::min<uint32_t>(
         picture_control_support_h264.MaxDPBCapacity,
         picture_control_support_h264.MaxLongTermReferences + 1);
+    // We never see driver with MaxL0ReferenceForP < MaxLongTermReferences,
+    // but bound to it in case it happens. Manual reference buffer is with
+    // budget (max_num_ref_frames_ -1).
+    max_num_ref_frames_ = std::min<uint32_t>(
+        max_num_ref_frames_,
+        picture_control_support_h264.MaxL0ReferencesForP + 1);
   }
 
   if ((config.bitrate.mode() == Bitrate::Mode::kConstant ||
diff --git a/media/gpu/windows/d3d12_video_encode_h264_delegate_unittest.cc b/media/gpu/windows/d3d12_video_encode_h264_delegate_unittest.cc
index fd032dc..3ae0cd8 100644
--- a/media/gpu/windows/d3d12_video_encode_h264_delegate_unittest.cc
+++ b/media/gpu/windows/d3d12_video_encode_h264_delegate_unittest.cc
@@ -73,6 +73,7 @@
           }
           picture_control->PictureSupport.pH264Support->MaxLongTermReferences =
               15;
+          picture_control->PictureSupport.pH264Support->MaxL0ReferencesForP = 3;
           picture_control->PictureSupport.pH264Support->MaxDPBCapacity = 16;
           return S_OK;
         });
diff --git a/media/gpu/windows/d3d12_video_encode_h265_delegate.cc b/media/gpu/windows/d3d12_video_encode_h265_delegate.cc
index 6a410b7..f99a2bd8 100644
--- a/media/gpu/windows/d3d12_video_encode_h265_delegate.cc
+++ b/media/gpu/windows/d3d12_video_encode_h265_delegate.cc
@@ -513,7 +513,9 @@
                   max_num_ref_frames_)};
     }
   } else {
-    max_num_ref_frames_ = picture_control_support_h265.MaxDPBCapacity;
+    max_num_ref_frames_ =
+        std::min<uint32_t>(picture_control_support_h265.MaxDPBCapacity,
+                           picture_control_support_h265.MaxL0ReferencesForP);
   }
 
   if ((config.bitrate.mode() == Bitrate::Mode::kConstant ||
diff --git a/media/gpu/windows/d3d12_video_encode_h265_delegate_unittest.cc b/media/gpu/windows/d3d12_video_encode_h265_delegate_unittest.cc
index 21b2d3e..054c4b80 100644
--- a/media/gpu/windows/d3d12_video_encode_h265_delegate_unittest.cc
+++ b/media/gpu/windows/d3d12_video_encode_h265_delegate_unittest.cc
@@ -70,6 +70,7 @@
           }
           picture_control->PictureSupport.pHEVCSupport->MaxLongTermReferences =
               1;
+          picture_control->PictureSupport.pHEVCSupport->MaxL0ReferencesForP = 3;
           picture_control->PictureSupport.pHEVCSupport->MaxDPBCapacity = 16;
           return S_OK;
         });
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/media/gpu/windows/d3d12_video_encode_h264_delegate_unittest.cc b/media/gpu/windows/d3d12_video_encode_h264_delegate_unittest.cc
index fd032dc..3ae0cd8 100644
--- a/media/gpu/windows/d3d12_video_encode_h264_delegate_unittest.cc
+++ b/media/gpu/windows/d3d12_video_encode_h264_delegate_unittest.cc
@@ -73,6 +73,7 @@
           }
           picture_control->PictureSupport.pH264Support->MaxLongTermReferences =
               15;
+          picture_control->PictureSupport.pH264Support->MaxL0ReferencesForP = 3;
           picture_control->PictureSupport.pH264Support->MaxDPBCapacity = 16;
           return S_OK;
         });
diff --git a/media/gpu/windows/d3d12_video_encode_h265_delegate_unittest.cc b/media/gpu/windows/d3d12_video_encode_h265_delegate_unittest.cc
index 21b2d3e..054c4b80 100644
--- a/media/gpu/windows/d3d12_video_encode_h265_delegate_unittest.cc
+++ b/media/gpu/windows/d3d12_video_encode_h265_delegate_unittest.cc
@@ -70,6 +70,7 @@
           }
           picture_control->PictureSupport.pHEVCSupport->MaxLongTermReferences =
               1;
+          picture_control->PictureSupport.pHEVCSupport->MaxL0ReferencesForP = 3;
           picture_control->PictureSupport.pHEVCSupport->MaxDPBCapacity = 16;
           return S_OK;
         });
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential GPU process OOB access via unvalidated D3D12 video encode active reference counts

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 D3D12 H264 and HEVC video encode delegates fail to clamp the number of active L0 reference frames against the driver-advertised MaxL0ReferencesForP capability. A compromised renderer could supply an excessive number of reference buffers, resulting in a potential out-of-bounds read/write inside the sandboxed GPU process via the vendor User Mode Driver. These are potential steps and findings as our tooling does not currently have the capability to run code to confirm.

Affected files:

  • media/gpu/windows/d3d12_video_encode_h264_delegate.cc
  • media/gpu/windows/d3d12_video_encode_h265_delegate.cc

Estimated timestamp from git blame: 2025-08-26

Root Cause

Under Direct3D 12, the D3D12_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT_H264 and _HEVC structures expose driver-defined capability boundaries, including MaxL0ReferencesForP (which specifies the maximum number of L0 reference frames allowed for P-frames).

However, Chrome’s D3D12 video encode delegates completely ignore MaxL0ReferencesForP. Instead, the delegates derive the maximum number of manual reference buffers solely using MaxDPBCapacity and MaxLongTermReferences:

  • H264 Delegate (media/gpu/windows/d3d12_video_encode_h264_delegate.cc:657-659):
    max_num_ref_frames_ = std::min<uint32_t>(
        picture_control_support_h264.MaxDPBCapacity,
        picture_control_support_h264.MaxLongTermReferences + 1);
    
  • HEVC Delegate (media/gpu/windows/d3d12_video_encode_h265_delegate.cc:515-517):
    max_num_ref_frames_ = picture_control_support_h265.MaxDPBCapacity;
    

Because of this, GetMaxNumOfManualRefBuffers() returns a bound that is not constrained by MaxL0ReferencesForP. When the compromised renderer issues an Encode() request, options.reference_buffers.size() is validated only against GetMaxNumOfManualRefBuffers().

This un-clamped list is then written straight to the D3D12 picture parameters sent to the vendor User Mode Driver (UMD):

  • H264 Sink (media/gpu/windows/d3d12_video_encode_h264_delegate.cc:500-501):
    pic_params_.List0ReferenceFramesCount = reference_buffers.size();
    pic_params_.pList0ReferenceFrames     = list0_reference_frames_.data();
    
  • HEVC Sink (media/gpu/windows/d3d12_video_encode_h265_delegate.cc:394-395):
    pic_params_.List0ReferenceFramesCount = reference_buffers.size();
    pic_params_.pList0ReferenceFrames     = list0_reference_frames_.data();
    

This structural data is ultimately submitted to the UMD via ID3D12VideoEncodeCommandList::EncodeFrame.

Potential Attack Path

Note: These steps are logical predictions based on static code analysis; our automated tools do not have the capability to execute code or verify runtime behavior against specific hardware.

  1. A compromised renderer initiates a video encoding session via Mojo VideoEncodeAccelerator::Initialize with manual_reference_buffer_control=true using an H.264 or HEVC profile.
  2. The GPU process initializes the corresponding delegate. The driver-reported capabilities are queried, but MaxL0ReferencesForP is ignored during bounds determination.
  3. The renderer calls Mojo Encode with an active reference list (options.reference_buffers) containing more elements than MaxL0ReferencesForP (e.g. 4 references on a driver supporting at most 1).
  4. The GPU-side validation accepts the request since the count is smaller than GetMaxNumOfManualRefBuffers().
  5. The delegate sets pic_params_.List0ReferenceFramesCount to the oversized count and submits the command buffer to the driver.
  6. If the vendor UMD sizes its internal structures or loop limits based on its advertised MaxL0ReferencesForP capability and trusts the API caller not to exceed it, an out-of-bounds read/write could occur inside the GPU process memory space.

Suggested Fix

We recommend reading and enforcing MaxL0ReferencesForP during initialization and validation:

  1. In D3D12VideoEncodeH264Delegate::InitializeVideoEncoder, clamp max_num_ref_frames_ or limit the allowable manual reference buffer count returned by GetMaxNumOfManualRefBuffers() for P-frames to also consider MaxL0ReferencesForP.
  2. In D3D12VideoEncodeH265Delegate::InitializeVideoEncoder, ensure the maximum manual reference buffer size is clamped by MaxL0ReferencesForP.
  3. Ensure that D3D12VideoEncodeDelegate::Encode() rejects any encode options where reference_buffers.size() is larger than the driver’s reported MaxL0ReferencesForP limit when encoding a non-keyframe (P-frame).

Evaluated with Chrome root at commit: 84065d9121f6e48f67755f0ae963cc09617e5c85


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