Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Codecs
DescriptionUse after free in Codecs
ComponentCodecs
Bug ClassUAF
Tracker516997135
Fix commitf4b59c1cc520 (chromium/src) +15/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-08

Files Changed

  • media/gpu/windows/d3d12_copy_command_list_wrapper.cc
  • media/gpu/windows/d3d12_copy_command_list_wrapper.h
  • media/gpu/windows/d3d12_fence.cc
From f4b59c1cc5201a0bf0d06359673d2bf4fe0557a9 Mon Sep 17 00:00:00 2001
From: Eugene Zemtsov <eugene@chromium.org>
Date: Wed, 27 May 2026 22:39:15 -0700
Subject: [PATCH] media: Sync copy queue before D3D12 VEA teardown

Ensure that in-flight COPY-engine operations complete before releasing
underlying D3D12 resources. Destructors of D3D12VideoEncodeAccelerator
will now CPU-wait on the copy command queue wrapper's fence to avoid
GPU-side use-after-free issues under early validation error paths.

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

diff --git a/media/gpu/windows/d3d12_copy_command_list_wrapper.cc b/media/gpu/windows/d3d12_copy_command_list_wrapper.cc
index f412f055..09fde46 100644
--- a/media/gpu/windows/d3d12_copy_command_list_wrapper.cc
+++ b/media/gpu/windows/d3d12_copy_command_list_wrapper.cc
@@ -60,7 +60,9 @@
       command_list_(std::move(command_list)),
       fence_(base::MakeRefCounted<D3D12Fence>(fence)) {}
 
-D3D12CopyCommandQueueWrapper::~D3D12CopyCommandQueueWrapper() = default;
+D3D12CopyCommandQueueWrapper::~D3D12CopyCommandQueueWrapper() {
+  WaitSync();
+}
 
 bool D3D12CopyCommandQueueWrapper::CopyTextureRegion(
     const D3D12_TEXTURE_COPY_LOCATION& dst_location,
@@ -146,6 +148,14 @@
   return {fence_->Get(), std::move(value_or_error).value()};
 }
 
+void D3D12CopyCommandQueueWrapper::WaitSync() {
+  // If we have successfully queued any GPU work, wait on the CPU for it to
+  // complete.
+  if (fence_->Value() > 0) {
+    fence_->WaitCPU(fence_->Value());
+  }
+}
+
 bool D3D12CopyCommandQueueWrapper::Reset() {
   HRESULT hr;
   // Resetting command allocator is only valid after the GPU has finished
diff --git a/media/gpu/windows/d3d12_copy_command_list_wrapper.h b/media/gpu/windows/d3d12_copy_command_list_wrapper.h
index 78fe8228..e87bbf1 100644
--- a/media/gpu/windows/d3d12_copy_command_list_wrapper.h
+++ b/media/gpu/windows/d3d12_copy_command_list_wrapper.h
@@ -44,6 +44,7 @@
                                uint32_t uv_stride);
 
   D3D12FenceAndValue Execute();
+  void WaitSync();
 
  private:
   // This will be automatically called before CopyTextureRegion().
diff --git a/media/gpu/windows/d3d12_fence.cc b/media/gpu/windows/d3d12_fence.cc
index 7bd9acd..661072c3 100644
--- a/media/gpu/windows/d3d12_fence.cc
+++ b/media/gpu/windows/d3d12_fence.cc
@@ -44,11 +44,13 @@
 
 D3D11Status::Or<uint64_t> D3D12Fence::Signal(
     ID3D12CommandQueue& command_queue) {
-  HRESULT hr = command_queue.Signal(fence_.Get(), ++fence_value_);
+  uint64_t next_value = fence_value_ + 1;
+  HRESULT hr = command_queue.Signal(fence_.Get(), next_value);
   if (FAILED(hr)) {
     return D3D11Status{D3D11StatusCode::kFenceSignalFailed,
                        "ID3D12CommandQueue failed to signal fence", hr};
   }
+  fence_value_ = next_value;
   return fence_value_;
 }
 
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential GPU Use-After-Free in D3D12VideoEncodeAccelerator via unsynced COPY queue on early error

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: A potential GPU-side Use-After-Free (UAF) can occur in the Windows GPU process when a compromised renderer triggers an early validation failure in D3D12VideoEncodeDelegate. Under early error paths, the encoder returns immediately, skipping CPU synchronization for COPY-queue operations that are already in flight. Subsequent immediate teardown of the accelerator can free the underlying resources while the GPU is still actively accessing them.

