Chrome · Media
CVE-2026-106318
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 |
Files Changed
third_party/blink/renderer/core/html/media/html_media_element.hthird_party/blink/renderer/core/html/media/html_media_element_test.ccthird_party/blink/renderer/core/html/media/html_media_element.cc
Patch
From 852936ed111c9ec6fdfafa7d81d6eca03c7a131b Mon Sep 17 00:00:00 2001 From: Dale Curtis <[email protected]> Date: Wed, 30 Sep 2026 10:39:52 -0700 Subject: [PATCH] Ignore <source> child element insertion when srcObject is present Per the WHATWG HTML specification, child <source> elements are only evaluated if the media element has neither an assigned media provider object (srcObject) nor a src attribute. Prior to this CL, HTMLMediaElement::SourceWasAdded() only checked FastHasAttribute(srcAttr), completely omitting the check for srcObject. Fixed: 566111249 Change-Id: I2fcb100b7da8aef970f88cb280ef753a049bece4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8490153 Commit-Queue: Benjamin Keen <[email protected]> Reviewed-by: Benjamin Keen <[email protected]> Auto-Submit: Dale Curtis <[email protected]> Cr-Commit-Position: refs/heads/main@{#1708276} --- 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 f50a6ae..55e715df 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 @@ -927,8 +927,7 @@ if (lazy_media_load_state_ == LazyMediaLoadState::kDeferred) { LazyMediaHelper::StartMonitoring(this); } else if ((!FastGetAttribute(html_names::kSrcAttr).empty() || - src_object_stream_descriptor_ || - src_object_media_source_handle_) && + HasSrcObject()) && network_state_ == kNetworkEmpty) { ignore_preload_none_ = false; InvokeLoadAlgorithm(); @@ -1327,7 +1326,7 @@ // 6 - If the media element has an assigned media provider object, then let // mode be object. - if (src_object_stream_descriptor_ || src_object_media_source_handle_) { + if (HasSrcObject()) { mode = kObject; } else if (FastHasAttribute(html_names::kSrcAttr)) { // Otherwise, if the media element has no assigned media provider object @@ -3069,8 +3068,7 @@ } bool HTMLMediaElement::HasMediaSources() const { - return FastHasAttribute(html_names::kSrcAttr) || - src_object_stream_descriptor_ || src_object_media_source_handle_ || + return FastHasAttribute(html_names::kSrcAttr) || HasSrcObject() || Traversal<HTMLSourceElement>::FirstChild(*this); } @@ -3936,10 +3934,11 @@ DVLOG(3) << "sourceWasAdded(" << *this << ") - 'src' is " << UrlForLoggingMedia(url); - // We should only consider a <source> element when there is not src attribute - // at all. - if (FastHasAttribute(html_names::kSrcAttr)) + // We should only consider a <source> element when there is neither a + // srcObject nor a src attribute at all. + if (FastHasAttribute(html_names::kSrcAttr) || HasSrcObject()) { return; + } // 4.8.8 - If a source element is inserted as a child of a media element that // has no src attribute and whose networkState has the value NETWORK_EMPTY, @@ -3965,8 +3964,17 @@ if (next_child_node_to_consider_) return; - if (load_state_ != kWaitingForSource) + // Step 21 ("Wait until the node after pointer is a node other than the end of + // the list") only applies when the resource selection algorithm is in the + // source elements section and waiting at the Wait step, which sets + // networkState to NETWORK_NO_SOURCE. If networkState is not NETWORK_NO_SOURCE + // (e.g. NETWORK_IDLE after a decode error where MediaEngineError() aborted + // resource selection but set load_state_ = kWaitingForSource), we must not + // resume candidate selection. + if (load_state_ != kWaitingForSource || + network_state_ != HTMLMediaElement::kNetworkNoSource) { return; + } // 4.8.9.5, resource selection algorithm, source elements section: // 21. Wait until the node after pointer is a node other than the end of the @@ -4001,6 +4009,12 @@ DVLOG(3) << "sourceWasRemoved(" << *this << ") - 'src' is " << UrlForLoggingMedia(url); + // We should only consider a <source> element when there is neither a + // srcObject nor a src attribute at all. + if (FastHasAttribute(html_names::kSrcAttr) || HasSrcObject()) { + return; + } + if (source != current_source_node_ && source != next_child_node_to_consider_) return; diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h index 7087407..f43cbc165 100644 --- a/third_party/blink/renderer/core/html/media/html_media_element.h +++ b/third_party/blink/renderer/core/html/media/html_media_element.h @@ -203,6 +203,9 @@ std::variant<MediaStreamDescriptor*, MediaSourceHandle*>; void SetSrcObjectVariant(SrcObjectVariant src_object_variant); SrcObjectVariant GetSrcObjectVariant() const; + bool HasSrcObject() const { + return src_object_stream_descriptor_ || src_object_media_source_handle_; + } enum NetworkState { kNetworkEmpty, 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 aa23ce3..b589859 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 @@ -43,6 +43,7 @@ #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/heap/thread_state_scopes.h" +#include "third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h" #include "third_party/blink/renderer/platform/network/network_state_notifier.h" #include "third_party/blink/renderer/platform/testing/empty_web_media_player.h" #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" @@ -3647,4 +3648,44 @@ EXPECT_TRUE(MediaShouldBeOpaque()); } +TEST_P(HTMLMediaElementTest, SourceWasAddedIgnoredWithSrcObject) { + MediaStreamComponentVector audio_components; + MediaStreamComponentVector video_components; + auto* descriptor = MakeGarbageCollected<MediaStreamDescriptor>( + audio_components, video_components); + Media()->SetSrcObjectVariant(descriptor); + EXPECT_TRUE(Media()->HasSrcObject()); + + // With srcObject set, appending a <source> child element must be ignored. + auto* source = + Media()->GetDocument().CreateRawElement(html_names::kSourceTag); + source->setAttribute(html_names::kSrcAttr, + AtomicString("http://example.com/test.mp4")); + Media()->appendChild(source); + + test::RunPendingTasks(); + + EXPECT_TRUE(Media()->HasSrcObject()); +} + +TEST_P(HTMLMediaElementTest, SourceWasAddedIgnoredWhenNetworkStateNotNoSource) { + Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp)); + test::RunPendingTasks(); + ASSERT_TRUE(Media()->GetWebMediaPlayer()); + + SetReadyState(HTMLMediaElement::kHaveMetadata); + SetNetworkState(WebMediaPlayer::kNetworkStateDecodeError); + EXPECT_EQ(Media()->getNetworkState(), HTMLMediaElement::kNetworkIdle); + + auto* source = + Media()->GetDocument().CreateRawElement(html_names::kSourceTag); + source->setAttribute(html_names::kSrcAttr, + AtomicString("http://example.com/test.mp4")); + Media()->appendChild(source); + + test::RunPendingTasks(); + + EXPECT_EQ(Media()->getNetworkState(), HTMLMediaElement::kNetworkIdle); +} + } // namespace blink
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 aa23ce3..b589859 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
@@ -43,6 +43,7 @@
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/thread_state_scopes.h"
+#include "third_party/blink/renderer/platform/mediastream/media_stream_descriptor.h"
#include "third_party/blink/renderer/platform/network/network_state_notifier.h"
#include "third_party/blink/renderer/platform/testing/empty_web_media_player.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
@@ -3647,4 +3648,44 @@
EXPECT_TRUE(MediaShouldBeOpaque());
}
+TEST_P(HTMLMediaElementTest, SourceWasAddedIgnoredWithSrcObject) {
+ MediaStreamComponentVector audio_components;
+ MediaStreamComponentVector video_components;
+ auto* descriptor = MakeGarbageCollected<MediaStreamDescriptor>(
+ audio_components, video_components);
+ Media()->SetSrcObjectVariant(descriptor);
+ EXPECT_TRUE(Media()->HasSrcObject());
+
+ // With srcObject set, appending a <source> child element must be ignored.
+ auto* source =
+ Media()->GetDocument().CreateRawElement(html_names::kSourceTag);
+ source->setAttribute(html_names::kSrcAttr,
+ AtomicString("http://example.com/test.mp4"));
+ Media()->appendChild(source);
+
+ test::RunPendingTasks();
+
+ EXPECT_TRUE(Media()->HasSrcObject());
+}
+
+TEST_P(HTMLMediaElementTest, SourceWasAddedIgnoredWhenNetworkStateNotNoSource) {
+ Media()->SetSrc(SrcSchemeToURL(TestURLScheme::kHttp));
+ test::RunPendingTasks();
+ ASSERT_TRUE(Media()->GetWebMediaPlayer());
+
+ SetReadyState(HTMLMediaElement::kHaveMetadata);
+ SetNetworkState(WebMediaPlayer::kNetworkStateDecodeError);
+ EXPECT_EQ(Media()->getNetworkState(), HTMLMediaElement::kNetworkIdle);
+
+ auto* source =
+ Media()->GetDocument().CreateRawElement(html_names::kSourceTag);
+ source->setAttribute(html_names::kSrcAttr,
+ AtomicString("http://example.com/test.mp4"));
+ Media()->appendChild(source);
+
+ test::RunPendingTasks();
+
+ EXPECT_EQ(Media()->getNetworkState(), HTMLMediaElement::kNetworkIdle);
+}
+
} // namespace blink
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