CVE-2026-13796
Overview
Files Changed
chromecast/media/audio/net/audio_socket.cc
Patch
From 6e070afc3d3e59fdfcfdf09656ab9ff3dbca25bb Mon Sep 17 00:00:00 2001
From: Simeon Anfinrud <sanfin@chromium.org>
Date: Mon, 11 May 2026 20:52:37 -0700
Subject: [PATCH] [chromecast] Fix Potential Integer Underflow and OOB Write in AudioSocket::ParseAudio
Adds an explicit bounds check to ensure size >= sizeof(int32_t) before
processing padding bytes. This prevents an integer underflow that bypasses
later bounds checks.
Bug: 491894115
Test: Compiled and passed unit tests.
Change-Id: I57d2c71e9276cfee6229483b1e3825baf07ccfd3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7765504
Auto-Submit: Simeon Anfinrud <sanfin@chromium.org>
Commit-Queue: Sandeep Vijayasekar <sandv@google.com>
Reviewed-by: Sandeep Vijayasekar <sandv@google.com>
Cr-Commit-Position: refs/heads/main@{#1629038}
---
diff --git a/chromecast/media/audio/net/audio_socket.cc b/chromecast/media/audio/net/audio_socket.cc
index 8c42dd8..babacee 100644
--- a/chromecast/media/audio/net/audio_socket.cc
+++ b/chromecast/media/audio/net/audio_socket.cc
@@ -349,7 +349,7 @@
bool AudioSocket::ParseAudio(char* data, size_t size) {
int64_t timestamp;
if (size < sizeof(timestamp)) {
- LOG(ERROR) << "Invalid audio packet size " << size << " from " << this;
+ LOG(ERROR) << "Invalid audio buffer size " << size << " from " << this;
delegate_->OnConnectionError();
return false;
}
@@ -359,6 +359,11 @@
size -= sizeof(timestamp);
// Handle padding bytes.
+ if (size < sizeof(int32_t)) {
+ LOG(ERROR) << "Invalid audio buffer size " << size << " from " << this;
+ delegate_->OnConnectionError();
+ return false;
+ }
UNSAFE_TODO(data += sizeof(int32_t));
size -= sizeof(int32_t);
@@ -380,6 +385,11 @@
size -= sizeof(timestamp);
// Handle padding bytes.
+ if (size < sizeof(int32_t)) {
+ LOG(ERROR) << "Invalid audio buffer size " << size << " from " << this;
+ delegate_->OnConnectionError();
+ return false;
+ }
UNSAFE_TODO(data += sizeof(int32_t));
size -= sizeof(int32_t);
Original Bug Report
Potential Integer Underflow and OOB Write in AudioSocket::ParseAudio
Flapjack (go/flapjack), an LLM-powered static analysis tool, has identified the following potential security issue.
Overview: An integer underflow in AudioSocket::ParseAudio can lead to a highly controllable out-of-bounds write in MixerInputConnection::HandleAudioData. This occurs when processing crafted interleaved audio packets, causing an undersized buffer allocation and subsequent OOB memcpy into the adjacent heap chunk, potentially leading to remote code execution in the mixer service.
Affected files:
chromecast/media/audio/net/audio_socket.ccchromecast/media/cma/backend/mixer/mixer_input_connection.cc
Estimated timestamp from git blame: 2025-11-18
Summary
A potential integer underflow vulnerability exists in the Chromecast AudioSocket implementation when parsing audio packets. This vulnerability can be triggered by a compromised renderer or any process capable of sending audio data to the Chromecast mixer service, potentially leading to a highly reliable out-of-bounds write on the heap. This could be leveraged to achieve a sandbox escape and arbitrary code execution in a more privileged process (e.g., the browser or mixer service).
Vulnerability Details
In chromecast/media/audio/net/audio_socket.cc, the functions ParseAudio and ParseAudioBuffer handle incoming audio data. Both functions perform insufficient bounds checking on the size parameter before subtracting the header and padding sizes.
In AudioSocket::ParseAudio:
349: bool AudioSocket::ParseAudio(char* data, size_t size) {
350: int64_t timestamp;
351: if (size < sizeof(timestamp)) {
... // Checks that size is at least 8 bytes
354: return false;
355: }
356:
357: UNSAFE_TODO(memcpy(×tamp, data, sizeof(timestamp)));
358: UNSAFE_TODO(data += sizeof(timestamp));
359: size -= sizeof(timestamp); // size is at least 0 here
360:
361: // Handle padding bytes.
362: UNSAFE_TODO(data += sizeof(int32_t));
363: size -= sizeof(int32_t); // Vulnerability: size can be less than 4 here, causing an integer underflow
364:
365: return delegate_->HandleAudioData(data, size, timestamp);
366: }
If the size passed to ParseAudio is between 8 and 11 inclusive, the subtraction at line 363 will underflow the unsigned size_t variable, resulting in an extremely large value (e.g., 0xFFFFFFFFFFFFFFFC).
Impact and Exploitation Path
This large size is passed to the socket’s delegate, MixerInputConnection::HandleAudioData.
To exploit this, an attacker could establish a connection with an interleaved audio format (e.g., SAMPLE_FORMAT_INT16_I). This format requires conversion, so CreateBufferPool skips calling socket_->UseBufferPool(). As a result, incoming messages bypass HandleAudioBuffer and are routed through OnMessage -> ParseAudio -> HandleAudioData.
- Trigger the Underflow: The attacker sends a
SmallMessageSocketpayload of exactly 10 bytes (2 bytes packet type + 8 bytes timestamp).ParseAudioprocesses this, underflowingsizeto0xFFFFFFFFFFFFFFFC. - Calculate
num_frames: InHandleAudioData,num_framesis calculated assize / frame_size. Assuming a 6-channel 16-bit stream,frame_sizeis 12. The result,0x1555555555555555, is truncated to a positiveint32_tvalue:0x55555555(1,431,655,765). - Buffer Pool Reallocation: Since
num_framesexceeds the default pool size,CreateBufferPool(num_frames * 2)is called. The argumentnum_frames * 2overflows a signed 32-bit integer, resulting in a negativeframe_count(-1431655766). - Undersized Allocation:
CreateBufferPoolcalculates the new buffer size:kAudioMessageHeaderSize + num_channels_ * sizeof(float) * frame_count. Due to the negativeframe_count, this evaluates to a small negative number that, when passed toIOBufferPool, truncates to a very small allocation size (e.g., 0 or a few bytes). - Out-of-Bounds Write:
IOBufferPoolallocates a small chunk (e.g., 32 or 48 bytes) to hold its internalStorageunion and the requested data area. Because the requested size is tiny,buffer->data()points precisely to the end of the allocated chunk (the start of the adjacent heap chunk). - Memory Corruption:
HandleAudioDataperforms the following writes:SinceUNSAFE_TODO(memcpy(buffer->data(), &num_frames, sizeof(int32_t))); UNSAFE_TODO(memcpy(buffer->data() + sizeof(int32_t), ×tamp, sizeof(timestamp)));buffer->data()points to the adjacent heap chunk, this writesnum_frames(0x55555555) and the attacker-controlled 8-bytetimestampcleanly out of bounds, directly corrupting the adjacent object. - Clean Exit:
HandleAudioDatathen calls the audio conversion function (e.g.,ConvertInterleavedData). Crucially, this function takessizeas a 32-bitint. The massivesize_tvalue (0xFFFFFFFFFFFFFFFC) is truncated to-4. The conversion loops evaluatenum_frames = data_size / ... = -4 / 12 = 0, causing the function to skip processing and return cleanly without crashing.
By carefully grooming the heap, an attacker could place another object with a vtable (such as an adjacent IOBuffer wrapper) in the next chunk. The OOB write would overwrite its vtable pointer with the attacker-controlled timestamp, providing a reliable primitive for arbitrary code execution.
Proposed Mitigation
Add a bounds check in both AudioSocket::ParseAudio and AudioSocket::ParseAudioBuffer to ensure the remaining size is at least sizeof(int32_t) before attempting to skip the padding bytes.
Evaluated with Chrome root at commit: 843d0814f9a6e06cb8c6dd3ea7d5613ff9ffda45
Results from Flapjack so far have been promising, but it can be wrong in its deductions. At this time, it does not produce proof of concepts or fuzzer tests. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve Flapjack’s accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.