Chrome · Media
CVE-2026-110171
UAF in Media
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcontent/renderer/media/renderer_web_media_player_delegate.cc |
modified | |
forcontent/renderer/media/renderer_web_media_player_delegate.cc |
modified | |
TEST_Fcontent/renderer/media/renderer_web_media_player_delegate_browsertest.cc |
modified |
Files Changed
content/renderer/media/renderer_web_media_player_delegate_browsertest.cccontent/renderer/media/renderer_web_media_player_delegate.cc
Patch
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.
References
On This Page