69bf1c67cb894365d151bd020bb0171fd583633a [M150] [media] Keep ref on MemoryTypeTracker in SharedImageReadLock
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
- Reachability — Hardware video encode path creates a SharedImageReadLock and hands it to an IMFSample (inferred).
- Trigger — The externally-owned MemoryTypeTracker or context is destroyed before the IMFSample releases the lock.
- Primitive — The lock’s destructor frees its representation through a now-dangling tracker/context, a use-after-free in the GPU process (inferred).
- 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
- Reach encode path — Attacker-controlled page submits frames to the hardware encoder to allocate SharedImageReadLocks (inferred).
- Reorder destruction — Induce the tracker/context to be freed while an IMFSample still holds the lock (inferred).
- Groom freed slot — Reallocate the freed MemoryTypeTracker/representation memory with controlled data (inferred).
- 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.