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 <dalecurtis@chromium.org>
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 <bkeen@google.com>
Reviewed-by: Benjamin Keen <bkeen@google.com>
Auto-Submit: Dale Curtis <dalecurtis@chromium.org>
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