Overview

Critical
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in UI
DescriptionUse after free in UI
ComponentUI
Bug ClassUAF
Tracker495108488
Fix commit411ebd37c1e0 (chromium/src) +13/-7
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-12

Changed Functions

FunctionChangeNotes
if
components/system_media_controls/win/system_media_controls_win.cc
modified

Files Changed

  • components/system_media_controls/win/system_media_controls_win.cc
From 411ebd37c1e01416a7db2dd2adfa5d7aad888a44 Mon Sep 17 00:00:00 2001
From: Tommy Steimel <steimel@chromium.org>
Date: Tue, 24 Mar 2026 11:57:15 -0700
Subject: [PATCH] [SMTC] Use weak ptr in SystemMediaControlsWin callback

We currently capture a raw `this` pointer in a SystemMediaControlsWin
callback for writing image data. This could be problematic as it's
possible the SystemMediaControlsWin doesn't outlive the operation.
This CL updates the callback to capture and use a weak pointer instead.

Bug: 495108488
Change-Id: I8698af5cf5d828ee5064793d35a6c33b6072ce53
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7694234
Commit-Queue: Tommy Steimel <steimel@chromium.org>
Reviewed-by: Frank Liberato <liberato@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1604285}
---

diff --git a/components/system_media_controls/win/system_media_controls_win.cc b/components/system_media_controls/win/system_media_controls_win.cc
index 052f0397..0bbfd11a 100644
--- a/components/system_media_controls/win/system_media_controls_win.cc
+++ b/components/system_media_controls/win/system_media_controls_win.cc
@@ -308,10 +308,16 @@
 
   // Make a callback that gives the icon to the SMTC once the bits make it into
   // |icon_stream_|
