Chrome · WebAudio › WebAudio
CVE-2026-106235
UAF in WebAudio (WebAudio)
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fthird_party/blink/renderer/modules/webaudio/audio_context_test.cc |
modified |
Files Changed
third_party/blink/renderer/modules/webaudio/audio_node.ccthird_party/blink/renderer/modules/webaudio/audio_context_test.cc
Patch
From e8cfa0184ae597598dff05b7cfb1c590ef492d48 Mon Sep 17 00:00:00 2001 From: Hongchan Choi <[email protected]> Date: Wed, 30 Sep 2026 12:16:21 -0700 Subject: [PATCH] [WebAudio] Defer AudioHandler disposal for all non-closed states AudioNode::Dispose() previously checked only for kSuspended and kInterrupted in addition to IsPullingAudioGraph() when adding the handler to DeferredTaskHandler::AddRenderingOrphanHandler(). If rendering was paused while ContextState() remained kRunning, disposed handlers were not deferred through DeferredTaskHandler. Changes: - Update AudioNode::Dispose() to defer handler cleanup whenever ContextState() is not kClosed (or when the graph is being pulled). - Add AudioContextTest.DisposeOrphansHandlerWhenRunningWithoutPulling to cover node disposal in this state. Bug: 565612897 Test: AudioContextTest.DisposeOrphansHandlerWhenRunningWithoutPulling Change-Id: I808b5776061d3c5eecad0651722b23b301625c8f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8462555 Commit-Queue: Hongchan Choi <[email protected]> Reviewed-by: Mahesh Kannan <[email protected]> Cr-Commit-Position: refs/heads/main@{#1708385} --- diff --git a/third_party/blink/renderer/modules/webaudio/audio_node.cc b/third_party/blink/renderer/modules/webaudio/audio_node.cc index 90ce5f78..0b1f792 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_node.cc +++ b/third_party/blink/renderer/modules/webaudio/audio_node.cc @@ -77,12 +77,12 @@ // Add the handler to the orphan list. This keeps the handler alive until it // can be deleted at a safe point (in pre/post handler task). If the graph is - // being processed, the handler must be added. If the context is suspended, - // the handler still needs to be added in case the context is resumed. + // being processed, the handler must be added. If the context is not closed, + // the handler still needs to be added in case rendering is paused or the + // context is later resumed. DCHECK(context()); if (context()->IsPullingAudioGraph() || - context()->ContextState() == V8AudioContextState::Enum::kSuspended || - context()->ContextState() == V8AudioContextState::Enum::kInterrupted) { + context()->ContextState() != V8AudioContextState::Enum::kClosed) { context()->GetDeferredTaskHandler().AddRenderingOrphanHandler( std::move(handler_)); } diff --git a/third_party/blink/renderer/modules/webaudio/audio_context_test.cc b/third_party/blink/renderer/modules/webaudio/audio_context_test.cc index 29ae192b..c09deed 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_context_test.cc +++ b/third_party/blink/renderer/modules/webaudio/audio_context_test.cc @@ -518,6 +518,60 @@ EXPECT_FALSE(handler->HasOneRef()); } +TEST_F(AudioContextTest, DisposeOrphansHandlerWhenRunningWithoutPulling) { + AudioContextOptions* options = AudioContextOptions::Create(); + AudioContext* context = AudioContext::Create( + GetFrame().DomWindow(), options, ASSERT_NO_EXCEPTION); + ExpectContextBecomesRunningAsync(context); + ExpectContextRunning(context); + + RealtimeAudioDestinationHandler* dest_handler = + &context->destinationNode()->GetAudioDestinationHandler(); + FakeAudioThread audio_thread(ThreadType::kRealtimeAudioWorkletThread); + { + DelayNode* node = context->createDelay(ASSERT_NO_EXCEPTION); + node->connect(context->destinationNode(), 0, 0, ASSERT_NO_EXCEPTION); + + // Run a render cycle so the summing junction updates its rendering state. + audio_thread.RunOnAudioThreadWithContext( + context, + CrossThreadBindOnce( + [](RealtimeAudioDestinationHandler* handler) { + const AudioIOPosition pos{0, 0, 0}; + const AudioCallbackMetric metric; + scoped_refptr<AudioBus> bus = + AudioBus::Create(2, 128, /*create_memory=*/true); + handler->Render(bus.get(), 128, pos, metric, base::TimeDelta(), + {}); + }, + CrossThreadUnretained(dest_handler))); + } + + // Stop pulling the graph while the context state remains kRunning. + dest_handler->StopRendering(); + ASSERT_EQ(context->ContextState(), V8AudioContextState::Enum::kRunning); + ASSERT_FALSE(context->IsPullingAudioGraph()); + + WebHeap::CollectAllGarbageForTesting(); + + // Resume rendering and verify the destination renders cleanly while the + // graph lock is held on the main thread. + dest_handler->StartRendering(); + { + DeferredTaskHandler::GraphAutoLocker locker( + context->GetDeferredTaskHandler()); + audio_thread.RunOnAudioThread(CrossThreadBindOnce( + [](RealtimeAudioDestinationHandler* handler) { + const AudioIOPosition pos{0, 0, 0}; + const AudioCallbackMetric metric; + scoped_refptr<AudioBus> bus = + AudioBus::Create(2, 128, /*create_memory=*/true); + handler->Render(bus.get(), 128, pos, metric, base::TimeDelta(), {}); + }, + CrossThreadUnretained(dest_handler))); + } +} + TEST_F(AudioContextTest, AudioContextOptions_WebAudioLatencyHint) { AudioContextOptions* interactive_options = AudioContextOptions::Create(); interactive_options->setLatencyHint(
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/renderer/modules/webaudio/audio_context_test.cc b/third_party/blink/renderer/modules/webaudio/audio_context_test.cc
index 29ae192b..c09deed 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_context_test.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_context_test.cc
@@ -518,6 +518,60 @@
EXPECT_FALSE(handler->HasOneRef());
}
+TEST_F(AudioContextTest, DisposeOrphansHandlerWhenRunningWithoutPulling) {
+ AudioContextOptions* options = AudioContextOptions::Create();
+ AudioContext* context = AudioContext::Create(
+ GetFrame().DomWindow(), options, ASSERT_NO_EXCEPTION);
+ ExpectContextBecomesRunningAsync(context);
+ ExpectContextRunning(context);
+
+ RealtimeAudioDestinationHandler* dest_handler =
+ &context->destinationNode()->GetAudioDestinationHandler();
+ FakeAudioThread audio_thread(ThreadType::kRealtimeAudioWorkletThread);
+ {
+ DelayNode* node = context->createDelay(ASSERT_NO_EXCEPTION);
+ node->connect(context->destinationNode(), 0, 0, ASSERT_NO_EXCEPTION);
+
+ // Run a render cycle so the summing junction updates its rendering state.
+ audio_thread.RunOnAudioThreadWithContext(
+ context,
+ CrossThreadBindOnce(
+ [](RealtimeAudioDestinationHandler* handler) {
+ const AudioIOPosition pos{0, 0, 0};
+ const AudioCallbackMetric metric;
+ scoped_refptr<AudioBus> bus =
+ AudioBus::Create(2, 128, /*create_memory=*/true);
+ handler->Render(bus.get(), 128, pos, metric, base::TimeDelta(),
+ {});
+ },
+ CrossThreadUnretained(dest_handler)));
+ }
+
+ // Stop pulling the graph while the context state remains kRunning.
+ dest_handler->StopRendering();
+ ASSERT_EQ(context->ContextState(), V8AudioContextState::Enum::kRunning);
+ ASSERT_FALSE(context->IsPullingAudioGraph());
+
+ WebHeap::CollectAllGarbageForTesting();
+
+ // Resume rendering and verify the destination renders cleanly while the
+ // graph lock is held on the main thread.
+ dest_handler->StartRendering();
+ {
+ DeferredTaskHandler::GraphAutoLocker locker(
+ context->GetDeferredTaskHandler());
+ audio_thread.RunOnAudioThread(CrossThreadBindOnce(
+ [](RealtimeAudioDestinationHandler* handler) {
+ const AudioIOPosition pos{0, 0, 0};
+ const AudioCallbackMetric metric;
+ scoped_refptr<AudioBus> bus =
+ AudioBus::Create(2, 128, /*create_memory=*/true);
+ handler->Render(bus.get(), 128, pos, metric, base::TimeDelta(), {});
+ },
+ CrossThreadUnretained(dest_handler)));
+ }
+}
+
TEST_F(AudioContextTest, AudioContextOptions_WebAudioLatencyHint) {
AudioContextOptions* interactive_options = AudioContextOptions::Create();
interactive_options->setLatencyHint(
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