Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Media
DescriptionUse after free in Media
ComponentMedia
Bug ClassUAF
Tracker536449742
Fix commit83b9159adc9d (chromium/src) +4/-3
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-06

Files Changed

  • media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
  • media/gpu/windows/media_foundation_video_encode_accelerator_win.h
From 83b9159adc9de65630b00dc86d9722b68227dca7 Mon Sep 17 00:00:00 2001
From: Saifuddin Hitawala <hitawala@chromium.org>
Date: Tue, 21 Jul 2026 13:24:29 -0700
Subject: [PATCH] [media] Reorder CommandBufferHelper member in MediaFoundationVEAWin

Reorder CommandBufferHelper member declaration in
MediaFoundationVideoEncodeAcceleratorWin so that it gets destroyed
after pending_input_queue_ which needs helper's MemoryTracker on
destruction. Also clear the queue proactively on destruction.

Bug: 536449742
Change-Id: Ieb79852aefc62ef262410bdfcb6af6e480b72e97
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8130565
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Saifuddin Hitawala <hitawala@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1665731}
---

diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
index 719978306..af335d2 100644
--- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
+++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
@@ -360,6 +360,7 @@
     ~MediaFoundationVideoEncodeAccelerator() {
   DVLOG(3) << __func__;
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  pending_input_queue_.clear();
 }
 
 void MediaFoundationVideoEncodeAccelerator::InitializeForTesting(
diff --git a/media/gpu/windows/media_foundation_video_encode_accelerator_win.h b/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
index 21dc319..09da848 100644
--- a/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
+++ b/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
@@ -269,6 +269,9 @@
 
   std::unique_ptr<MediaLog> media_log_;
 
+  // Helper for accessing shared textures
+  scoped_refptr<CommandBufferHelper> command_buffer_helper_;
+
   // Bitstream buffers ready to be used to return encoded output as a FIFO.
   base::circular_deque<std::unique_ptr<BitstreamBufferRef>>
       bitstream_buffer_queue_;
@@ -378,9 +381,6 @@
   // Preferred adapter for DXGIDeviceManager.
   const CHROME_LUID luid_;
 
-  // Helper for accessing shared textures
-  scoped_refptr<CommandBufferHelper> command_buffer_helper_;
-
   // Used for frame format conversion.
   VideoFrameConverter frame_converter_;
 
Loading diff…

Original Bug Report

reported by aw...@chromium.org

Potential UAF in GPU process via dangling MemoryTypeTracker in SharedImageRepresentation

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 Use-After-Free (UAF) vulnerability exists in the Chrome GPU process on Windows due to a lifetime mismatch in MediaFoundationVideoEncodeAccelerator. Reverse member destruction order posts a deletion task for CommandBufferHelperImpl before the cleanup task for VideoImageRepresentation, leading to an exploitable UAF write and virtual call when the representation attempts to update memory tracking on a freed MemoryTypeTracker. The pointer is marked kUnprotectedInRelease, meaning it bypasses MiraclePtr protections in Release builds.

Affected files:

  • media/gpu/command_buffer_helper.h
  • media/gpu/command_buffer_helper.cc
  • media/base/win/mf_helpers.cc
  • media/base/win/mf_helpers.h
  • media/gpu/windows/media_foundation_video_encode_accelerator_win.h
  • media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
  • gpu/command_buffer/service/shared_image/shared_image_representation.h
  • gpu/command_buffer/service/shared_image/shared_image_backing.cc
  • gpu/command_buffer/service/memory_tracking.cc

Estimated timestamp from git blame: 2026-04-24

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

A high-severity potential Use-After-Free (UAF) vulnerability exists in the Google Chrome GPU process on Windows. The issue occurs due to a lifetime mismatch between a gpu::VideoImageRepresentation object and the MemoryTypeTracker it points to.

During the teardown of the MediaFoundationVideoEncodeAccelerator (MFVEA), C++ member destruction order dictates that command_buffer_helper_ is destroyed before pending_input_queue_ and encoder_. Because command_buffer_helper_ uses base::RefCountedDeleteOnSequence, its destruction posts a non-nestable task to the GPU main thread to delete the CommandBufferHelperImpl (which owns the MemoryTypeTracker). Subsequently, the queue/encoder destruction releases the SharedImageReadLock, which posts a nestable task to the exact same GPU main thread to destroy the VideoImageRepresentation.

The Chromium SequenceManager guarantees strict FIFO execution order for tasks when no nested message loop is running, causing the CommandBufferHelperImpl to be freed first. When the VideoImageRepresentation is finally destroyed, it dereferences its dangling tracker_ pointer to adjust memory allocation accounting. This results in an exploitable heap-write Use-After-Free and an indirect virtual function call via a freed scoped_refptr<MemoryTracker> inside the sandboxed Windows GPU process, reachable by a compromised renderer process. The tracker_ pointer is marked with the kUnprotectedInRelease trait, which actively disables MiraclePtr (BackupRefPtr) protections.

  1. Proof-of-Concept & Detailed Execution Flow

Note: Our tooling agent does not have the ability to run code, but the following step-by-step trace demonstrates how an attacker controlling a compromised renderer could reliably trigger this vulnerability based on source code analysis.

  • Attacker Setup: The attacker compromises a renderer process (A-RENDERER). They use media.mojom.VideoEncodeAcceleratorProvider to instantiate a MediaFoundationVideoEncodeAccelerator on the GPU process’s COM STA thread, and allocate a D3D-backed SharedImage via gpu.mojom.SharedImageInterface.
  • Helper Initialization: A unique CommandBufferHelperImpl is created and stored in MediaFoundationVideoEncodeAccelerator::command_buffer_helper_ (media/gpu/windows/media_foundation_video_encode_accelerator_win.h:382).
  • Submitting the Frame: The attacker sends a media.mojom.VideoEncodeAccelerator::Encode IPC containing the SharedImage mailbox. MediaFoundationVideoEncodeAccelerator::EncodeInternal pushes a PendingInput onto pending_input_queue_ and posts a task to resolve the shared image on the GPU thread.
  • Unsafe Pointer Exposure: On the GPU thread, GenerateResourceOnSyncTokenReleased calls command_buffer_helper->GetMemoryTypeTracker() (media/base/win/mf_helpers.cc:1140). This returns the address of its inline member &memory_type_tracker_ (media/gpu/command_buffer_helper.cc:82-84). The pointer has no lifetime contract.
  • Bypassing MiraclePtr: The tracker pointer is passed to SharedImageManager::ProduceVideo() and stored in SharedImageRepresentation::tracker_. This field is explicitly declared as const raw_ptr<MemoryTypeTracker, kUnprotectedInRelease> tracker_ = nullptr; (gpu/command_buffer/service/shared_image/shared_image_representation.h:175). In standard Release builds, kUnprotectedInRelease statically disables MiraclePtr instrumentation for performance reasons (base/allocator/partition_allocator/src/partition_alloc/pointers/raw_ptr.h:1254-1255).
  • Read Lock Creation: The representation is wrapped in a SharedImageReadLock (mf_helpers.cc:1153), which does not acquire a scoped_refptr to the CommandBufferHelper (mf_helpers.h:273-278). The lock is assigned to the PendingInput struct in pending_input_queue_.
  • Representation Promotion: The attacker sends gpu.mojom.SharedImageInterface::DestroySharedImage(mailbox). This erases the primary factory reference on the GPU thread. Because the VideoImageRepresentation held by the SharedImageReadLock is still alive, SharedImageBacking::ReleaseRef promotes it to the new owner (refs_[0]) and successfully calls TrackMemAlloc on its tracker (gpu/command_buffer/service/shared_image/shared_image_backing.cc:364).
  • Teardown Sequence Initiated: The attacker drops the media.mojom.VideoEncodeAccelerator mojo pipe. This triggers MediaFoundationVideoEncodeAccelerator::Destroy() on the COM STA thread (media/gpu/windows/media_foundation_video_encode_accelerator_win.cc:1304). Destroy() issues delete this without explicitly emptying the queues.
  • Reverse Destruction Order: C++ destroys members in reverse declaration order:
    1. command_buffer_helper_ (declared at line 382) is destroyed first. Its refcount drops to 0. Since it inherits RefCountedDeleteOnSequence bound to the GPU main thread, it calls DeleteSoon(). This posts a Non-nestable task to execute ~CommandBufferHelperImpl() on the GPU task runner (base/task/sequenced_task_runner.cc:133).
    2. encoder_ (line 337) and pending_input_queue_ (line 277) are destroyed next. The queued PendingInput structs and IMFSamples are destructed, dropping the reference count of their ComPtr<SharedImageReadLock> si_lock to 0. ~SharedImageReadLock() executes and posts DestroySharedImageResourcesOnGpuThread to the GPU main thread as a standard Nestable task (mf_helpers.cc:82-86).
  • GPU Thread FIFO Execution: The Chromium SequenceManager on the GPU main thread processes tasks. Because no nested message loop is running (nesting_depth == 0), the SequenceManager guarantees strict FIFO execution based on insertion order, completely bypassing the non-nestable deferral checks (base/task/sequence_manager/sequence_manager_impl.cc:518-530).
  • UAF Execution:
    1. The Non-nestable task executes first. ~CommandBufferHelperImpl() runs, and the compiler destroys its inline memory_type_tracker_ member, deallocating the memory.
    2. The Nestable task executes second. DestroySharedImageResourcesOnGpuThread deletes the VideoImageRepresentation.
    3. This calls manager_->OnRepresentationDestroyed, which calls SharedImageBacking::ReleaseRef.
    4. Since the representation is the owning reference (promoted earlier), SharedImageBacking executes (*found)->tracker()->TrackMemFree(estimated_size_) (shared_image_backing.cc:356).
    5. Execution jumps to MemoryTypeTracker::TrackMemFree (gpu/command_buffer/service/memory_tracking.cc:103), dereferencing the freed pointer.
  • The Exploit Sink: Inside TrackMemFree, the code attempts an OS lock operation on freed heap memory (base::AutoLock auto_lock(lock_)), overwrites freed heap memory (mem_represented_ -= bytes;), and finally performs an indirect virtual call through a freed scoped_refptr (memory_tracker_->TrackMemoryAllocatedChange(-static_cast<int64_t>(bytes))). By grooming the GPU heap, an attacker can control the vtable pointer and achieve arbitrary code execution.

Suggested Fix: Update SharedImageReadLock to hold a scoped_refptr<media::CommandBufferHelper> alongside the VideoImageRepresentation. This ensures the CommandBufferHelperImpl (and its inline MemoryTypeTracker) strictly outlives the VideoImageRepresentation, preventing the UAF regardless of task-posting or member-destruction order.

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

> Critic Verdict (2026-07-18 05:47:11): > * Severity: High (S1) > * Brief Notes / Reasoning: > The vulnerability is a highly credible, exploitable Use-After-Free (UAF) in the GPU process, reachable by a compromised renderer. >
> Root Cause: gpu::VideoImageRepresentation holds a RAW_PTR_EXCLUSION MemoryTypeTracker* const tracker_. During MediaFoundationVideoEncodeAccelerator::Destroy(), C++ member destruction order dictates that command_buffer_helper_ (which owns the MemoryTypeTracker) is destroyed before pending_input_queue_ (which holds SharedImageReadLock and ultimately the VideoImageRepresentation). >
> Exploitability: Because both destructors post cleanup tasks to the GPU main thread, the strict FIFO ordering ensures ~CommandBufferHelperImpl runs first, freeing the MemoryTypeTracker. The ~SharedImageReadLock cleanup then executes, calling TrackMemFree on the freed tracker. This results in an exploitable heap write (mem_represented_ -= bytes) and a wild virtual call (memory_tracker_->TrackMemoryAllocatedChange) through a freed scoped_refptr. The attacker completely controls the timing via mojo IPC. >
> Severity Justification: > - Primitive: Write-UAF + Virtual Call in the GPU process. > - Attacker Model: Reachable by A-RENDERER (compromised renderer forging mojo messages). > - MiraclePtr: The sink tracker_ is explicitly marked RAW_PTR_EXCLUSION, meaning it is not MiraclePtr-protected. > - Sandbox: The GPU process is sandboxed on Windows. A renderer-to-GPU memory corruption is a sandbox escape, qualifying for High (S1) severity. (If this occurred on an unsandboxed GPU platform like Android, it would be Critical/S0, but this code is Windows-specific). > - Gating/Modifiers: The feature kMediaFoundationD3DVideoProcessing is DISABLED_BY_DEFAULT but has an active fieldtrial_testing_config.json arm. Per the severity guidelines, features with field trials are assessed at full severity as-if-enabled. >
> Therefore, the S1 (High) rating is accurate.

Additional Validator Notes:

  • The previous report mentioned RAW_PTR_EXCLUSION explicitly in shared_image_representation.h:172. Codebase investigation confirms that the actual code uses const raw_ptr<MemoryTypeTracker, kUnprotectedInRelease> tracker_ = nullptr; at gpu/command_buffer/service/shared_image/shared_image_representation.h:175. In standard Release builds, kUnprotectedInRelease resolves to internal::RawPtrNoOpImpl, actively bypassing MiraclePtr (BackupRefPtr). The security implications are identical (unprotected).
  • MediaFoundationVideoEncodeAccelerator::Destroy() does not manually drain or clear encoder_ or pending_input_queue_, relying entirely on compiler-generated member destruction (delete this).
  • CommandBufferHelperImpl deletion uses base::RefCountedDeleteOnSequence::DestructOnSequence(), which invokes owning_task_runner_->DeleteSoon(...). DeleteSoon generates a Non-nestable task. ~SharedImageReadLock() uses PostTask(), generating a Nestable task. base::SequenceManager guarantees FIFO ordering when a non-nestable task precedes a nestable task, provided nesting_depth == 0 (no nested loops active).
  • gpu_task_runner_ assigned to the MFVEA resolves to stub_->channel()->task_runner(), establishing that both teardown tasks post to the identical GPU main thread task runner instance.
  • The vulnerability also identically applies to the D3D12VideoEncodeAccelerator implementation (gated behind kD3D12SharedImageEncode).

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