+  auto weak_ptr = weak_factory_.GetWeakPtr();
   auto store_async_callback = Microsoft::WRL::Callback<
       ABI::Windows::Foundation::IAsyncOperationCompletedHandler<unsigned int>>(
-      [this](ABI::Windows::Foundation::IAsyncOperation<unsigned int>* async_op,
-             ABI::Windows::Foundation::AsyncStatus status) mutable {
+      [weak_ptr](
+          ABI::Windows::Foundation::IAsyncOperation<unsigned int>* async_op,
+          ABI::Windows::Foundation::AsyncStatus status) mutable {
+        if (!weak_ptr) {
+          return S_OK;
+        }
+
         // Check the async operation completed successfully.
         ABI::Windows::Foundation::IAsyncInfo* async_info;
         HRESULT hr = async_op->QueryInterface(
@@ -328,15 +334,15 @@
               &reference_statics);
           DCHECK(SUCCEEDED(result));
 
-          result = reference_statics->CreateFromStream(icon_stream_.Get(),
-                                                       &icon_stream_reference_);
+          result = reference_statics->CreateFromStream(
+              weak_ptr->icon_stream_.Get(), &weak_ptr->icon_stream_reference_);
           DCHECK(SUCCEEDED(result));
 
-          result =
-              display_updater_->put_Thumbnail(icon_stream_reference_.Get());
+          result = weak_ptr->display_updater_->put_Thumbnail(
+              weak_ptr->icon_stream_reference_.Get());
           DCHECK(SUCCEEDED(result));
 
-          result = display_updater_->Update();
+          result = weak_ptr->display_updater_->Update();
           DCHECK(SUCCEEDED(result));
         }
         return hr;
Loading diff…

Original Bug Report

reported by er...@chromium.org

Potential Use-After-Free in SystemMediaControlsWin::SetThumbnail via raw 'this' capture

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: A potential Use-After-Free (UAF) vulnerability exists in the browser process due to a raw this pointer capture in a WinRT asynchronous callback within SystemMediaControlsWin::SetThumbnail. If the object is destroyed while a media artwork update is pending, the callback will execute on a WinRT thread-pool thread and dereference the freed memory, potentially leading to arbitrary code execution.

Affected files:

  • components/system_media_controls/win/system_media_controls_win.cc

Estimated timestamp from git blame: 2021-09-08

Description

A potential Use-After-Free (UAF) vulnerability has been identified in the browser process within SystemMediaControlsWin::SetThumbnail on Windows. The issue stems from capturing a raw C++ this pointer in a Microsoft::WRL::Callback that handles the completion of an asynchronous WinRT operation.

In components/system_media_controls/win/system_media_controls_win.cc, the SetThumbnail method initiates an asynchronous write to an icon stream using icon_data_writer_->StoreAsync. A completion handler is registered via store_async_operation->put_Completed():

// components/system_media_controls/win/system_media_controls_win.cc:311
auto store_async_callback = Microsoft::WRL::Callback<
    ABI::Windows::Foundation::IAsyncOperationCompletedHandler<unsigned int>>(
    [this](ABI::Windows::Foundation::IAsyncOperation<unsigned int>* async_op,
           ABI::Windows::Foundation::AsyncStatus status) mutable {
      // ... 
      result = display_updater_->put_Thumbnail(icon_stream_reference_.Get());
      // ...
    });

The lambda captures this as a raw pointer. However, the SystemMediaControlsWin destructor does not cancel or synchronize with this pending asynchronous operation.

If the SystemMediaControlsWin object is destroyed while the write is still in flight, the WinRT runtime will eventually execute the callback, dereferencing the freed this pointer to access member variables like display_updater_ (which is a ComPtr). Because the capture is a raw pointer and no base::raw_ptr references keep the memory in MiraclePtr’s quarantine, the memory is fully freed and can be reclaimed by an attacker.

Reachability and Impact

This code path is reachable via the Media Session API. When a Progressive Web App (PWA) or an app popup window plays media, Chrome creates an instanced SystemMediaControlsWin object for that specific WebContents, managed by WebAppSystemMediaControlsManager.

If an attacker’s PWA sets media artwork (triggering SetThumbnail) and then immediately closes its own window (e.g., via window.close()), the underlying WebContents and its MediaSession are destroyed. This causes WebAppSystemMediaControlsManager to delete the SystemMediaControlsWin object while the WinRT async operation is still pending.

When the callback eventually runs, it attempts to make a virtual method call on the display_updater_ member of the freed object (display_updater_->put_Thumbnail(...)). By using standard heap spraying techniques in the browser process to control the freed memory chunk, an attacker could point display_updater_ to a forged vtable, achieving arbitrary Remote Code Execution (RCE) and a full Sandbox Escape.

Suggested Steps to Reproduce

Note: These are potential steps as we have not yet developed a working exploit.

  1. An attacker convinces a user to install a malicious website as a PWA, or opens an app popup window.
  2. The user launches the PWA, fulfilling Browser::ShouldUseInstancedSystemMediaControls().
  3. The PWA’s JavaScript starts playing an audio track (e.g., <audio autoplay src="...">), creating a MediaSession and requesting audio focus.
  4. The browser creates a WebAppSystemMediaControls instance, which allocates a SystemMediaControlsWin object.
  5. The JavaScript updates the Media Session metadata with a large artwork image to delay the WinRT processing: navigator.mediaSession.metadata = new MediaMetadata({ artwork: [{ src: "large.png" }] });
  6. SystemMediaControlsWin::SetThumbnail is called, initiating the asynchronous StoreAsync operation and registering the vulnerable callback.
  7. Immediately after setting the metadata (allowing ~10-20ms for debounce timers to fire), the JavaScript executes window.close().
  8. The window closes, destroying the WebContents and causing the SystemMediaControlsWin object to be freed.
  9. The attacker performs a heap spray in the browser process to reclaim the freed memory with a controlled payload.
  10. The WinRT async operation completes, the callback executes, and the dangling this pointer is dereferenced, leading to a virtual call on the attacker’s sprayed data.

Suggested Fix

To prevent this Use-After-Free, the callback should use a base::WeakPtr instead of a raw this pointer, similar to how the ButtonPressed handler is implemented in SystemMediaControlsWin::Initialize (lines 113-125).

auto weak_ptr = weak_factory_.GetWeakPtr();
auto store_async_callback = Microsoft::WRL::Callback<
    ABI::Windows::Foundation::IAsyncOperationCompletedHandler<unsigned int>>(
    [weak_ptr](ABI::Windows::Foundation::IAsyncOperation<unsigned int>* async_op,
               ABI::Windows::Foundation::AsyncStatus status) mutable {
      if (!weak_ptr) return S_OK;
      // ... safe to access weak_ptr->display_updater_ ...
    });

Evaluated with Chrome root at commit: 9760e6c70cd33a320713361f17c6dcca85648c0f


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. Please feel free to reach out to me if you have concerns or feedback.

View on issue tracker