High chrome UAF 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Media
DescriptionUse after free in Media
ComponentMedia
Bug ClassUAF
Tracker570675082
Fix commit8b59df19e200 (chromium/src) +75/-8
CISA KEVNot listed
CreditedXinyang Ge
Disclosed2026-10-13

Changed Functions

FunctionChangeNotes
if
content/renderer/media/renderer_web_media_player_delegate.cc
modified
for
content/renderer/media/renderer_web_media_player_delegate.cc
modified
TEST_F
content/renderer/media/renderer_web_media_player_delegate_browsertest.cc
modified

Files Changed

  • content/renderer/media/renderer_web_media_player_delegate_browsertest.cc
  • content/renderer/media/renderer_web_media_player_delegate.cc
From 8b59df19e200d81d59d6f75985fe0f9a00b744ff Mon Sep 17 00:00:00 2001
From: Dale Curtis <dalecurtis@chromium.org>
Date: Wed, 07 Oct 2026 13:54:59 -0700
Subject: [PATCH] Guard against reentrancy in OnPageVisibilityChanged

OnPageShown() and OnPageHidden() can synchronously run script (e.g. via
text-track cue updates) that removes the frame and destroys the
RendererWebMediaPlayerDelegate. Snapshot player IDs before iterating and
check a WeakPtr after each observer notification.

Fixed: 570675082
Change-Id: I5e784dcd7bb779fa689ba832ae683de827f62792
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8526472
Reviewed-by: Frank Liberato <liberato@chromium.org>
Auto-Submit: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1713842}
---
diff --git a/content/renderer/media/renderer_web_media_player_delegate.cc b/content/renderer/media/renderer_web_media_player_delegate.cc
index 2727f894..d62b0953 100644
--- a/content/renderer/media/renderer_web_media_player_delegate.cc
+++ b/content/renderer/media/renderer_web_media_player_delegate.cc
@@ -6,6 +6,8 @@
 
 #include <stdint.h>
 
+#include <vector>
+
 #include "base/functional/bind.h"
 #include "base/logging.h"
 #include "base/metrics/user_metrics_action.h"
@@ -204,17 +206,34 @@
 
   if (is_shown) {
     RecordAction(base::UserMetricsAction("Media.Shown"));
-
-    for (base::IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd();
-         it.Advance()) {
-      it.GetCurrentValue()->OnPageShown();
-    }
   } else {
     RecordAction(base::UserMetricsAction("Media.Hidden"));
+  }
 
-    for (base::IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd();
-         it.Advance()) {
-      it.GetCurrentValue()->OnPageHidden();
+  // Create a list of players before making any possibly reentrant calls to
+  // OnPageShown() or OnPageHidden().
+  std::vector<int> player_ids;
+  player_ids.reserve(id_map_.size());
+  for (base::IDMap<Observer*>::iterator it(&id_map_); !it.IsAtEnd();
+       it.Advance()) {
+    player_ids.push_back(it.GetCurrentKey());
+  }
+
+  auto weak_this = weak_ptr_factory_.GetWeakPtr();
+  for (int player_id : player_ids) {
+    Observer* player = id_map_.Lookup(player_id);
+    if (!player) {
+      continue;
+    }
+
+    if (is_shown) {
+      player->OnPageShown();
+    } else {
+      player->OnPageHidden();
+    }
+
+    if (!weak_this) {
+      return;
     }
   }
 
diff --git a/content/renderer/media/renderer_web_media_player_delegate_browsertest.cc b/content/renderer/media/renderer_web_media_player_delegate_browsertest.cc
index 6ea3fe4a..a737f50 100644
--- a/content/renderer/media/renderer_web_media_player_delegate_browsertest.cc
+++ b/content/renderer/media/renderer_web_media_player_delegate_browsertest.cc
@@ -199,4 +199,52 @@
   }
 }
 
