CVE-2026-79139
Overview
Files Changed
media/gpu/windows/d3d12_video_encode_h264_delegate.ccmedia/gpu/windows/d3d12_video_encode_h264_delegate_unittest.ccmedia/gpu/windows/d3d12_video_encode_h265_delegate.ccmedia/gpu/windows/d3d12_video_encode_h265_delegate_unittest.cc
Patch
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;
});
Regression Test / PoC
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;
});
Original Bug Report
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.ccmedia/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.
- A compromised renderer initiates a video encoding session via Mojo
VideoEncodeAccelerator::Initializewithmanual_reference_buffer_control=trueusing an H.264 or HEVC profile. - The GPU process initializes the corresponding delegate. The driver-reported capabilities are queried, but
MaxL0ReferencesForPis ignored during bounds determination. - The renderer calls Mojo
Encodewith an active reference list (options.reference_buffers) containing more elements thanMaxL0ReferencesForP(e.g. 4 references on a driver supporting at most 1). - The GPU-side validation accepts the request since the count is smaller than
GetMaxNumOfManualRefBuffers(). - The delegate sets
pic_params_.List0ReferenceFramesCountto the oversized count and submits the command buffer to the driver. - If the vendor UMD sizes its internal structures or loop limits based on its advertised
MaxL0ReferencesForPcapability 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:
- In
D3D12VideoEncodeH264Delegate::InitializeVideoEncoder, clampmax_num_ref_frames_or limit the allowable manual reference buffer count returned byGetMaxNumOfManualRefBuffers()for P-frames to also considerMaxL0ReferencesForP. - In
D3D12VideoEncodeH265Delegate::InitializeVideoEncoder, ensure the maximum manual reference buffer size is clamped byMaxL0ReferencesForP. - Ensure that
D3D12VideoEncodeDelegate::Encode()rejects any encode options wherereference_buffers.size()is larger than the driver’s reportedMaxL0ReferencesForPlimit 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.