CVE-2026-79224
Overview
Files Changed
chromecast/media/common/audio_decoder_software_wrapper.ccchromecast/media/common/audio_decoder_software_wrapper.h
Patch
From 23cc22d65ef44403a48a2218e64a584267437ff2 Mon Sep 17 00:00:00 2001 From: Simeon Anfinrud <[email protected]> Date: Tue, 14 Jul 2026 11:06:04 -0700 Subject: [PATCH] [chromecast] Use WeakPtr for AudioDecoderSoftwareWrapper decode callback This resolves a potential Use-After-Free by replacing base::Unretained(this) with a WeakPtr in the callback passed to CastAudioDecoder::Decode. If AudioDecoderSoftwareWrapper is destroyed while a decode is pending, the callback will safely be a no-op instead of executing on a deleted pointer. BUG=516988476 TEST=Compiled and passed unit tests. Change-Id: I7d973d2cc3a19465ae56b52be6d8223f17dc7c1b Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8077607 Auto-Submit: Simeon Anfinrud <[email protected]> Reviewed-by: Shawn Quereshi <[email protected]> Commit-Queue: Shawn Quereshi <[email protected]> Cr-Commit-Position: refs/heads/main@{#1662004} --- diff --git a/chromecast/media/common/audio_decoder_software_wrapper.cc b/chromecast/media/common/audio_decoder_software_wrapper.cc index e1e9bd4..6fa24cb 100644 --- a/chromecast/media/common/audio_decoder_software_wrapper.cc +++ b/chromecast/media/common/audio_decoder_software_wrapper.cc @@ -70,7 +70,7 @@ software_decoder_->Decode( base::WrapRefCounted(buffer_base), base::BindOnce(&AudioDecoderSoftwareWrapper::OnDecodedBuffer, - base::Unretained(this))); + weak_factory_.GetWeakPtr())); return MediaPipelineBackend::kBufferPending; } diff --git a/chromecast/media/common/audio_decoder_software_wrapper.h b/chromecast/media/common/audio_decoder_software_wrapper.h index acb1b65..e76a71b1 100644 --- a/chromecast/media/common/audio_decoder_software_wrapper.h +++ b/chromecast/media/common/audio_decoder_software_wrapper.h @@ -11,6 +11,7 @@ #include <string> #include "base/memory/scoped_refptr.h" +#include "base/memory/weak_ptr.h" #include "chromecast/media/api/cast_audio_decoder.h" #include "chromecast/public/media/media_pipeline_backend.h" @@ -68,6 +69,8 @@ AudioConfig output_config_; scoped_refptr<DecoderBufferBase> pending_pushed_buffer_; bool decoder_error_; + + base::WeakPtrFactory<AudioDecoderSoftwareWrapper> weak_factory_{this}; }; } // namespace media
Original Bug Report
Potential Use-After-Free in AudioDecoderSoftwareWrapper during Media Pipeline Revocation
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A potential Use-After-Free (UAF) vulnerability exists in Chromecast’s audio decoding pipeline during a pipeline revocation operation. When software-decoded buffers are freed via the destruction of AudioDecoderSoftwareWrapper, the underlying vendor MediaPipelineBackend may still hold and dereference a dangling raw pointer to the buffer. This can potentially lead to virtual method table hijacking and out-of-bounds execution in the unsandboxed Cast browser process.
Affected files:
chromecast/media/common/audio_decoder_software_wrapper.ccchromecast/media/common/media_pipeline_backend_wrapper.ccchromecast/media/common/audio_decoder_wrapper.cc
Estimated timestamp from git blame: 2018-11-28
Description
A potential Use-After-Free (UAF) memory corruption vulnerability has been identified in the Chromecast media pipeline. When falling back to software audio decoding via AudioDecoderSoftwareWrapper, the pipeline can destroy the software decoder wrapper and release the sole scoped_refptr holding the decoded buffer before stopping or destroying the underlying vendor MediaPipelineBackend. Under the kBufferPending contract, the vendor backend holds a raw pointer to this buffer, creating a window where a dangling raw pointer can be dereferenced.
Root Cause Analysis
-
Software Audio Ingestion: When software audio decoding fallback is active,
AudioDecoderSoftwareWrapper::OnDecodedBufferstores the unique owning reference to the software-decoded PCM buffer in its memberpending_pushed_buffer_(scoped_refptr<DecoderBufferBase>) and passes a raw pointer of this buffer to the vendor-implementedMediaPipelineBackend::AudioDecoderviaPushBuffer:// chromecast/media/common/audio_decoder_software_wrapper.cc pending_pushed_buffer_ = decoded; // sole ref MediaPipelineBackend::BufferStatus buffer_status = backend_decoder_->PushBuffer(pending_pushed_buffer_.get()); // raw ptr to vendorIf the vendor backend returns
MediaPipelineBackend::kBufferPending, it is permitted to retain and asynchronously dereference the raw pointer until it notifies completion viaOnPushBufferComplete()(seechromecast/public/media/cast_decoder_buffer.hspecifications). -
Pipeline Revocation Sequence: When a revocation event occurs (for example, when a new video stream requests resource allocation in
MediaPipelineBackendManager::BackendUseVideoDecoder),MediaPipelineBackendWrapper::Revoke()is executed:// chromecast/media/common/media_pipeline_backend_wrapper.cc void MediaPipelineBackendWrapper::Revoke() { if (!revoked_) { revoked_ = true; if (audio_decoder_) audio_decoder_->Revoke(); // Step 1: Immediately destroys active wrapper if (video_decoder_) video_decoder_->Revoke(); backend_ = std::make_unique<RevokedMediaPipelineBackendWrapper>( content_type_, backend_->GetCurrentPts()); // Step 3: Destructs vendor backend } } -
Premature Buffer Destruction: When
audio_decoder_->Revoke()runs, it immediately replaces the internal active decoder wrapper with a revoked dummy wrapper insideAudioDecoderWrapper::Revoke():// chromecast/media/common/audio_decoder_wrapper.cc void AudioDecoderWrapper::Revoke() { ... audio_decoder_ = std::make_unique<RevokedAudioDecoderWrapper>(...); }This unique_ptr reassignment triggers the immediate destruction of
ActiveAudioDecoderWrapperand its memberAudioDecoderSoftwareWrapper decoder_. This releasespending_pushed_buffer_, and since its reference count drops to 0, the decoded buffer is deallocated immediately. -
The Vulnerability Window: At this point, the real vendor backend (
backend_) is still active and has not been stopped or destroyed. It is only destroyed later when the reassignment ofbackend_occurs in Step 3 ofMediaPipelineBackendWrapper::Revoke(). If the vendor’s destructor or background worker threads attempt to access or clean up the pendingCastDecoderBuffer*pointer, they will dereference freed heap memory. SinceCastDecoderBufferis a polymorphic object with virtual methods (e.g.,data(),data_size(),timestamp()), this can result in virtual method table hijacking.
Potential Attack Scenario
While we do not have a working dynamic proof of concept, a potential exploitation path is analyzed as follows:
- A compromised renderer initiates a
CastRendererwith an audio configuration that forces a fallback to software decoding (AudioDecoderSoftwareWrapper). - The stream pushes buffers such that the vendor backend returns
kBufferPendingin response toPushBuffer(), leaving the vendor holding a raw pointer to the software-decoded buffer. - The renderer triggers a pipeline revocation event (such as starting a second video-only stream that forces reclamation of the video decoder resource).
- During
Revoke(),AudioDecoderSoftwareWrapperis destroyed first, freeing the buffer while the vendor’s background threads or destructor are still active and holding the raw pointer. - When the vendor backend accesses the dangling pointer, control flow is hijacked. Because the media service runs in the context of the unsandboxed Cast browser process, this would lead to a sandbox escape and Remote Code Execution.
Suggested Remediation
To address this vulnerability, ensure that the active audio decoder and its buffered resources are not destroyed before the vendor backend is notified or stopped.
One potential fix is to emulate VideoDecoderWrapper behavior: prevent AudioDecoderWrapper::Revoke() from immediately destroying the underlying ActiveAudioDecoderWrapper and releasing its buffers, and instead let them persist until the outer MediaPipelineBackendWrapper is destroyed. Alternatively, ensure Stop() or a teardown signal is explicitly dispatched to the vendor backend before releasing pending_pushed_buffer_ during the revocation flow.
Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.