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 <[email protected]> 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 <[email protected]> Auto-Submit: Dale Curtis <[email protected]> Commit-Queue: Dale Curtis <[email protected]> Reviewed-by: Benjamin Keen <[email protected]> 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