CVE-2026-11655
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifmedia/audio/apple/audio_auhal.cc |
modified | |
ifmedia/audio/mac/avfoundation_output_stream.mm |
modified | |
ifmedia/filters/mac/audio_toolbox_audio_decoder.cc |
modified |
Files Changed
media/audio/apple/audio_auhal.ccmedia/audio/mac/avfoundation_output_stream.mmmedia/base/mac/channel_layout_util_mac.ccmedia/filters/mac/audio_toolbox_audio_decoder.cc
Patch
From 136bb7f450dae7225bb90c721f1616d4e57d1e78 Mon Sep 17 00:00:00 2001
From: Thomas Guilbert <tguilbert@chromium.org>
Date: Thu, 28 May 2026 20:03:07 -0700
Subject: [PATCH] Use checked math before allocating ScopedAudioChannelLayout
This CL fixes a potential issue when trying to allocate a
`ScopedAudioChannelLayout` with a huge number of channels.
The CL fixes the issue by using checked math, and preventing integer
truncation when converting from `size_t` to `int`.
We prefer this approach over adding CHECKs to guard against a channel
count higher than `media::limits::kMaxChannels`, since `kMaxChannels`
(32 channels as of this commit) is lower than some high-end audio
interfaces (e.g. 40 channels), and this could introduce crashes in
otherwise valid scenarios.
Fixed: 513396305
Change-Id: I65f3f41bcffef5ce193f623eeac5d5178cdf6502
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7876673
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Auto-Submit: Thomas Guilbert <tguilbert@chromium.org>
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1638201}
---
diff --git a/media/audio/apple/audio_auhal.cc b/media/audio/apple/audio_auhal.cc
index a1c0f34b..819b082 100644
--- a/media/audio/apple/audio_auhal.cc
+++ b/media/audio/apple/audio_auhal.cc
@@ -174,6 +174,10 @@
auto coreaudio_layout =
ChannelLayoutToAudioChannelLayout(channel_layout, channels);
+ if (!coreaudio_layout) {
+ DLOG(ERROR) << "Failed to create audio channel layout.";
+ return;
+ }
MaybeMapRearSurroundChannelToSurroundChannel(audio_unit,
coreaudio_layout->layout());
diff --git a/media/audio/mac/avfoundation_output_stream.mm b/media/audio/mac/avfoundation_output_stream.mm
index e4a2052c..1487221 100644
--- a/media/audio/mac/avfoundation_output_stream.mm
+++ b/media/audio/mac/avfoundation_output_stream.mm
@@ -95,6 +95,7 @@
callback_(nullptr),
objc_storage_(std::make_unique<ObjCStorage>()),
audio_bus_(AudioBus::Create(params)) {
+ CHECK(params_.IsValid());
DVLOG(1)
<< __func__
<< ": Initializing AVFoundationOutputStream with these AudioParameters:: "
@@ -147,6 +148,10 @@
auto scoped_layout = ChannelLayoutToAudioChannelLayout(
params_.channel_layout(), params_.channels());
+ if (!scoped_layout) {
+ LOG(ERROR) << "Failed to create audio channel layout.";
+ return false;
+ }
OSStatus status = CMAudioFormatDescriptionCreate(
/*allocator=*/kCFAllocatorDefault,
diff --git a/media/base/mac/channel_layout_util_mac.cc b/media/base/mac/channel_layout_util_mac.cc
index 5c9d3c6..10c341f 100644
--- a/media/base/mac/channel_layout_util_mac.cc
+++ b/media/base/mac/channel_layout_util_mac.cc
@@ -10,6 +10,8 @@
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/containers/span.h"
+#include "base/logging.h"
+#include "base/numerics/safe_math.h"
#include "media/base/channel_layout.h"
namespace media {
@@ -116,8 +118,21 @@
//
// Code modeled after example from Apple documentation here:
// https://developer.apple.com/library/content/qa/qa1627/_index.html
- int output_layout_size =
- offsetof(AudioChannelLayout, mChannelDescriptions[input_channels]);
+ // Modified to use `size_t` and `base::CheckedNumeric` to prevent integer
+ // truncation and overflow.
+ // `AudioChannelLayout` already includes one `AudioChannelDescription` in its
+ // definition, so we allocate space for `input_channels - 1` additional
+ // descriptions.
+ base::CheckedNumeric<size_t> checked_layout_size =
+ sizeof(AudioChannelLayout) +
+ base::CheckedNumeric<size_t>(input_channels - 1) *
+ sizeof(AudioChannelDescription);
+ size_t output_layout_size = 0;
+ if (!checked_layout_size.AssignIfValid(&output_layout_size)) {
+ DLOG(ERROR) << "Invalid AudioChannelLayout size.";
+ return nullptr;
+ }
+
auto new_layout =
std::make_unique<ScopedAudioChannelLayout>(output_layout_size);
diff --git a/media/filters/mac/audio_toolbox_audio_decoder.cc b/media/filters/mac/audio_toolbox_audio_decoder.cc
index dabd6f0..620b24c 100644
--- a/media/filters/mac/audio_toolbox_audio_decoder.cc
+++ b/media/filters/mac/audio_toolbox_audio_decoder.cc
@@ -376,6 +376,11 @@
// channel order description. This let decoder output correct orders.
auto ordered_layout = ChannelLayoutToAudioChannelLayout(
channel_layout, input_format.mChannelsPerFrame);
+ if (!ordered_layout) {
+ MEDIA_LOG(ERROR, media_log_) << "Failed to create audio channel layout.";
+ return false;
+ }
+
auto result = AudioConverterSetProperty(
decoder_.get(), kAudioConverterOutputChannelLayout,
ordered_layout->layout_size(), ordered_layout->layout());
Original Bug Report
Potential linear heap OOB write in macOS GPU process via integer truncation in AudioChannelLayout
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: An integer truncation vulnerability in the macOS-specific audio utility code allows for an undersized heap allocation when processing high channel counts. A compromised renderer can potentially exploit this to trigger a massive linear heap out-of-bounds write in the GPU process. The issue stems from a 64-bit to 32-bit truncation during a buffer size calculation.
Affected files:
media/base/mac/channel_layout_util_mac.ccmedia/filters/mac/audio_toolbox_audio_decoder.ccmedia/base/mac/channel_layout_util_mac.h
Estimated timestamp from git blame: 2023-04-27
Summary
A potential linear heap out-of-bounds (OOB) write vulnerability exists in the macOS GPU process due to an integer truncation in media::ChannelLayoutToAudioChannelLayout. When calculating the required size for an AudioChannelLayout structure, a 64-bit size_t value is truncated into a 32-bit int, leading to an undersized heap allocation. Subsequent initialization of the structure’s members results in a linear OOB write.
Root Cause Analysis
The vulnerability is located in media/base/mac/channel_layout_util_mac.cc within the ChannelLayoutToAudioChannelLayout function:
std::unique_ptr<ScopedAudioChannelLayout> ChannelLayoutToAudioChannelLayout(
ChannelLayout input_layout, int input_channels) {
// ...
int output_layout_size = // [1] Truncation occurs here
offsetof(AudioChannelLayout, mChannelDescriptions[input_channels]);
auto new_layout =
std::make_unique<ScopedAudioChannelLayout>(output_layout_size); // [2] Undersized allocation
new_layout->layout()->mNumberChannelDescriptions = input_channels; // [3] Large count stored
// ...
auto descriptions = GetDescriptions(*new_layout->layout());
if (input_layout == CHANNEL_LAYOUT_DISCRETE) {
for (int ch = 0; ch < input_channels; ++ch) {
descriptions[ch].mChannelLabel = kAudioChannelLabel_Unknown; // [4] OOB write
descriptions[ch].mChannelFlags = kAudioChannelFlags_AllOff;
}
}
- Truncation: In
[1],offsetofreturns a 64-bitsize_t. For a sufficiently largeinput_channels(e.g.,214,748,365), the result12 + (214,748,365 * 20) = 4,294,967,312(0x100000010) is truncated when assigned to the 32-bit signedintoutput_layout_size, resulting in a value of16. - Allocation: In
[2], abase::HeapArrayof only 16 bytes is allocated. - Inconsistent State: In
[3], the structure’s member count is set to the original large value. TheGetDescriptionshelper subsequently creates abase::spanwhose length is based on this large value, bypassing span safety checks for the underlying small allocation. - OOB Write: In
[4], the loop iteratesinput_channelstimes, performing writes starting at offset 12 and continuing linearly well beyond the 16-byte buffer.
Potential Attack Vector
Note: These are potential steps as a functional PoC has not yet been executed.
- A compromised renderer binds the
media.mojom.AudioDecoderinterface in the GPU process (on macOS, this often instantiatesAudioToolboxAudioDecoder). - The renderer calls
Initialize()with anAudioDecoderConfigwherecodeciskAACandprofileiskXHE_AAC(xHE-AAC/USAC). - The
extra_datafield is crafted to contain a USAC configuration that specifies an extremely large channel count (e.g., via ISO/IEC 23003-3escapedValuesyntax). AudioToolboxAudioDecoder::CreateDecoderuses macOSAudioFormatGetPropertyto parse the configuration. If the system API returns the large channel count inmChannelsPerFrame, it triggers the calls toChannelLayoutToAudioChannelLayoutwith the malicious count.
Impact
This flaw could allow a compromised renderer to achieve Remote Code Execution (RCE) in the GPU process by corrupting the PartitionAlloc heap. On macOS, the GPU process is sandboxed but maintains access to system audio/graphics frameworks.
Suggested Fix
- Use
base::CheckedNumeric<size_t>to perform the size calculation inChannelLayoutToAudioChannelLayoutand ensure the result fits in asize_tand is within sane bounds before allocation. - Change the type of
output_layout_sizefrominttosize_tto avoid truncation. - Implement explicit validation of the channel count in
AudioToolboxAudioDecoderbefore passing it to utility functions, enforcing limits such asmedia::limits::kMaxChannels.
Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e
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.