Affected files:

  • media/gpu/windows/d3d12_video_encode_delegate.cc
  • media/gpu/windows/d3d12_video_encode_accelerator.cc

Estimated timestamp from git blame: 2026-02-05

Summary and Root Cause

A potential GPU-side Use-After-Free (UAF) exists in the D3D12VideoEncodeAccelerator within the Windows GPU process. When processing shared-memory backed video frames, D3D12VideoEncodeAccelerator::CreateResourceForSharedMemoryVideoFrame() copies renderer-supplied data into upload_buffer_ (UPLOAD heap) and records a copy operation (upload_buffer_ to input_texture_) on the COPY command queue:

if (!copy_command_queue_->CopyBufferToNV12Texture(
        input_texture_.Get(), upload_buffer_.Get(), 0, y_size.width(),
        uv_offset, uv_size.width())) { ... }

auto fence_and_value = copy_command_queue_->Execute(); // Executes commands on the GPU but does NOT wait on the CPU

This copy command list executes asynchronously on the GPU COPY engine. However, D3D12VideoEncodeDelegate::Encode() performs early validation of the reference buffer options before submitting any encoding work or performing CPU-to-GPU synchronization steps:

if (options.reference_buffers.size() > GetMaxNumOfManualRefBuffers()) {
  return {EncoderStatus::Codes::kBadReferenceBuffer, ...};
}
for (uint8_t ref_idx : options.reference_buffers) {
  if (ref_idx >= GetMaxNumOfManualRefBuffers()) {
    return {EncoderStatus::Codes::kBadReferenceBuffer, ...};
  }
}
if (options.update_buffer.has_value() &&
    options.update_buffer.value() >= GetMaxNumOfManualRefBuffers()) {
  return {EncoderStatus::Codes::kBadReferenceBuffer, ...};
}

If any of these validation checks fail (which a compromised renderer can trigger by sending out-of-range indices), Encode() returns early with a kBadReferenceBuffer status. Because of the early return, the execution flow completely bypasses the subsequent encoder pipeline, which would normally perform CPU fence wait and synchronization operations on the COPY queue’s fence.

If the renderer immediately closes the Mojo pipe upon receiving the error notification, D3D12VideoEncodeAccelerator::DestroyTask() is executed and deletes the accelerator. During destruction, the underlying upload_buffer_ and input_texture_ ComPtr structures are released, deallocating the memory heaps while the GPU copy engine is still actively reading from or writing to them, leading to a GPU-side UAF.

Potential Trigger Steps

Note: These are suggested steps to trigger the bug. Our tooling does not currently have the capability to run code or verify a proof-of-concept.

  1. Initialize D3D12VideoEncodeAccelerator with a supported profile (requires the kD3D12VideoEncodeAccelerator feature to be enabled).
  2. Call UseOutputBitstreamBuffer to register output buffers.
  3. Send an Encode() request containing a STORAGE_SHMEM VideoFrame and a malicious options.update_buffer value set to 255 (or any value >= GetMaxNumOfManualRefBuffers()).
  4. Immediately close the VideoEncodeAccelerator Mojo pipe.
  5. The accelerator enters destruction on the encoder task runner, releasing the underlying D3D12 resources while the GPU copy operation is still executing in the background.

Suggested Fix

To remediate this issue, perform early validation of the EncodeOptions before initiating the copy operation on the COPY command queue, or ensure that the COPY queue fence is explicitly synchronized and completed CPU-side before the accelerator or its associated D3D12 resource heaps are destroyed in ~D3D12VideoEncodeAccelerator() or D3D12CopyCommandQueueWrapper::~D3D12CopyCommandQueueWrapper().

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.

View on issue tracker