Chrome · Media
CVE-2026-106383
UAF in Media
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/core/html/media/html_media_element.cc |
modified | |
TEST_Pthird_party/blink/renderer/core/html/media/html_media_element_test.cc |
modified | |
forthird_party/blink/renderer/core/html/media/html_media_element_test.cc |
modified |
Files Changed
third_party/blink/renderer/core/html/media/html_media_element_test.ccthird_party/blink/renderer/core/html/media/html_media_element.cc
Patch
From fee8baee165a2165c254585eeef25a304381bd1d Mon Sep 17 00:00:00 2001
From: Dale Curtis <dalecurtis@chromium.org>
Date: Wed, 30 Sep 2026 21:43:49 -0700
Subject: [PATCH] Fix bugs in HTMLMediaElement::SelectNextSourceChild
In SelectNextSourceChild, src_value was held across MediaQueryMatches().
That call can trigger a style and layout update in an embedding
document, synchronizing dirty style attributes and causing the element's
attribute buffer to reallocate.
Fixed: 567447106
Change-Id: I7a5b13183f65710937d57b6298165bdd0c2f0841
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8494613
Commit-Queue: Benjamin Keen <bkeen@google.com>
Auto-Submit: Dale Curtis <dalecurtis@chromium.org>
Commit-Queue: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: Benjamin Keen <bkeen@google.com>
Cr-Commit-Position: refs/heads/main@{#1708606}
---
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc
index 55e715df..8c8aa2f 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -3857,13 +3857,11 @@
// 2. If candidate does not have a src attribute, or if its src
// attribute's value is the empty string ... jump down to the failed
// step below
- const AtomicString& src_value =
- source->FastGetAttribute(html_names::kSrcAttr);
if (should_log) {
DVLOG(3) << "selectNextSourceChild(" << *this << ") - 'src' is "
<< UrlForLoggingMedia(media_url);
}
- if (src_value.empty()) {
+ if (source->FastGetAttribute(html_names::kSrcAttr).empty()) {
goto checkAgain;
}
@@ -3878,7 +3876,11 @@
// 4. Let urlRecord be the result of encoding-parsing a URL given
// candidate's src attribute's value, relative to candidate's node document
// when the src attribute was last changed.
- media_url = source->GetDocument().CompleteURL(src_value);
+ // Note: Do not cache `kSrcAttr` in a local reference across step 3, as
+ // `MediaQueryMatches()` can trigger style/layout updates that mutate
+ // attributes or reallocate the element's attribute buffer.
+ media_url = source->GetDocument().CompleteURL(
+ source->FastGetAttribute(html_names::kSrcAttr));
// 5. If urlRecord is failure, then end the synchronous section, and jump
// down to the failed with elements step below.
diff --git a/third_party/blink/renderer/core/html/media/html_media_element_test.cc b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
index b589859..bed9553 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element_test.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
@@ -28,6 +28,7 @@
#include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
#include "third_party/blink/renderer/core/html/html_document.h"
#include "third_party/blink/renderer/core/html/html_head_element.h"
+#include "third_party/blink/renderer/core/html/html_source_element.h"
#include "third_party/blink/renderer/core/html/media/html_audio_element.h"
#include "third_party/blink/renderer/core/html/media/html_video_element.h"
#include "third_party/blink/renderer/core/html/media/media_error.h"
@@ -613,6 +614,11 @@
}
protected:
+ KURL SelectNextSourceChild(String* content_type,
+ HTMLMediaElement::InvalidURLAction action) {
+ return Media()->SelectNextSourceChild(content_type, action);
+ }
+
// Helpers to call MediaPlayerObserver mojo methods and check their results.
void NotifyMediaPlaying() {
media_->DidPlayerStartPlaying();
@@ -3648,6 +3654,31 @@
EXPECT_TRUE(MediaShouldBeOpaque());
}
+TEST_P(HTMLMediaElementTest, SelectNextSourceChildMediaQueryMatching) {
+ auto* source1 =
+ Media()->GetDocument().CreateRawElement(html_names::kSourceTag);
+ source1->setAttribute(html_names::kSrcAttr,
+ AtomicString("http://example.com/not_matched.mp4"));
+ source1->setAttribute(html_names::kMediaAttr, AtomicString("not all"));
+ // Fill up inline attribute vector capacity to verify attribute stability.
+ for (int i = 0; i < 6; ++i) {
+ source1->setAttribute(AtomicString("data-attr" + String::Number(i)),
+ AtomicString("val"));
+ }
+ Media()->AppendChild(source1);
+
+ auto* source2 =
+ Media()->GetDocument().CreateRawElement(html_names::kSourceTag);
+ source2->setAttribute(html_names::kSrcAttr,
+ AtomicString("http://example.com/matched.mp4"));
+ source2->setAttribute(html_names::kMediaAttr, AtomicString("all"));
+ Media()->AppendChild(source2);
+
+ String content_type;
+ KURL url = SelectNextSourceChild(&content_type, HTMLMediaElement::kDoNothing);
+ EXPECT_EQ(url.GetString(), "http://example.com/matched.mp4");
+}
+
TEST_P(HTMLMediaElementTest, SourceWasAddedIgnoredWithSrcObject) {
MediaStreamComponentVector audio_components;
MediaStreamComponentVector video_components;
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/renderer/core/html/media/html_media_element_test.cc b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
index b589859..bed9553 100644
--- a/third_party/blink/renderer/core/html/media/html_media_element_test.cc
+++ b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
@@ -28,6 +28,7 @@
#include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
#include "third_party/blink/renderer/core/html/html_document.h"
#include "third_party/blink/renderer/core/html/html_head_element.h"
+#include "third_party/blink/renderer/core/html/html_source_element.h"
#include "third_party/blink/renderer/core/html/media/html_audio_element.h"
#include "third_party/blink/renderer/core/html/media/html_video_element.h"
#include "third_party/blink/renderer/core/html/media/media_error.h"
@@ -613,6 +614,11 @@
}
protected:
+ KURL SelectNextSourceChild(String* content_type,
+ HTMLMediaElement::InvalidURLAction action) {
+ return Media()->SelectNextSourceChild(content_type, action);
+ }
+
// Helpers to call MediaPlayerObserver mojo methods and check their results.
void NotifyMediaPlaying() {
media_->DidPlayerStartPlaying();
@@ -3648,6 +3654,31 @@
EXPECT_TRUE(MediaShouldBeOpaque());
}
+TEST_P(HTMLMediaElementTest, SelectNextSourceChildMediaQueryMatching) {
+ auto* source1 =
+ Media()->GetDocument().CreateRawElement(html_names::kSourceTag);
+ source1->setAttribute(html_names::kSrcAttr,
+ AtomicString("http://example.com/not_matched.mp4"));
+ source1->setAttribute(html_names::kMediaAttr, AtomicString("not all"));
+ // Fill up inline attribute vector capacity to verify attribute stability.
+ for (int i = 0; i < 6; ++i) {
+ source1->setAttribute(AtomicString("data-attr" + String::Number(i)),
+ AtomicString("val"));
+ }
+ Media()->AppendChild(source1);
+
+ auto* source2 =
+ Media()->GetDocument().CreateRawElement(html_names::kSourceTag);
+ source2->setAttribute(html_names::kSrcAttr,
+ AtomicString("http://example.com/matched.mp4"));
+ source2->setAttribute(html_names::kMediaAttr, AtomicString("all"));
+ Media()->AppendChild(source2);
+
+ String content_type;
+ KURL url = SelectNextSourceChild(&content_type, HTMLMediaElement::kDoNothing);
+ EXPECT_EQ(url.GetString(), "http://example.com/matched.mp4");
+}
+
TEST_P(HTMLMediaElementTest, SourceWasAddedIgnoredWithSrcObject) {
MediaStreamComponentVector audio_components;
MediaStreamComponentVector video_components;
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