CVE-2026-10924
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchromecast/media/cma/backend/audio_decoder_for_mixer.cc |
modified |
Files Changed
chromecast/media/cma/backend/audio_decoder_for_mixer.ccchromecast/public/media/decoder_config.h
Patch
From 3d50d72b758348e8906070eb1c7b65f7d145d177 Mon Sep 17 00:00:00 2001 From: Simeon Anfinrud <[email protected]> Date: Tue, 28 Apr 2026 16:34:06 -0700 Subject: [PATCH] [chromecast] Fix Renderer Sandbox Escape via Heap Buffer Overflow Add strict bounds checking to AudioConfig::channel_number to prevent integer overflow. Bug: 500055357 Test: Compiled and passed unit tests. Change-Id: I4b49738480ea46bc9ca7f4283ecc5a1fdd963b0e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7765500 Auto-Submit: Simeon Anfinrud <[email protected]> Reviewed-by: Jordan Bayles <[email protected]> Commit-Queue: Simeon Anfinrud <[email protected]> Cr-Commit-Position: refs/heads/main@{#1622106} --- diff --git a/chromecast/media/cma/backend/audio_decoder_for_mixer.cc b/chromecast/media/cma/backend/audio_decoder_for_mixer.cc index bf7cb35..8ac9c45 100644 --- a/chromecast/media/cma/backend/audio_decoder_for_mixer.cc +++ b/chromecast/media/cma/backend/audio_decoder_for_mixer.cc @@ -487,6 +487,11 @@ UpdateStatistics(delta); if (has_config) { + if (!IsValidConfig(config)) { + LOG(ERROR) << "Invalid audio config from decoder"; + delegate_->OnPushBufferComplete(MediaPipelineBackend::kBufferFailed); + return; + } bool changed_config = false; if (config.samples_per_second != decoded_config_.samples_per_second) { LOG(INFO) << "Input sample rate changed from " diff --git a/chromecast/public/media/decoder_config.h b/chromecast/public/media/decoder_config.h index ae5864b7..32949ae2 100644 --- a/chromecast/public/media/decoder_config.h +++ b/chromecast/public/media/decoder_config.h @@ -378,8 +378,9 @@ config.channel_layout != ChannelLayout::UNSUPPORTED && config.sample_format >= kSampleFormatMin && config.sample_format <= kSampleFormatMax && + config.channel_number > 0 && config.channel_number <= 32 && ((config.sample_format != kUnknownSampleFormat && - config.channel_number > 0 && config.bytes_per_channel > 0 && + config.bytes_per_channel > 0 && config.bytes_per_channel <= kMaxBytesPerSample) || config.channel_layout == ChannelLayout::BITSTREAM) && config.samples_per_second > 0 &&
Original Bug Report
Renderer Sandbox Escape via Heap Buffer Overflow in cast_service's AudioDecoderForMixer
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 without the security team.
Overview: A compromised renderer can escape the sandbox on Chromecast devices by sending an unvalidated, malicious AudioConfig to the privileged cast_service process. Setting an extremely large channel_number causes a 32-bit integer overflow during buffer pool allocation, resulting in a heap buffer overflow of attacker-controlled data.
Affected files:
chromecast/media/audio/audio_output_service/receiver/cma_backend_shim.ccchromecast/media/cma/backend/audio_decoder_for_mixer.ccchromecast/media/audio/audio_output_service/receiver/audio_output_service_receiver.ccchromecast/browser/audio_socket_broker.ccchromecast/starboard/media/media/starboard_audio_decoder.ccchromecast/media/common/audio_decoder_software_wrapper.ccchromecast/public/media/decoder_config.h
Estimated timestamp from git blame: 2021-09-03
Summary
A potential vulnerability in the Chromecast cast_service allows a compromised renderer to achieve arbitrary code execution in a privileged context. The issue stems from an IPC validation bypass where audio configuration parameters received via a raw socket are used in memory-sensitive operations without proper bounds checking. This leads to a controllable heap buffer overflow in the media backend on 32-bit platforms.
Vulnerability Details
1. IPC Path and Validation Bypass
The mojom::AudioSocketBroker interface allows renderers to obtain a raw Unix domain socket pair to communicate with the AudioOutputServiceReceiver in the highly privileged, unsandboxed cast_service process.
Messages sent over this socket are parsed as audio_output_service::Generic protobufs in AudioOutputServiceReceiver::Stream::HandleMetadata. The unvalidated protobuf parameters are forwarded to CmaBackendShim::SetAudioConfig, where fields like num_channels are copied directly into a media::AudioConfig object. This raw socket IPC path entirely bypasses the strict media::AudioDecoderConfig Mojo StructTraits validation used in standard media pipelines.
2. Integer Overflow leading to Heap Buffer Overflow
The malicious configuration reaches AudioDecoderForMixer::SetConfig and bypasses the IsValidConfig(config) check, which merely enforces that channel_number > 0.
When playback begins via a SetStartTimestamp message, the audio decoder initializes an IOBufferPool with a default buffer_pool_frames_ value of 512. In CreateBufferPool, the allocation size is calculated as:
frame_count * sizeof(float) * config.channel_number + kAudioMessageHeaderSize
On a 32-bit architecture (common for Chromecast targets), an attacker-supplied channel_number of 0x4000001F (1073741855) causes the size_t multiplication 512 * 4 * 1073741855 to overflow modulo 2^32, wrapping around to exactly 63488. The buffer is allocated with a capacity of 63488 + 16 = 63504 bytes via base::AlignedAlloc (which is routed to PartitionAlloc).
3. Execution of the Overflow
The attacker then sends a kAudio message payload of exactly 63611 bytes over the socket (which fits within the 64KB SmallMessageSocket limit).
In AudioDecoderForMixer::WritePcm, the local frame_size is calculated as sizeof(float) * channel_number. The intermediate size_t multiplication (4 * 1073741855) overflows to 124 and is assigned to the signed const int frame_size.
The code computes frame_count = buffer->data_size() / frame_size using integer division (63611 / 124 = 512). It then checks if the buffer needs resizing:
if (frame_count > buffer_pool_frames_) {
CreateBufferPool(decoded_config_, frame_count * 2);
}
Because 512 > 512 is false, it uses the existing undersized buffer pool.
Finally, the code copies the attacker’s payload:
memcpy(io_buffer->data() + kAudioMessageHeaderSize, buffer->data(), buffer->data_size());
Writing 63611 bytes starting at offset 16 requires 63627 bytes of space. Since the buffer’s data area is only 63504 bytes, this results in a precise, synchronous 123-byte heap buffer overflow of pure attacker-controlled data into adjacent PartitionAlloc objects.
Potential Exploitation Steps
- Compromise the renderer process and bind
mojom::AudioSocketBroker. - Retrieve a raw socket descriptor and establish communication with
cast_service. - Send a
kMetadataprotobuf message settingnum_channelsto0x4000001Fandaudio_codectokCodecPCM. - Send a
SetStartTimestampmessage to initialize playback and the undersized buffer pool. - Immediately send a
kAudiobinary payload of63611bytes containing the RCE payload (ROP chain, vtable hijack, etc.). - The
cast_servicemedia thread synchronously executes the overflow before any asynchronous mixer checks occur, triggering arbitrary code execution.
(Note: These steps are based on theoretical static analysis; a working Proof-of-Concept has not been run.)
Suggested Fix
- Strict IPC Validation: Sanitize the
CmaBackendParamsprotobuf inHandleMetadataorCmaBackendShim.num_channelsmust be validated againstmedia::limits::kMaxChannels. - Safe Arithmetic: Use
base::CheckedNumericfor all buffer size andframe_sizecalculations inAudioDecoderForMixerandCreateBufferPoolto prevent integer overflows.
Evaluated with Chrome root at commit: f200f57a19490707ff8bc7aa5de3cbc443a3afad
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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.