Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Media
DescriptionUse after free in Media
ComponentMedia
Bug ClassUAF
Tracker499018355
Fix commit20d81a09abb1 (chromium/src) +20/-9
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
for
media/cdm/win/media_foundation_cdm.cc
modified

Files Changed

  • media/cdm/win/media_foundation_cdm.cc
From 20d81a09abb1313dbaeee0d9ee3bdac368db76a0 Mon Sep 17 00:00:00 2001
From: Sangbaek Park <sangbaekpark@google.com>
Date: Tue, 07 Apr 2026 10:13:32 -0700
Subject: [PATCH] media: Fix data race in CdmProxyImpl

CdmProxyImpl internal members (`input_trust_authorities_`,
`last_key_ids_`, and `trusted_input_`) are accessed and mutated
concurrently by the Chromium media task runner and the Windows Media
Foundation (MF) threadpool.

Because `absl::flat_hash_map` is not thread-safe, this lack of
synchronization leads to a data race that can cause map corruption,
double-frees of COM pointers, and potential Use-After-Free (UAF)
vulnerabilities.

This CL addresses the issue by:
1. Introducing a `base::Lock` to synchronize read and write access to
the mutable state in `CdmProxyImpl`.
2. Adding `base::AutoLock` in `GetInputTrustAuthority`, `SetLastKeyId`,
   `RefreshTrustedInput`, and `OnHardwareContextReset`.

Bug: 499018355
Change-Id: I4af960c4edcb37f52a2565c996bbe94f67d20821
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7729681
Commit-Queue: Sangbaek Park <sangbaekpark@chromium.org>
Reviewed-by: Xiaohan Wang <xhwang@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1610846}
---

diff --git a/media/cdm/win/media_foundation_cdm.cc b/media/cdm/win/media_foundation_cdm.cc
index 8424314c..6374f15 100644
--- a/media/cdm/win/media_foundation_cdm.cc
+++ b/media/cdm/win/media_foundation_cdm.cc
@@ -18,6 +18,7 @@
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
 #include "base/win/scoped_co_mem.h"
 #include "base/win/scoped_propvariant.h"
 #include "base/win/win_util.h"
