High patch-now ✔︎ verified CVSS 8.8 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Media Stream
DescriptionUse after free in Media Stream
ComponentMedia Stream
Sub-componentBlink>MediaStream
Bug ClassUAF
Action tierpatch-now — corruption primitive, web-reachable in default config (confirmed) — upheld by independent review by AI
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Independent review by AIConfirmed
Reviewer notes: Mechanism matches the diff comment and test. Minor: the test adds the sink to the platform track manually; the production path that registers sinks and its default-config web reachability are not shown in the diff.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker426054987
Fix commit90d99dcd83af (chromium/src) +79/-18
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedAnonymous
Reported2025-06-19 · bounty $8000 (per vendor advisory)
Fix landed2025-07-14 (public git)
Disclosed2025-07-29
Patch gap15 days — fix public in source before the advisory

Background

`MediaStreamTrackImpl`
The primary Blink implementation of the MediaStreamTrack IDL type, a garbage-collected object that can own audio sinks registered against its underlying platform track.
`SpeechRecognitionMediaStreamAudioSink`
An audio sink owned (via registered_sinks_) by a MediaStreamTrackImpl and also referenced by raw pointer inside the platform MediaStreamAudioTrack’s sink list.
`MediaStreamAudioTrack`
The platform-side audio track (MediaStreamAudioTrack::From(Component())) that keeps a list of sinks it delivers audio to and may call into them (e.g., during stop/notify) using raw pointers.
Oilpan pre-finalizer (`USING_PRE_FINALIZER`)
A hook that runs when a garbage-collected object is being reclaimed, allowing it to detach from external structures before its memory is freed.

Root Cause Analysis

A MediaStreamTrackImpl owns its SpeechRecognitionMediaStreamAudioSinks (strong GC references in registered_sinks_), while the platform MediaStreamAudioTrack separately holds raw pointers to those same sinks in its own sink list. The MediaStreamTrackImpl and its sinks could be garbage-collected before the MediaStreamAudioTrack, but nothing removed the sinks from the platform track’s raw-pointer list at that point, so the track retained dangling pointers to freed sinks. When the MediaStreamAudioTrack was later stopped/destroyed and iterated its sink list (the test drives this by clearing the component and running GC so the component’s pre-finalizer stops the track), it dereferenced the freed sinks — a use-after-free.

The fix adds a Dispose pre-finalizer to MediaStreamTrackImpl (USING_PRE_FINALIZER(MediaStreamTrackImpl, Dispose)) that, before the object and its sinks are reclaimed, looks up the platform MediaStreamAudioTrack and calls RemoveSink for every sink in registered_sinks_. Removing the raw pointers from the platform track’s list before the sinks are freed re-establishes the invariant that the track never holds references to destroyed sinks, eliminating the dangling access.

Key insight
The platform MediaStreamAudioTrack kept raw pointers to sinks owned by a MediaStreamTrackImpl that could be destroyed first, leaving dangling entries; the fix adds a pre-finalizer Dispose that removes those sinks from the track before they are freed.

Attack Path

  1. Create a track wrapper and sink Script creates a MediaStreamTrackImpl over a live audio component and a SpeechRecognitionMediaStreamAudioSink, registered via RegisterSink so the sink is both owned by the wrapper and added to the platform track (AddSink).
  2. Drop the wrapper while the track outlives it Arrange for the MediaStreamTrackImpl (and its owned sink) to become unreachable while the underlying MediaStreamComponent/MediaStreamAudioTrack stays alive (the regression test does this by scoping track1/sink and forcing GC).
  3. Sinks freed, raw pointers left behind Garbage collection destroys the wrapper and the sink, but the platform track’s sink list still holds raw pointers to the now-freed sink (pre-patch, no removal occurred).
  4. Stop/destroy the platform track When the platform track is later stopped and iterates its sink list (e.g., via the component’s pre-finalizer calling StopAndNotify), it dereferences the freed sink — a use-after-free. Turning this into a controlled primitive is not shown by the patch (inferred).

Impact Assessment

A web page using speech-recognition audio sinks on a media-stream track can arrange lifetimes so the platform MediaStreamAudioTrack accesses SpeechRecognitionMediaStreamAudioSinks after they are freed, causing a use-after-free in the Blink renderer process. The advisory classifies this as a use-after-free in Media Stream (bug 426054987) and does not report in-the-wild exploitation; the precondition is script that registers such sinks and controls object lifetimes/GC, and any escalation beyond the dangling access to memory disclosure or code execution is (inferred).

