CVE-2026-19163
Overview
Files Changed
media/gpu/windows/media_foundation_video_encode_accelerator_win.ccmedia/gpu/windows/media_foundation_video_encode_accelerator_win.h
Patch
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_;
Original Bug Report
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.hmedia/gpu/command_buffer_helper.ccmedia/base/win/mf_helpers.ccmedia/base/win/mf_helpers.hmedia/gpu/windows/media_foundation_video_encode_accelerator_win.hmedia/gpu/windows/media_foundation_video_encode_accelerator_win.ccgpu/command_buffer/service/shared_image/shared_image_representation.hgpu/command_buffer/service/shared_image/shared_image_backing.ccgpu/command_buffer/service/memory_tracking.cc
Estimated timestamp from git blame: 2026-04-24
- 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.
- 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.VideoEncodeAcceleratorProviderto instantiate aMediaFoundationVideoEncodeAcceleratoron the GPU process’s COM STA thread, and allocate a D3D-backedSharedImageviagpu.mojom.SharedImageInterface. - Helper Initialization: A unique
CommandBufferHelperImplis created and stored inMediaFoundationVideoEncodeAccelerator::command_buffer_helper_(media/gpu/windows/media_foundation_video_encode_accelerator_win.h:382). - Submitting the Frame: The attacker sends a
media.mojom.VideoEncodeAccelerator::EncodeIPC containing theSharedImagemailbox.MediaFoundationVideoEncodeAccelerator::EncodeInternalpushes aPendingInputontopending_input_queue_and posts a task to resolve the shared image on the GPU thread. - Unsafe Pointer Exposure: On the GPU thread,
GenerateResourceOnSyncTokenReleasedcallscommand_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 inSharedImageRepresentation::tracker_. This field is explicitly declared asconst raw_ptr<MemoryTypeTracker, kUnprotectedInRelease> tracker_ = nullptr;(gpu/command_buffer/service/shared_image/shared_image_representation.h:175). In standard Release builds,kUnprotectedInReleasestatically 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 ascoped_refptrto theCommandBufferHelper(mf_helpers.h:273-278). The lock is assigned to thePendingInputstruct inpending_input_queue_. - Representation Promotion: The attacker sends
gpu.mojom.SharedImageInterface::DestroySharedImage(mailbox). This erases the primary factory reference on the GPU thread. Because theVideoImageRepresentationheld by theSharedImageReadLockis still alive,SharedImageBacking::ReleaseRefpromotes it to the new owner (refs_[0]) and successfully callsTrackMemAllocon its tracker (gpu/command_buffer/service/shared_image/shared_image_backing.cc:364). - Teardown Sequence Initiated: The attacker drops the
media.mojom.VideoEncodeAcceleratormojo pipe. This triggersMediaFoundationVideoEncodeAccelerator::Destroy()on the COM STA thread (media/gpu/windows/media_foundation_video_encode_accelerator_win.cc:1304).Destroy()issuesdelete thiswithout explicitly emptying the queues. - Reverse Destruction Order: C++ destroys members in reverse declaration order:
command_buffer_helper_(declared at line 382) is destroyed first. Its refcount drops to 0. Since it inheritsRefCountedDeleteOnSequencebound to the GPU main thread, it callsDeleteSoon(). This posts a Non-nestable task to execute~CommandBufferHelperImpl()on the GPU task runner (base/task/sequenced_task_runner.cc:133).encoder_(line 337) andpending_input_queue_(line 277) are destroyed next. The queuedPendingInputstructs andIMFSamples are destructed, dropping the reference count of theirComPtr<SharedImageReadLock> si_lockto 0.~SharedImageReadLock()executes and postsDestroySharedImageResourcesOnGpuThreadto the GPU main thread as a standard Nestable task (mf_helpers.cc:82-86).
- GPU Thread FIFO Execution: The Chromium
SequenceManageron 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:
- The Non-nestable task executes first.
~CommandBufferHelperImpl()runs, and the compiler destroys its inlinememory_type_tracker_member, deallocating the memory. - The Nestable task executes second.
DestroySharedImageResourcesOnGpuThreaddeletes theVideoImageRepresentation. - This calls
manager_->OnRepresentationDestroyed, which callsSharedImageBacking::ReleaseRef. - Since the representation is the owning reference (promoted earlier),
SharedImageBackingexecutes(*found)->tracker()->TrackMemFree(estimated_size_)(shared_image_backing.cc:356). - Execution jumps to
MemoryTypeTracker::TrackMemFree(gpu/command_buffer/service/memory_tracking.cc:103), dereferencing the freed pointer.
- The Non-nestable task executes first.
- 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 freedscoped_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.
- 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_EXCLUSIONexplicitly inshared_image_representation.h:172. Codebase investigation confirms that the actual code usesconst raw_ptr<MemoryTypeTracker, kUnprotectedInRelease> tracker_ = nullptr;atgpu/command_buffer/service/shared_image/shared_image_representation.h:175. In standard Release builds,kUnprotectedInReleaseresolves tointernal::RawPtrNoOpImpl, actively bypassing MiraclePtr (BackupRefPtr). The security implications are identical (unprotected). MediaFoundationVideoEncodeAccelerator::Destroy()does not manually drain or clearencoder_orpending_input_queue_, relying entirely on compiler-generated member destruction (delete this).CommandBufferHelperImpldeletion usesbase::RefCountedDeleteOnSequence::DestructOnSequence(), which invokesowning_task_runner_->DeleteSoon(...).DeleteSoongenerates a Non-nestable task.~SharedImageReadLock()usesPostTask(), generating a Nestable task.base::SequenceManagerguarantees FIFO ordering when a non-nestable task precedes a nestable task, providednesting_depth == 0(no nested loops active).gpu_task_runner_assigned to the MFVEA resolves tostub_->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
D3D12VideoEncodeAcceleratorimplementation (gated behindkD3D12SharedImageEncode).
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.