Overview

Critical
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Chromecast
DescriptionUse after free in Chromecast
ComponentChromecast
Bug ClassUAF
Tracker516988476
Fix commit23cc22d65ef4 (chromium/src) +4/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Files Changed

  • chromecast/media/common/audio_decoder_software_wrapper.cc
  • chromecast/media/common/audio_decoder_software_wrapper.h
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
Loading diff…

Original Bug Report

reported by [email protected]

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.cc
  • chromecast/media/common/media_pipeline_backend_wrapper.cc
  • chromecast/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

  1. Software Audio Ingestion: When software audio decoding fallback is active, AudioDecoderSoftwareWrapper::OnDecodedBuffer stores the unique owning reference to the software-decoded PCM buffer in its member pending_pushed_buffer_ (scoped_refptr<DecoderBufferBase>) and passes a raw pointer of this buffer to the vendor-implemented MediaPipelineBackend::AudioDecoder via PushBuffer:

    // 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 vendor
    

    If the vendor backend returns MediaPipelineBackend::kBufferPending, it is permitted to retain and asynchronously dereference the raw pointer until it notifies completion via OnPushBufferComplete() (see chromecast/public/media/cast_decoder_buffer.h specifications).

  2. 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
      }
    }
    
  3. Premature Buffer Destruction: When audio_decoder_->Revoke() runs, it immediately replaces the internal active decoder wrapper with a revoked dummy wrapper inside AudioDecoderWrapper::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 ActiveAudioDecoderWrapper and its member AudioDecoderSoftwareWrapper decoder_. This releases pending_pushed_buffer_, and since its reference count drops to 0, the decoded buffer is deallocated immediately.

  4. 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 of backend_ occurs in Step 3 of MediaPipelineBackendWrapper::Revoke(). If the vendor’s destructor or background worker threads attempt to access or clean up the pending CastDecoderBuffer* pointer, they will dereference freed heap memory. Since CastDecoderBuffer is 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:

  1. A compromised renderer initiates a CastRenderer with an audio configuration that forces a fallback to software decoding (AudioDecoderSoftwareWrapper).
  2. The stream pushes buffers such that the vendor backend returns kBufferPending in response to PushBuffer(), leaving the vendor holding a raw pointer to the software-decoded buffer.
  3. The renderer triggers a pipeline revocation event (such as starting a second video-only stream that forces reclamation of the video decoder resource).
  4. During Revoke(), AudioDecoderSoftwareWrapper is destroyed first, freeing the buffer while the vendor’s background threads or destructor are still active and holding the raw pointer.
  5. 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.

View on issue tracker