Changed Functions

FunctionChangeNotes
for
third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc
modified
MODULES_EXPORT
third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h
modified
MakeLocalMediaStreamAudioSource
third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc
modified
TEST_F
third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc
modified

Files Changed

  • third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc
  • third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h
  • third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc

Audit Directions

  • Raw pointers to GC-owned objects
    Look for platform/native structures (sink lists, observer lists) that store raw pointers to Oilpan-managed objects owned elsewhere, where the owner can be collected before the holder.
  • Missing detach on finalization
    Audit garbage-collected classes that register themselves or their children with longer-lived non-GC objects but lack a pre-finalizer/RemoveX to detach before destruction.
  • Asymmetric register/unregister
    Search for AddSink/RegisterSink-style APIs whose corresponding removal is only invoked on explicit teardown paths (stop/close) and not on garbage collection or error paths.
From 90d99dcd83af2f13e0f917b5a9dc59eeee0204d0 Mon Sep 17 00:00:00 2001
From: Evan Liu <[email protected]>
Date: Mon, 14 Jul 2025 12:21:45 -0700
Subject: [PATCH] Fix potential UAF in MediaStreamTrackImpl

This CL fixes a potential UAF vulnerability in MediaStreamTrackImpl
where pointers to the SpeechRecognitionMediaStreamAudioSinks that are
owned by the MediaStreamTrackImpl could potentially be accessed after
the sinks are destroyed.

Fixed: 426054987
Change-Id: I453160a8eed7926e2cc3500260de04d2722c98e1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6712212
Commit-Queue: Evan Liu <[email protected]>
Reviewed-by: Mark Foltz <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1486476}
---

diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc
index 2131a1b1..320d73e80 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.cc
@@ -810,6 +810,19 @@
   return capture_handle;
 }
 
