High watch CVSS 8.3 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
8.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Media
DescriptionUse after free in Media
ComponentMedia
Sub-componentMedia
Bug ClassUAF
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker536449742
Fix commit83b9159adc9d (chromium/src) +4/-3
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-07-19 (per vendor advisory)
Fix landed2026-07-21 (public git)
Disclosed2026-08-06
Patch gap16 days — fix public in source before the advisory

Background

`MediaFoundationVideoEncodeAccelerator`
Chrome’s Windows hardware video encoder, built on Media Foundation (media/gpu/windows). It runs in the GPU process and is driven over Mojo by clients such as WebRTC and WebCodecs.
`CommandBufferHelper`
A ref-counted (scoped_refptr) helper that gives the encoder access to GPU shared textures. According to the commit message, it also provides the MemoryTracker used by objects that hold those textures.
`pending_input_queue_`
The encoder’s queue of input frames waiting to be encoded. According to the commit, its entries need the helper’s MemoryTracker when they are destroyed.
C++ member destruction order
Non-static data members are destroyed in the reverse order of their declaration, after the destructor body has run.

Root Cause Analysis

In the old class layout, command_buffer_helper_ was declared near the end of the class (after luid_), and the commit says pending_input_queue_ depends on it. Since members are destroyed in reverse declaration order, the helper reference was released before pending_input_queue_ was torn down. If the encoder was destroyed with input frames still queued, those entries’ destructors used the helper’s MemoryTracker after the helper, and the tracker it owns, could already have been freed. That is a use-after-free in the GPU process.

The fix moves the command_buffer_helper_ declaration up, next to media_log_ and before bitstream_buffer_queue_, so the helper now outlives the queue during implicit member destruction. It also adds pending_input_queue_.clear() to the destructor body, so queued inputs are released explicitly while every member, including the helper, is still alive. The .h hunk does not show where pending_input_queue_ is declared; its relative position is taken from the commit message.

Key insight
The lifetime dependency “queued inputs need the CommandBufferHelper’s MemoryTracker” was not reflected in member declaration order, so implicit destruction freed the dependency first. The fix declares the helper earlier and clears the queue in the destructor.

Attack Path

  1. Use the Windows hardware encoder A client, such as a web page using WebCodecs VideoEncoder or WebRTC hardware encoding (inferred), causes the GPU process to create a MediaFoundationVideoEncodeAccelerator that uses command_buffer_helper_ for shared-texture input.
  2. Queue inputs Frames are submitted faster than the encoder consumes them, so entries pile up in pending_input_queue_.
  3. Destroy the encoder with a non-empty queue The client closes or resets the encoder while inputs are still pending, which runs ~MediaFoundationVideoEncodeAccelerator().
  4. Freed tracker used during teardown In the pre-patch layout, command_buffer_helper_ is released before pending_input_queue_. The queue entries’ destructors then use the helper’s MemoryTracker after it may have been freed, which is the UAF. Whether this was the last reference to the helper depends on other holders and is not shown in the diff (inferred).

Impact Assessment

The UAF is in the GPU process and affects Windows builds only, because the code is under media/gpu/windows and needs the Media Foundation hardware encode path. Corrupting GPU-process memory would be a step toward escaping the renderer sandbox, but that is not demonstrated (inferred). The precondition is destroying the encoder while shared-texture input frames are still queued. Ordinary web content can plausibly arrange this in a stock Windows build through the WebCodecs or WebRTC encode APIs when a hardware encoder is selected (inferred; the diff shows only the GPU-side lifetime bug). Whether command_buffer_helper_ holds the last reference at destruction affects reliability. The advisory states only “Use after free in Media”, with no in-the-wild status.

Files Changed

  • media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
  • media/gpu/windows/media_foundation_video_encode_accelerator_win.h

Audit Directions

  • Members with cross-member destruction dependencies
    In other media/gpu encoders and decoders (D3D11, VA-API, V4L2, Mac VideoToolbox), check whether queued frames, shared-image representations or scoped-access objects need a helper, tracker or device member that is declared after them, and so is destroyed before them.
  • Destructors relying on implicit teardown of queues
    Look for classes whose destructors leave pending input/output queues to implicit destruction. Clearing them explicitly in the destructor body makes the ordering visible and robust to future member reshuffling.
  • `MemoryTracker`/`CommandBufferHelper` consumers
    Audit objects that store a raw pointer or reference to a MemoryTracker obtained from a CommandBufferHelper without holding a reference to the helper, because their lifetime depends on someone else keeping the helper alive.
From 83b9159adc9de65630b00dc86d9722b68227dca7 Mon Sep 17 00:00:00 2001
From: Saifuddin Hitawala <[email protected]>
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 <[email protected]>
Commit-Queue: Saifuddin Hitawala <[email protected]>
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 [email protected]

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