+TEST_F(RendererWebMediaPlayerDelegateTest, DestroyingDelegateOnPageHidden) {
+  const int delegate_id_1 = delegate_manager_->AddObserver(&observer_1_);
+  const int delegate_id_2 = delegate_manager_->AddObserver(&observer_2_);
+
+  int call_count = 0;
+  auto destroy_delegate = [&]() {
+    ++call_count;
+    delegate_manager_->RemoveObserver(delegate_id_1);
+    delegate_manager_->RemoveObserver(delegate_id_2);
+    delegate_manager_.reset();
+  };
+
+  EXPECT_CALL(observer_1_, OnPageHidden())
+      .Times(testing::AtMost(1))
+      .WillRepeatedly(destroy_delegate);
+  EXPECT_CALL(observer_2_, OnPageHidden())
+      .Times(testing::AtMost(1))
+      .WillRepeatedly(destroy_delegate);
+
+  delegate_manager_->OnPageVisibilityChanged(
+      blink::mojom::PageVisibilityState::kHidden);
+  EXPECT_EQ(1, call_count);
+}
+
+TEST_F(RendererWebMediaPlayerDelegateTest, DestroyingDelegateOnPageShown) {
+  const int delegate_id_1 = delegate_manager_->AddObserver(&observer_1_);
+  const int delegate_id_2 = delegate_manager_->AddObserver(&observer_2_);
+
+  int call_count = 0;
+  auto destroy_delegate = [&]() {
+    ++call_count;
+    delegate_manager_->RemoveObserver(delegate_id_1);
+    delegate_manager_->RemoveObserver(delegate_id_2);
+    delegate_manager_.reset();
+  };
+
+  EXPECT_CALL(observer_1_, OnPageShown())
+      .Times(testing::AtMost(1))
+      .WillRepeatedly(destroy_delegate);
+  EXPECT_CALL(observer_2_, OnPageShown())
+      .Times(testing::AtMost(1))
+      .WillRepeatedly(destroy_delegate);
+
+  delegate_manager_->OnPageVisibilityChanged(
+      blink::mojom::PageVisibilityState::kVisible);
+  EXPECT_EQ(1, call_count);
+}
+
 }  // namespace media
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/renderer/media/renderer_web_media_player_delegate_browsertest.cc b/content/renderer/media/renderer_web_media_player_delegate_browsertest.cc
index 6ea3fe4a..a737f50 100644
--- a/content/renderer/media/renderer_web_media_player_delegate_browsertest.cc
+++ b/content/renderer/media/renderer_web_media_player_delegate_browsertest.cc
@@ -199,4 +199,52 @@
   }
 }
 
+TEST_F(RendererWebMediaPlayerDelegateTest, DestroyingDelegateOnPageHidden) {
+  const int delegate_id_1 = delegate_manager_->AddObserver(&observer_1_);
+  const int delegate_id_2 = delegate_manager_->AddObserver(&observer_2_);
+
+  int call_count = 0;
+  auto destroy_delegate = [&]() {
+    ++call_count;
+    delegate_manager_->RemoveObserver(delegate_id_1);
+    delegate_manager_->RemoveObserver(delegate_id_2);
+    delegate_manager_.reset();
+  };
+
+  EXPECT_CALL(observer_1_, OnPageHidden())
+      .Times(testing::AtMost(1))
+      .WillRepeatedly(destroy_delegate);
+  EXPECT_CALL(observer_2_, OnPageHidden())
+      .Times(testing::AtMost(1))
+      .WillRepeatedly(destroy_delegate);
+
+  delegate_manager_->OnPageVisibilityChanged(
+      blink::mojom::PageVisibilityState::kHidden);
+  EXPECT_EQ(1, call_count);
+}
+
+TEST_F(RendererWebMediaPlayerDelegateTest, DestroyingDelegateOnPageShown) {
+  const int delegate_id_1 = delegate_manager_->AddObserver(&observer_1_);
+  const int delegate_id_2 = delegate_manager_->AddObserver(&observer_2_);
+
+  int call_count = 0;
+  auto destroy_delegate = [&]() {
+    ++call_count;
+    delegate_manager_->RemoveObserver(delegate_id_1);
+    delegate_manager_->RemoveObserver(delegate_id_2);
+    delegate_manager_.reset();
+  };
+
+  EXPECT_CALL(observer_1_, OnPageShown())
+      .Times(testing::AtMost(1))
+      .WillRepeatedly(destroy_delegate);
+  EXPECT_CALL(observer_2_, OnPageShown())
+      .Times(testing::AtMost(1))
+      .WillRepeatedly(destroy_delegate);
+
+  delegate_manager_->OnPageVisibilityChanged(
+      blink::mojom::PageVisibilityState::kVisible);
+  EXPECT_EQ(1, call_count);
+}
+
 }  // namespace media
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.