+void MediaStreamTrackImpl::Dispose() {
+  // `MediaStreamTrackImpl` and the `SpeechRecognitionMediaStreamAudioSink`
+  // which it owns may be destroyed before the `MediaStreamAudioTrack`. Remove
+  // the sinks before destroying them to prevent `MediaStreamAudioTrack` from
+  // using them after destruction.
+  if (MediaStreamAudioTrack* audio_track =
+          MediaStreamAudioTrack::From(Component())) {
+    for (SpeechRecognitionMediaStreamAudioSink* sink : registered_sinks_) {
+      audio_track->RemoveSink(sink);
+    }
+  }
+}
+
 ScriptPromise<IDLUndefined> MediaStreamTrackImpl::applyConstraints(
     ScriptState* script_state,
     const MediaTrackConstraints* constraints) {
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h
index a7ec455..aa392255 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h
@@ -57,6 +57,8 @@
 // Primary implementation of the MediaStreamTrack interface and idl type.
 class MODULES_EXPORT MediaStreamTrackImpl : public MediaStreamTrack,
                                             public MediaStreamSource::Observer {
+  USING_PRE_FINALIZER(MediaStreamTrackImpl, Dispose);
+
  public:
   // Create a MediaStreamTrackImpl of the appropriate type for the display
   // surface type.
@@ -169,6 +171,8 @@
   friend class CanvasCaptureMediaStreamTrack;
   friend class InternalsMediaStream;
 
+  void Dispose();
+
   // MediaStreamTrack
   void applyConstraints(ScriptPromiseResolver<IDLUndefined>*,
                         const MediaTrackConstraints*) override;
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc
index 55c7b9c0..f9675051 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc
@@ -20,6 +20,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_constrain_long_range.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_media_stream_track_state.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_media_track_constraints.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_constrainlongrange_long.h"
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
@@ -28,14 +29,15 @@
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/core/streams/readable_stream_default_reader.h"
 #include "third_party/blink/renderer/modules/mediastream/apply_constraints_processor.h"
-#include "third_party/blink/renderer/modules/mediastream/local_media_stream_audio_source.h"
 #include "third_party/blink/renderer/modules/mediastream/media_constraints.h"
 #include "third_party/blink/renderer/modules/mediastream/media_constraints_impl.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_content.h"
+#include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_sink.h"
 #include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h"
+#include "third_party/blink/renderer/modules/mediastream/speech_recognition_media_stream_audio_sink.h"
 #include "third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h"
@@ -90,20 +92,6 @@
   MOCK_METHOD(void, EnabledStateChangedForWebRtcAudio, (bool));
 };
 
-std::unique_ptr<blink::LocalMediaStreamAudioSource>
-MakeLocalMediaStreamAudioSource() {
-  blink::MediaStreamDevice device;
-  device.type = blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE;
-  return std::make_unique<blink::LocalMediaStreamAudioSource>(
-      /*blink::WebLocalFrame=*/nullptr, device,
-      /*requested_buffer_size=*/nullptr,
-      /*disable_local_echo=*/false,
-      MediaStreamAudioProcessingLayout::MakeForUnprocessedLocalSourceForTests(
-          /*platform_aec=*/false, /*available_platform_effects=*/0),
-      blink::WebPlatformMediaStreamSource::ConstraintsRepeatingCallback(),
-      blink::scheduler::GetSingleThreadTaskRunnerForTesting());
-}
-
 MediaStreamComponent* MakeMockVideoComponent() {
   std::unique_ptr<MockMediaStreamVideoSource> platform_source =
       MakeMockMediaStreamVideoSource();
@@ -121,7 +109,10 @@
 MediaStreamComponent* MakeMockAudioComponent() {
   MediaStreamSource* source = MakeGarbageCollected<MediaStreamSource>(
       "id", MediaStreamSource::StreamType::kTypeAudio, "name",
-      /*remote=*/false, MakeLocalMediaStreamAudioSource());
+      /*remote=*/false,
+      std::make_unique<MediaStreamAudioSource>(
+          scheduler::GetSingleThreadTaskRunnerForTesting(),
+          true /* is_local_source */));
   auto platform_track =
       std::make_unique<MediaStreamAudioTrack>(true /* is_local_track */);
   return MakeGarbageCollected<MediaStreamComponentImpl>(
@@ -284,7 +275,10 @@
   MediaStreamComponent* MakeMockWebRtcAudioComponent() {
     auto* source = MakeGarbageCollected<MediaStreamSource>(
         "id", MediaStreamSource::StreamType::kTypeAudio, "name",
-        /*remote=*/true, MakeLocalMediaStreamAudioSource());
+        /*remote=*/true,
+        std::make_unique<MediaStreamAudioSource>(
+            scheduler::GetSingleThreadTaskRunnerForTesting(),
+            false /* is_local_source */));
 
     scoped_refptr<webrtc::AudioTrackInterface> remote_track(
         blink::MockWebRtcAudioTrack::Create("track_id").get());
@@ -298,7 +292,10 @@
   MediaStreamComponent* MakeMockAudioComponent() {
     MediaStreamSource* source = MakeGarbageCollected<MediaStreamSource>(
         "id", MediaStreamSource::StreamType::kTypeAudio, "name",
-        /*remote=*/false, MakeLocalMediaStreamAudioSource());
+        /*remote=*/false,
+        std::make_unique<MediaStreamAudioSource>(
+            scheduler::GetSingleThreadTaskRunnerForTesting(),
+            true /* is_local_source */));
     auto platform_track =
         std::make_unique<MediaStreamAudioTrack>(true /* is_local_track */);
     return MakeGarbageCollected<MediaStreamComponentImpl>(
@@ -861,4 +858,51 @@
   EXPECT_EQ(video_track->min_frame_rate(), kMinFrameRate);
 }
 
+TEST_F(MediaStreamTrackImplTest, StopAudioTrackAfterSinkDestroyed) {
+  V8TestingScope v8_scope;
+
+  // 1. Create the underlying platform track and its component.
+  // Keep this component alive with a Persistent handle to control its
+  // lifetime, ensuring it outlives the temporary track and sink created below.
+  Persistent<MediaStreamComponent> component = MakeMockAudioComponent();
+  MediaStreamAudioSource* source =
+      MediaStreamAudioSource::From(component->Source());
+
+  MediaStreamAudioTrack* platform_track =
+      MediaStreamAudioTrack::From(component.Get());
+  ASSERT_TRUE(source);
+  ASSERT_TRUE(platform_track);
+
+  // 2. Start the platform track by connecting it to the source. After this, the
+  // track is "live" and can accept sinks.
+  source->ConnectToInitializedTrack(component.Get());
+
+  // 3. Create a temporary MediaStreamTrackImpl wrapper and a sink in a
+  // separate scope. This wrapper will "own" the sink via a strong GC ref.
+  {
+    MediaStreamTrack* track1 = MakeGarbageCollected<MediaStreamTrackImpl>(
+        v8_scope.GetExecutionContext(), component.Get());
+    auto* sink = MakeGarbageCollected<SpeechRecognitionMediaStreamAudioSink>(
+        v8_scope.GetExecutionContext(), base::DoNothing());
+
+    // 4. Register the sink with the wrapper and add its raw pointer to the
+    // platform track's sink list.
+    track1->RegisterSink(sink);
+    platform_track->AddSink(sink);
+  }
+
+  // 5. Force garbage collection. This destroys `track_with_sink` and `sink`.
+  // If the fix is present, `track_with_sink->Dispose()` is called, removing
+  // the sink from `platform_track`.
+  WebHeap::CollectAllGarbageForTesting();
+
+  // 5. Now, destroy the component that owns the platform track by clearing the
+  // persistent handle and running GC again. The component's pre-finalizer,
+  // Dispose(), will call `platform_track->StopAndNotify()`.
+  component.Clear();
+  WebHeap::CollectAllGarbageForTesting();
+
+  // The test passes if it doesn't crash.
+}
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc
index 55c7b9c0..f9675051 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_track_impl_test.cc
@@ -20,6 +20,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_constrain_long_range.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_media_stream_track_state.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_media_track_constraints.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_union_constrainlongrange_long.h"
 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
@@ -28,14 +29,15 @@
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/core/streams/readable_stream_default_reader.h"
 #include "third_party/blink/renderer/modules/mediastream/apply_constraints_processor.h"
-#include "third_party/blink/renderer/modules/mediastream/local_media_stream_audio_source.h"
 #include "third_party/blink/renderer/modules/mediastream/media_constraints.h"
 #include "third_party/blink/renderer/modules/mediastream/media_constraints_impl.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_video_content.h"
+#include "third_party/blink/renderer/modules/mediastream/media_stream_track.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_sink.h"
 #include "third_party/blink/renderer/modules/mediastream/mock_media_stream_video_source.h"
+#include "third_party/blink/renderer/modules/mediastream/speech_recognition_media_stream_audio_sink.h"
 #include "third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/mediastream/media_stream_audio_source.h"
@@ -90,20 +92,6 @@
   MOCK_METHOD(void, EnabledStateChangedForWebRtcAudio, (bool));
 };
 
-std::unique_ptr<blink::LocalMediaStreamAudioSource>
-MakeLocalMediaStreamAudioSource() {
-  blink::MediaStreamDevice device;
-  device.type = blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE;
-  return std::make_unique<blink::LocalMediaStreamAudioSource>(
-      /*blink::WebLocalFrame=*/nullptr, device,
-      /*requested_buffer_size=*/nullptr,
-      /*disable_local_echo=*/false,
-      MediaStreamAudioProcessingLayout::MakeForUnprocessedLocalSourceForTests(
-          /*platform_aec=*/false, /*available_platform_effects=*/0),
-      blink::WebPlatformMediaStreamSource::ConstraintsRepeatingCallback(),
-      blink::scheduler::GetSingleThreadTaskRunnerForTesting());
-}
-
 MediaStreamComponent* MakeMockVideoComponent() {
   std::unique_ptr<MockMediaStreamVideoSource> platform_source =
       MakeMockMediaStreamVideoSource();
@@ -121,7 +109,10 @@
 MediaStreamComponent* MakeMockAudioComponent() {
   MediaStreamSource* source = MakeGarbageCollected<MediaStreamSource>(
       "id", MediaStreamSource::StreamType::kTypeAudio, "name",
-      /*remote=*/false, MakeLocalMediaStreamAudioSource());
+      /*remote=*/false,
+      std::make_unique<MediaStreamAudioSource>(
+          scheduler::GetSingleThreadTaskRunnerForTesting(),
+          true /* is_local_source */));
   auto platform_track =
       std::make_unique<MediaStreamAudioTrack>(true /* is_local_track */);
   return MakeGarbageCollected<MediaStreamComponentImpl>(
@@ -284,7 +275,10 @@
   MediaStreamComponent* MakeMockWebRtcAudioComponent() {
     auto* source = MakeGarbageCollected<MediaStreamSource>(
         "id", MediaStreamSource::StreamType::kTypeAudio, "name",
-        /*remote=*/true, MakeLocalMediaStreamAudioSource());
+        /*remote=*/true,
+        std::make_unique<MediaStreamAudioSource>(
+            scheduler::GetSingleThreadTaskRunnerForTesting(),
+            false /* is_local_source */));
 
     scoped_refptr<webrtc::AudioTrackInterface> remote_track(
         blink::MockWebRtcAudioTrack::Create("track_id").get());
@@ -298,7 +292,10 @@
   MediaStreamComponent* MakeMockAudioComponent() {
     MediaStreamSource* source = MakeGarbageCollected<MediaStreamSource>(
         "id", MediaStreamSource::StreamType::kTypeAudio, "name",
-        /*remote=*/false, MakeLocalMediaStreamAudioSource());
+        /*remote=*/false,
+        std::make_unique<MediaStreamAudioSource>(
+            scheduler::GetSingleThreadTaskRunnerForTesting(),
+            true /* is_local_source */));
     auto platform_track =
         std::make_unique<MediaStreamAudioTrack>(true /* is_local_track */);
     return MakeGarbageCollected<MediaStreamComponentImpl>(
@@ -861,4 +858,51 @@
   EXPECT_EQ(video_track->min_frame_rate(), kMinFrameRate);
 }
 
+TEST_F(MediaStreamTrackImplTest, StopAudioTrackAfterSinkDestroyed) {
+  V8TestingScope v8_scope;
+
+  // 1. Create the underlying platform track and its component.
+  // Keep this component alive with a Persistent handle to control its
+  // lifetime, ensuring it outlives the temporary track and sink created below.
+  Persistent<MediaStreamComponent> component = MakeMockAudioComponent();
+  MediaStreamAudioSource* source =
+      MediaStreamAudioSource::From(component->Source());
+
+  MediaStreamAudioTrack* platform_track =
+      MediaStreamAudioTrack::From(component.Get());
+  ASSERT_TRUE(source);
+  ASSERT_TRUE(platform_track);
+
+  // 2. Start the platform track by connecting it to the source. After this, the
+  // track is "live" and can accept sinks.
+  source->ConnectToInitializedTrack(component.Get());
+
+  // 3. Create a temporary MediaStreamTrackImpl wrapper and a sink in a
+  // separate scope. This wrapper will "own" the sink via a strong GC ref.
+  {
+    MediaStreamTrack* track1 = MakeGarbageCollected<MediaStreamTrackImpl>(
+        v8_scope.GetExecutionContext(), component.Get());
+    auto* sink = MakeGarbageCollected<SpeechRecognitionMediaStreamAudioSink>(
+        v8_scope.GetExecutionContext(), base::DoNothing());
+
+    // 4. Register the sink with the wrapper and add its raw pointer to the
+    // platform track's sink list.
+    track1->RegisterSink(sink);
+    platform_track->AddSink(sink);
+  }
+
+  // 5. Force garbage collection. This destroys `track_with_sink` and `sink`.
+  // If the fix is present, `track_with_sink->Dispose()` is called, removing
+  // the sink from `platform_track`.
+  WebHeap::CollectAllGarbageForTesting();
+
+  // 5. Now, destroy the component that owns the platform track by clearing the
+  // persistent handle and running GC again. The component's pre-finalizer,
+  // Dispose(), will call `platform_track->StopAndNotify()`.
+  component.Clear();
+  WebHeap::CollectAllGarbageForTesting();
+
+  // The test passes if it doesn't crash.
+}
+
 }  // namespace blink
Loading diff…

Original Bug Report

reported by [email protected]

use-after-poison in blink::MediaStreamAudioTrack::StopAndNotify(class base::OnceCallback<(void)>)

Security Bug

Important: Please do not change the component of this bug manually.

Please READ THIS FAQ before filing a bug: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/faq.md

Please see the following link for instructions on filing security bugs: https://www.chromium.org/Home/chromium-security/reporting-security-bugs

Reports may be eligible for reward payments under the Chrome VRP: https://g.co/chrome/vrp

NOTE: Security bugs are normally made public once a fix has been widely deployed.


VULNERABILITY DETAILS AddressSanitizer: use-after-poison in blink::MediaStreamAudioTrack::StopAndNotify(class base::OnceCallback<(void)>)

VERSION

asan-win32-release_x64-1476007

Reproduction Steps

  • python -m http.server 8000
  • Run: chrome --autoplay-policy=no-user-gesture-required --js-flags="-expose-gc" --no-sandbox --user-data-dir=test --enable-logging=stderr poc.html

Type of crash

Tab

Root Cause Analysis (RCA)

Coming soon.

View on issue tracker