@@ -166,6 +167,7 @@
                                  REFIID riid,
                                  IUnknown** object_out) override {
     DVLOG_FUNC(1);
+    base::AutoLock lock(lock_);
 
     if (input_trust_authorities_.count(stream_id)) {
       RETURN_IF_FAILED(input_trust_authorities_[stream_id].CopyTo(object_out));
@@ -195,12 +197,14 @@
 
   HRESULT SetLastKeyId(uint32_t stream_id, REFGUID key_id) override {
     DVLOG_FUNC(1);
+    base::AutoLock lock(lock_);
     last_key_ids_[stream_id] = key_id;
     return S_OK;
   }
 
   HRESULT RefreshTrustedInput() override {
     DVLOG_FUNC(1);
+    base::AutoLock lock(lock_);
 
     // Refresh all decryptors of the last key IDs.
     for (const auto& entry : input_trust_authorities_) {
@@ -234,12 +238,15 @@
   }
 
   void OnHardwareContextReset() override {
-    // Hardware context reset happens, all the crypto sessions are in invalid
-    // states. So drop everything here.
-    // TODO(xhwang): Keep the `last_key_ids_` here for faster resume.
-    trusted_input_.Reset();
-    input_trust_authorities_.clear();
-    last_key_ids_.clear();
+    {
+      base::AutoLock lock(lock_);
+      // Hardware context reset happens, all the crypto sessions are in invalid
+      // states. So drop everything here.
+      // TODO(xhwang): Keep the `last_key_ids_` here for faster resume.
+      trusted_input_.Reset();
+      input_trust_authorities_.clear();
+      last_key_ids_.clear();
+    }
 
     // `CdmEvent::kHardwareContextReset` will be reported in
     // `hardware_context_reset_cb_` below.
@@ -283,18 +290,22 @@
   base::RepeatingClosure hardware_context_reset_cb_;
   MediaFoundationCdm::CdmEventCB cdm_event_cb_;
 
+  // Lock to protect the mutable members. The data can be accessed from Chromium
+  // thread and MF thread pool threads.
+  base::Lock lock_;
+
   // Store IMFTrustedInput to avoid potential performance cost.
-  ComPtr<IMFTrustedInput> trusted_input_;
+  ComPtr<IMFTrustedInput> trusted_input_ GUARDED_BY(lock_);
 
   // |stream_id| to IMFInputTrustAuthority (ITA) mapping. Serves two purposes:
   // 1. The same ITA should always be returned in GetInputTrustAuthority() for
   // the same |stream_id|.
   // 2. The ITA must keep alive for decryptors to work.
   absl::flat_hash_map<uint32_t, ComPtr<IMFInputTrustAuthority>>
-      input_trust_authorities_;
+      input_trust_authorities_ GUARDED_BY(lock_);
 
   // |stream_id| to last used key ID mapping.
-  absl::flat_hash_map<uint32_t, GUID> last_key_ids_;
+  absl::flat_hash_map<uint32_t, GUID> last_key_ids_ GUARDED_BY(lock_);
 };
 
 }  // namespace
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential Data Race in CdmProxyImpl leading to UAF in MediaFoundationService

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 without the security team.

Overview: CdmProxyImpl instances are shared across multiple MediaFoundationRenderers that use the same Content Decryption Module (CDM). Its internal absl::flat_hash_map members are mutated concurrently by the Chromium task runner and the Windows Media Foundation threadpool without synchronization. This lack of thread safety can lead to map corruption, a double-free of COM pointers, and potential RCE in the MediaFoundationService utility process.

Affected files:

  • media/cdm/win/media_foundation_cdm.cc
  • media/cdm/win/media_foundation_cdm.h

Estimated timestamp from git blame: 2021-06-01

Summary

CdmProxyImpl (defined in media/cdm/win/media_foundation_cdm.cc) is a RefCountedThreadSafe class that implements MediaFoundationCdmProxy. A single instance of CdmProxyImpl is shared across all MediaFoundationRenderer instances that are bound to the same MediaFoundationCdm (i.e., when multiple video elements share the same MediaKeys object).

CdmProxyImpl maintains two absl::flat_hash_map members:

  • input_trust_authorities_: Maps a stream ID to its ComPtr<IMFInputTrustAuthority>.
  • last_key_ids_: Maps a stream ID to its last used Key ID.

These maps are accessed and mutated concurrently from two different execution contexts without any synchronization primitives (such as a base::Lock), leading to a critical data race.

Technical Details

The vulnerability is triggered by concurrent access from the following threads:

  1. Windows Media Foundation (MF) Threadpool:

    • MediaFoundationSourceWrapper::GetInputTrustAuthority is called by the OS on an MF threadpool thread. This directly calls CdmProxyImpl::GetInputTrustAuthority, which inserts the created IMFInputTrustAuthority into input_trust_authorities_.
    • MediaFoundationSourceWrapper::~MediaFoundationSourceWrapper is a COM object destructor that runs on the thread releasing the final reference, which is often an MF threadpool thread when the pipeline tears down. This destructor calls CdmProxyImpl::SetLastKeyId, modifying last_key_ids_.
  2. Chromium Media Task Runner:

    • When a MediaFoundationRenderer is initialized, it calls MediaFoundationSourceWrapper::SetCdmProxy() on the media task runner.
    • This invokes CdmProxyImpl::RefreshTrustedInput(), which iterates over input_trust_authorities_ and then explicitly calls clear() on both input_trust_authorities_ and last_key_ids_.

Because absl::flat_hash_map is not thread-safe, a concurrent insertion (from the MF threadpool) and clear() (from the task runner) causes undefined behavior. It can tear the internal control bytes of the map, tricking the map’s slot destruction logic into destructing a ComPtr that is simultaneously being written or has already been freed. This results in a Use-After-Free (UAF) and a double Release() call on the COM object.

Potential Exploit Scenario

Note: These are potential steps to trigger the bug. We don’t yet have a working proof of concept that has been successfully run.

  1. An attacker creates a malicious web page that instantiates an Encrypted Media Extensions (EME) session, creating a MediaKeys object.
  2. The attacker creates two HTML <video> elements (Video 1 and Video 2) and attaches the exact same MediaKeys object to both via setMediaKeys().
  3. The attacker assigns an encrypted source to Video 1 and starts playback. The browser creates a MediaFoundationRenderer in the MediaFoundationService utility process.
  4. The MediaFoundationRenderer initializes and creates a shared CdmProxyImpl instance.
  5. Video 1’s Media Foundation pipeline starts up on the Windows MF threadpool. It requests the Input Trust Authority, causing the MF thread to insert an item into the input_trust_authorities_ map.
  6. Concurrently, the attacker assigns a source to Video 2 to start its playback.
  7. Video 2’s MediaFoundationRenderer initializes on the Chromium media task runner, acquiring the same CdmProxyImpl instance.
  8. Video 2’s initialization calls RefreshTrustedInput(), which begins iterating and calling clear() on the map.
  9. The concurrent mutation and clearing corrupts the hash map. The map destroys a ComPtr leading to a double Release().
  10. Since Release() performs a virtual call (vtable->Release()), an attacker who has groomed the heap can hijack the control flow, potentially achieving Remote Code Execution (RCE) within the MediaFoundationService process.

Recommendation

Synchronize access to the internal maps in CdmProxyImpl.

  • The most straightforward fix is to add a base::Lock to CdmProxyImpl and acquire it in GetInputTrustAuthority, SetLastKeyId, RefreshTrustedInput, and OnHardwareContextReset.
  • Alternatively, restrict all map modifications to a single sequence (e.g., the Chromium media task runner) by using base::BindPostTask to bounce calls from the MF threadpool to the task runner, though this may introduce latency into the COM calls.

Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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