Chrome · Media › Media
CVE-2026-106184
Uninitialized Memory in Media (Media)
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/platform/media/web_audio_source_provider_impl.cc |
modified | |
WebAudioSourceProviderImplTestthird_party/blink/renderer/platform/media/web_audio_source_provider_impl_test.cc |
modified | |
TEST_Fthird_party/blink/renderer/platform/media/web_audio_source_provider_impl_test.cc |
modified |
Files Changed
third_party/blink/renderer/platform/media/web_audio_source_provider_impl_test.ccthird_party/blink/renderer/platform/media/web_audio_source_provider_impl.cc
Patch
From 2048cd07495ea84d4c20b54d4f0f49f7124e603e Mon Sep 17 00:00:00 2001
From: Thomas Guilbert <tguilbert@chromium.org>
Date: Wed, 30 Sep 2026 12:12:37 -0700
Subject: [PATCH] [Media] Zero unfilled frames in TeeFilter::Render()
Make sure frames not written by the renderer are cleared before the bus
is used, rather than relying on callers to do it. See bug for details.
TAG=agy
CONV=1c37ed3b-c42c-4f78-a468-c9f88041c34b
Fixed: 567379988
Test: WebAudioSourceProviderImplTest.CopyAudioCBZeroesUnrenderedFrames
Change-Id: I388d3f48b30b718659c73b4440d3e4f9f5f9af69
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8488289
Reviewed-by: Hongchan Choi <hongchan@google.com>
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Auto-Submit: Thomas Guilbert <tguilbert@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1708380}
---
diff --git a/third_party/blink/renderer/platform/media/web_audio_source_provider_impl.cc b/third_party/blink/renderer/platform/media/web_audio_source_provider_impl.cc
index 211c700..74d24e43 100644
--- a/third_party/blink/renderer/platform/media/web_audio_source_provider_impl.cc
+++ b/third_party/blink/renderer/platform/media/web_audio_source_provider_impl.cc
@@ -57,6 +57,18 @@
const int num_rendered_frames =
renderer_->Render(delay, delay_timestamp, glitch_info, audio_bus);
+ // Bitstream (passthrough) formats are not supported here.
+ CHECK(!audio_bus->is_bitstream_format());
+
+ // `Render()` only guarantees that the first `num_rendered_frames` frames
+ // are written. The rest of `audio_bus` may hold stale data from buffers
+ // reused by the caller (e.g. `AudioConverter`), so zero it before it is
+ // copied below or consumed downstream.
+ if (num_rendered_frames < audio_bus->frames()) {
+ audio_bus->ZeroFramesPartial(num_rendered_frames,
+ audio_bus->frames() - num_rendered_frames);
+ }
+
// Avoid taking the copy lock for the vast majority of cases.
if (copy_required_) {
base::AutoLock auto_lock(copy_lock_);
@@ -211,8 +223,8 @@
// TODO(fhernqvist): If we need glitches propagated through WebAudio, plumb
// them through here.
- const int frames = tee_filter_->Render(
- base::TimeDelta(), base::TimeTicks::Now(), {}, bus_wrapper_.get());
+ tee_filter_->Render(base::TimeDelta(), base::TimeTicks::Now(), {},
+ bus_wrapper_.get());
// Zero out frames after rendering for tainted origins.
if (tee_filter_->is_tainted()) {
@@ -220,9 +232,6 @@
return;
}
- if (frames < number_of_frames)
- bus_wrapper_->ZeroFramesPartial(frames, number_of_frames - frames);
-
bus_wrapper_->Scale(volume_);
}
diff --git a/third_party/blink/renderer/platform/media/web_audio_source_provider_impl_test.cc b/third_party/blink/renderer/platform/media/web_audio_source_provider_impl_test.cc
index f28bfec..ca0688c 100644
--- a/third_party/blink/renderer/platform/media/web_audio_source_provider_impl_test.cc
+++ b/third_party/blink/renderer/platform/media/web_audio_source_provider_impl_test.cc
@@ -6,7 +6,10 @@
#include <stddef.h>
+#include <algorithm>
+#include <memory>
#include <ranges>
+#include <tuple>
#include "base/compiler_specific.h"
#include "base/containers/span.h"
@@ -14,6 +17,7 @@
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_glitch_info.h"
#include "media/base/audio_parameters.h"
@@ -47,6 +51,15 @@
return channel_spans;
}
+// Returns true if every frame at or after `start_frame` is zero.
+bool AreTrailingFramesZero(const media::AudioBus* bus, int start_frame) {
+ return std::ranges::all_of(bus->AllChannels(), [start_frame](auto channel) {
+ return std::ranges::all_of(
+ channel.subspan(static_cast<size_t>(start_frame)),
+ [](float sample) { return sample == 0.0f; });
+ });
+}
+
} // namespace
class WebAudioSourceProviderImplTest : public testing::Test,
@@ -357,6 +370,36 @@
testing::Mock::VerifyAndClear(mock_sink_.get());
}
+// Verify that frames past those written by the renderer are zeroed, both in
+// the bus passed to CopyAudioCB and in the rendered bus.
+TEST_F(WebAudioSourceProviderImplTest, CopyAudioCBZeroesUnrenderedFrames) {
+ constexpr float kStaleSampleValue = 1.0f;
+
+ wasp_impl_->Initialize(params_, &fake_callback_);
+
+ base::test::TestFuture<std::unique_ptr<media::AudioBus>, uint32_t, int>
+ copy_future;
+ wasp_impl_->SetCopyAudioCallback(copy_future.GetRepeatingCallback());
+
+ // Simulate a bus that still contains samples from another source.
+ const auto bus = media::AudioBus::Create(params_);
+ for (auto channel : bus->AllChannels()) {
+ std::ranges::fill(channel, kStaleSampleValue);
+ }
+
+ fake_callback_.set_half_fill(true);
+ const int frames_rendered = Render(bus.get());
+ ASSERT_EQ(frames_rendered, bus->frames() / 2);
+
+ std::unique_ptr<media::AudioBus> bus_copy = std::get<0>(copy_future.Take());
+ ASSERT_TRUE(bus_copy);
+ EXPECT_TRUE(AreTrailingFramesZero(bus_copy.get(), frames_rendered));
+ EXPECT_TRUE(AreTrailingFramesZero(bus.get(), frames_rendered));
+
+ wasp_impl_->ClearCopyAudioCallback();
+ testing::Mock::VerifyAndClear(mock_sink_.get());
+}
+
TEST_F(WebAudioSourceProviderImplTest, MultipleInitializeWithSetClient) {
// setClient() with a nullptr client should do nothing if no client is set.
wasp_impl_->SetClient(nullptr);
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/renderer/platform/media/web_audio_source_provider_impl_test.cc b/third_party/blink/renderer/platform/media/web_audio_source_provider_impl_test.cc
index f28bfec..ca0688c 100644
--- a/third_party/blink/renderer/platform/media/web_audio_source_provider_impl_test.cc
+++ b/third_party/blink/renderer/platform/media/web_audio_source_provider_impl_test.cc
@@ -6,7 +6,10 @@
#include <stddef.h>
+#include <algorithm>
+#include <memory>
#include <ranges>
+#include <tuple>
#include "base/compiler_specific.h"
#include "base/containers/span.h"
@@ -14,6 +17,7 @@
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_glitch_info.h"
#include "media/base/audio_parameters.h"
@@ -47,6 +51,15 @@
return channel_spans;
}
+// Returns true if every frame at or after `start_frame` is zero.
+bool AreTrailingFramesZero(const media::AudioBus* bus, int start_frame) {
+ return std::ranges::all_of(bus->AllChannels(), [start_frame](auto channel) {
+ return std::ranges::all_of(
+ channel.subspan(static_cast<size_t>(start_frame)),
+ [](float sample) { return sample == 0.0f; });
+ });
+}
+
} // namespace
class WebAudioSourceProviderImplTest : public testing::Test,
@@ -357,6 +370,36 @@
testing::Mock::VerifyAndClear(mock_sink_.get());
}
+// Verify that frames past those written by the renderer are zeroed, both in
+// the bus passed to CopyAudioCB and in the rendered bus.
+TEST_F(WebAudioSourceProviderImplTest, CopyAudioCBZeroesUnrenderedFrames) {
+ constexpr float kStaleSampleValue = 1.0f;
+
+ wasp_impl_->Initialize(params_, &fake_callback_);
+
+ base::test::TestFuture<std::unique_ptr<media::AudioBus>, uint32_t, int>
+ copy_future;
+ wasp_impl_->SetCopyAudioCallback(copy_future.GetRepeatingCallback());
+
+ // Simulate a bus that still contains samples from another source.
+ const auto bus = media::AudioBus::Create(params_);
+ for (auto channel : bus->AllChannels()) {
+ std::ranges::fill(channel, kStaleSampleValue);
+ }
+
+ fake_callback_.set_half_fill(true);
+ const int frames_rendered = Render(bus.get());
+ ASSERT_EQ(frames_rendered, bus->frames() / 2);
+
+ std::unique_ptr<media::AudioBus> bus_copy = std::get<0>(copy_future.Take());
+ ASSERT_TRUE(bus_copy);
+ EXPECT_TRUE(AreTrailingFramesZero(bus_copy.get(), frames_rendered));
+ EXPECT_TRUE(AreTrailingFramesZero(bus.get(), frames_rendered));
+
+ wasp_impl_->ClearCopyAudioCallback();
+ testing::Mock::VerifyAndClear(mock_sink_.get());
+}
+
TEST_F(WebAudioSourceProviderImplTest, MultipleInitializeWithSetClient) {
// setClient() with a nullptr client should do nothing if no client is set.
wasp_impl_->SetClient(nullptr);
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