From 063d00967b355feb64e7ae502da9073cfcef5df9 Mon Sep 17 00:00:00 2001 From: Kyujung Youn Date: Tue, 10 Feb 2026 11:16:46 -0800 Subject: [PATCH 1/4] cobalt/media/audio: fix heap-use-after-free in CobaltAudioRendererSink Use std::unique_ptr with a custom deleter for SbAudioSink to ensure the audio sink is properly destroyed when CobaltAudioRendererSink is destroyed. This prevents background ALSA threads from accessing the freed sink object. Fixes an ASAN error where UpdateSourceStatus was called on a freed CobaltAudioRendererSink instance. Bug: 483384414 Issue: 483384414 --- .../media/audio/cobalt_audio_renderer_sink.cc | 43 +++++++++++-------- .../media/audio/cobalt_audio_renderer_sink.h | 9 +++- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/cobalt/media/audio/cobalt_audio_renderer_sink.cc b/cobalt/media/audio/cobalt_audio_renderer_sink.cc index 741af98d16c5..c3bb6eb0dbb9 100644 --- a/cobalt/media/audio/cobalt_audio_renderer_sink.cc +++ b/cobalt/media/audio/cobalt_audio_renderer_sink.cc @@ -31,6 +31,12 @@ int AlignUp(int value, int alignment) { } } // namespace +void SbAudioSinkDeleter::operator()(SbAudioSink sink) const { + if (SbAudioSinkIsValid(sink)) { + SbAudioSinkDestroy(sink); + } +} + void CobaltAudioRendererSink::Initialize(const AudioParameters& params, RenderCallback* callback) { LOG(INFO) << "CobaltAudioRendererSink::Initialize - called with following " @@ -78,17 +84,19 @@ void CobaltAudioRendererSink::Start() { } output_frame_buffers_[0] = output_frame_buffer_.get(); - audio_sink_ = SbAudioSinkCreate( - params_.channels(), nearest_supported_sample_rate_, output_sample_type_, - kSbMediaAudioFrameStorageTypeInterleaved, &output_frame_buffers_[0], - frames_per_channel_, &CobaltAudioRendererSink::UpdateSourceStatusFunc, - &CobaltAudioRendererSink::ConsumeFramesFunc, this); - DCHECK(SbAudioSinkIsValid(audio_sink_)); + audio_sink_ = + std::unique_ptr(SbAudioSinkCreate( + params_.channels(), nearest_supported_sample_rate_, + output_sample_type_, kSbMediaAudioFrameStorageTypeInterleaved, + &output_frame_buffers_[0], frames_per_channel_, + &CobaltAudioRendererSink::UpdateSourceStatusFunc, + &CobaltAudioRendererSink::ConsumeFramesFunc, /*context=*/this)); + CHECK(audio_sink_); } void CobaltAudioRendererSink::Stop() { is_eos_reached_.store(true); - SbAudioSinkDestroy(audio_sink_); + audio_sink_.reset(); callback_ = nullptr; } @@ -101,22 +109,23 @@ void CobaltAudioRendererSink::Pause() { } void CobaltAudioRendererSink::Flush() { - if (!SbAudioSinkIsValid(audio_sink_)) { + if (!audio_sink_) { return; } // TODO(b/390468794) consolidate this recreation as it duplicates code from // Stop() and Start() - SbAudioSinkDestroy(audio_sink_); + audio_sink_.reset(); frames_rendered_ = 0; frames_consumed_ = 0; - audio_sink_ = SbAudioSinkCreate( - params_.channels(), - SbAudioSinkGetNearestSupportedSampleFrequency(params_.sample_rate()), - output_sample_type_, kSbMediaAudioFrameStorageTypeInterleaved, - &output_frame_buffers_[0], frames_per_channel_, - &CobaltAudioRendererSink::UpdateSourceStatusFunc, - &CobaltAudioRendererSink::ConsumeFramesFunc, this); - DCHECK(SbAudioSinkIsValid(audio_sink_)); + audio_sink_ = + std::unique_ptr(SbAudioSinkCreate( + params_.channels(), + SbAudioSinkGetNearestSupportedSampleFrequency(params_.sample_rate()), + output_sample_type_, kSbMediaAudioFrameStorageTypeInterleaved, + &output_frame_buffers_[0], frames_per_channel_, + &CobaltAudioRendererSink::UpdateSourceStatusFunc, + &CobaltAudioRendererSink::ConsumeFramesFunc, /*context=*/this)); + CHECK(audio_sink_); } bool CobaltAudioRendererSink::SetVolume(double volume) { diff --git a/cobalt/media/audio/cobalt_audio_renderer_sink.h b/cobalt/media/audio/cobalt_audio_renderer_sink.h index ef62f3bdd8b7..31e86a143c94 100644 --- a/cobalt/media/audio/cobalt_audio_renderer_sink.h +++ b/cobalt/media/audio/cobalt_audio_renderer_sink.h @@ -16,6 +16,7 @@ #define COBALT_MEDIA_AUDIO_COBALT_AUDIO_RENDERER_SINK_H_ #include +#include #include "media/base/audio_renderer_sink.h" #include "media/base/media_export.h" @@ -24,6 +25,12 @@ namespace media { +// Custom deleter for Starboard Audio Sink. +struct SbAudioSinkDeleter { + using pointer = SbAudioSink; + void operator()(SbAudioSink sink) const; +}; + class MEDIA_EXPORT CobaltAudioRendererSink final : public AudioRendererSink { public: CobaltAudioRendererSink() = default; @@ -84,7 +91,7 @@ class MEDIA_EXPORT CobaltAudioRendererSink final : public AudioRendererSink { std::atomic is_playing_ = false; std::atomic is_eos_reached_ = false; - SbAudioSink audio_sink_ = kSbAudioSinkInvalid; + std::unique_ptr audio_sink_; double resample_ratio_ = 1.0; std::unique_ptr resampler_ = nullptr; From e2fe4f60ceba53981a44b5d870a14b114e08f351 Mon Sep 17 00:00:00 2001 From: Kyujung Youn Date: Tue, 10 Feb 2026 11:33:40 -0800 Subject: [PATCH 2/4] add unique ptr alias --- .../media/audio/cobalt_audio_renderer_sink.cc | 27 +++++++++---------- .../media/audio/cobalt_audio_renderer_sink.h | 3 ++- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/cobalt/media/audio/cobalt_audio_renderer_sink.cc b/cobalt/media/audio/cobalt_audio_renderer_sink.cc index c3bb6eb0dbb9..9ee06e63b1c9 100644 --- a/cobalt/media/audio/cobalt_audio_renderer_sink.cc +++ b/cobalt/media/audio/cobalt_audio_renderer_sink.cc @@ -84,13 +84,11 @@ void CobaltAudioRendererSink::Start() { } output_frame_buffers_[0] = output_frame_buffer_.get(); - audio_sink_ = - std::unique_ptr(SbAudioSinkCreate( - params_.channels(), nearest_supported_sample_rate_, - output_sample_type_, kSbMediaAudioFrameStorageTypeInterleaved, - &output_frame_buffers_[0], frames_per_channel_, - &CobaltAudioRendererSink::UpdateSourceStatusFunc, - &CobaltAudioRendererSink::ConsumeFramesFunc, /*context=*/this)); + audio_sink_ = AudioSinkUniquePtr(SbAudioSinkCreate( + params_.channels(), nearest_supported_sample_rate_, output_sample_type_, + kSbMediaAudioFrameStorageTypeInterleaved, &output_frame_buffers_[0], + frames_per_channel_, &CobaltAudioRendererSink::UpdateSourceStatusFunc, + &CobaltAudioRendererSink::ConsumeFramesFunc, /*context=*/this)); CHECK(audio_sink_); } @@ -117,14 +115,13 @@ void CobaltAudioRendererSink::Flush() { audio_sink_.reset(); frames_rendered_ = 0; frames_consumed_ = 0; - audio_sink_ = - std::unique_ptr(SbAudioSinkCreate( - params_.channels(), - SbAudioSinkGetNearestSupportedSampleFrequency(params_.sample_rate()), - output_sample_type_, kSbMediaAudioFrameStorageTypeInterleaved, - &output_frame_buffers_[0], frames_per_channel_, - &CobaltAudioRendererSink::UpdateSourceStatusFunc, - &CobaltAudioRendererSink::ConsumeFramesFunc, /*context=*/this)); + audio_sink_ = AudioSinkUniquePtr(SbAudioSinkCreate( + params_.channels(), + SbAudioSinkGetNearestSupportedSampleFrequency(params_.sample_rate()), + output_sample_type_, kSbMediaAudioFrameStorageTypeInterleaved, + &output_frame_buffers_[0], frames_per_channel_, + &CobaltAudioRendererSink::UpdateSourceStatusFunc, + &CobaltAudioRendererSink::ConsumeFramesFunc, /*context=*/this)); CHECK(audio_sink_); } diff --git a/cobalt/media/audio/cobalt_audio_renderer_sink.h b/cobalt/media/audio/cobalt_audio_renderer_sink.h index 31e86a143c94..eb490fbdd4b3 100644 --- a/cobalt/media/audio/cobalt_audio_renderer_sink.h +++ b/cobalt/media/audio/cobalt_audio_renderer_sink.h @@ -91,7 +91,8 @@ class MEDIA_EXPORT CobaltAudioRendererSink final : public AudioRendererSink { std::atomic is_playing_ = false; std::atomic is_eos_reached_ = false; - std::unique_ptr audio_sink_; + using AudioSinkUniquePtr = std::unique_ptr; + AudioSinkUniquePtr audio_sink_; double resample_ratio_ = 1.0; std::unique_ptr resampler_ = nullptr; From b34dad82c4d58e439c805e82276575be3ed73a3c Mon Sep 17 00:00:00 2001 From: Kyujung Youn Date: Tue, 10 Feb 2026 12:46:18 -0800 Subject: [PATCH 3/4] minor change: fast-fail in deleter --- cobalt/media/audio/cobalt_audio_renderer_sink.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cobalt/media/audio/cobalt_audio_renderer_sink.cc b/cobalt/media/audio/cobalt_audio_renderer_sink.cc index 9ee06e63b1c9..6235341f0320 100644 --- a/cobalt/media/audio/cobalt_audio_renderer_sink.cc +++ b/cobalt/media/audio/cobalt_audio_renderer_sink.cc @@ -32,9 +32,11 @@ int AlignUp(int value, int alignment) { } // namespace void SbAudioSinkDeleter::operator()(SbAudioSink sink) const { - if (SbAudioSinkIsValid(sink)) { - SbAudioSinkDestroy(sink); + if (!SbAudioSinkIsValid(sink)) { + return; } + + SbAudioSinkDestroy(sink); } void CobaltAudioRendererSink::Initialize(const AudioParameters& params, From ef6ba9461440921f36a486c37bf221517514a8c8 Mon Sep 17 00:00:00 2001 From: Kyujung Youn Date: Fri, 13 Feb 2026 06:48:50 -0800 Subject: [PATCH 4/4] Remove unnecessary invalid check --- cobalt/media/audio/cobalt_audio_renderer_sink.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cobalt/media/audio/cobalt_audio_renderer_sink.cc b/cobalt/media/audio/cobalt_audio_renderer_sink.cc index 6235341f0320..868835ef6009 100644 --- a/cobalt/media/audio/cobalt_audio_renderer_sink.cc +++ b/cobalt/media/audio/cobalt_audio_renderer_sink.cc @@ -32,10 +32,6 @@ int AlignUp(int value, int alignment) { } // namespace void SbAudioSinkDeleter::operator()(SbAudioSink sink) const { - if (!SbAudioSinkIsValid(sink)) { - return; - } - SbAudioSinkDestroy(sink); }