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 <hongchan@google.com>
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 <hongchan@google.com>
Reviewed-by: Mahesh Kannan <kmaheshb@google.com>
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