← Chrome — 2026-W31

69bf1c67cb894365d151bd020bb0171fd583633a  [M150] [media] Keep ref on MemoryTypeTracker in SharedImageReadLock

severity medium class UAF CVSS 7.3 signal score 7.97 media/ patch-now AI review CONFIRMED
Structural signals: smartptr_member_added (+6.47), area:media/ (+1.5)

The bug at a glance

The commit explicitly adds a MemoryTypeTracker ref to SharedImageReadLock to avoid a use-after-free caused by unexpected destruction ordering when the lock is handed to an IMFSample, a GPU-process lifetime bug on the Windows media encode path.

An attacker driving the hardware video encode path (e.g. via WebCodecs/media capture) could influence object destruction ordering so that the MemoryTypeTracker (or GPU image representation) is freed while the IMFSample-held lock still references it, yielding a GPU-process UAF.

Root cause

SharedImageReadLock previously produced its VideoImageRepresentation with command_buffer_helper->GetMemoryTypeTracker(), holding no ownership of the tracker, and destroyed representation_/scoped_read_access_ via OnTaskRunnerDeleter. Because the lock can outlive its creator by being passed to an IMFSample, the externally-owned MemoryTypeTracker (and the context state needed to tear GPU resources down) could be destroyed before the lock, so the lock’s representation freed through a dangling tracker/context. The fix makes the lock own a dedicated MemoryTypeTracker and a SharedContextState ref, and routes destruction through a single DestroySharedImageResourcesOnGpuThread task that makes the context current, drops read access, representation, then tracker in a well-defined order.

Key code

Lock now owns its MemoryTypeTracker + context state and tears them down in a defined order on the GPU thread

+  auto tracker = std::make_unique<gpu::MemoryTypeTracker>(
+      base::WrapRefCounted(shared_image_stub->memory_tracker()));
...
+      context_state_(std::move(context_state)),
+      tracker_(std::move(tracker)),
...
+SharedImageReadLock::~SharedImageReadLock() {
+  task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&DestroySharedImageResourcesOnGpuThread, ...));

Patch walkthrough

  • media/base/win/mf_helpers.cc — Adds a per-lock gpu::MemoryTypeTracker and SharedContextState owned by SharedImageReadLock, and a DestroySharedImageResourcesOnGpuThread helper posted from the destructor so GPU resources are released in a fixed order while the context is current, removing the dependency on externally-owned tracker lifetime that caused the UAF.
  • media/base/win/mf_helpers.h — Extends the constructor signature and adds context_state_/tracker_/task_runner_ members, replacing the OnTaskRunnerDeleter-wrapped unique_ptrs so the lock itself owns everything needed to safely free the representation.
  • media/gpu/windows/d3d12_video_encode_accelerator.cc — Updates the D3D12 encode accelerator call site to construct the tracker from shared_image_stub->memory_tracker() and pass both the tracker and the shared_context_state into the lock, so the encode path adopts the owned-lifetime model.

Background

SharedImageReadLock — RefCounted COM-style object that keeps a GPU shared-image read access alive while a frame is consumed by Media Foundation.

IMFSample — Windows Media Foundation sample object that can retain Chromium-provided buffers/locks beyond the caller’s scope.

MemoryTypeTracker — gpu bookkeeping object (not inherently thread-safe/ref-counted here) used when producing a video image representation.

VideoImageRepresentation / ScopedReadAccess — GPU shared-image abstractions whose teardown requires a current GL/graphics context.

Vulnerability window

  1. Reachability — Hardware video encode path creates a SharedImageReadLock and hands it to an IMFSample (inferred).
  2. Trigger — The externally-owned MemoryTypeTracker or context is destroyed before the IMFSample releases the lock.
  3. Primitive — The lock’s destructor frees its representation through a now-dangling tracker/context, a use-after-free in the GPU process (inferred).
  4. Escalation — GPU-process memory corruption could be shaped toward control-flow hijack within the GPU sandbox (inferred).

Proof of concept

Conceptually, exercise the Windows D3D11/D3D12 hardware encode path (e.g. via WebCodecs VideoEncoder or captured media), keep the produced sample alive while forcing teardown of the owning command buffer / context, and observe the lock being destroyed after its tracker; no exploit code is provided.

Exploitation

  1. Reach encode path — Attacker-controlled page submits frames to the hardware encoder to allocate SharedImageReadLocks (inferred).
  2. Reorder destruction — Induce the tracker/context to be freed while an IMFSample still holds the lock (inferred).
  3. Groom freed slot — Reallocate the freed MemoryTypeTracker/representation memory with controlled data (inferred).
  4. Corrupt on free — Lock destructor dereferences the dangling object, enabling controlled GPU-process corruption (inferred).

Detection & hunting

For defenders and detection engineers:

  • weggli/grep for representations produced with command_buffer_helper->GetMemoryTypeTracker() or …GetSharedImageManager()->ProduceVideo(… tracker) where the tracker is not owned by the holder — Flag any object that outlives its creator (passed to IMFSample, posted tasks, callbacks) yet references a tracker/context it does not own.
  • grep for OnTaskRunnerDeleter on GPU representation/scoped-read-access members — Review whether the deleter’s task runner and the lifetime of the tracker/context are guaranteed to still be valid at destruction time.

Audit directions

  • media/base/win and media/gpu/windows shared-image/IMFSample holders — Audit every object handed to Media Foundation that retains GPU shared-image state for whether it owns (not borrows) the tracker and context needed to free that state.
  • Other platforms’ SharedImageReadLock equivalents — Check Android/Linux/mac video paths for the same borrow-not-own lifetime assumption on MemoryTypeTracker.

Before / after

Loading diff…