Medium patch-now CVSS 8.8 chrome OOB 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds write in Media
DescriptionOut of bounds write in Media
ComponentMedia
Bug ClassOOB
Action tierpatch-now — corruption primitive, web-reachable in default config (unverified)
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker496607380
Fix commit497faf3e01db (chromium/src) +3/-1
CWECWE-787 (Out-of-bounds write)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-03-26 (per vendor advisory)
Fix landed2026-03-30 (public git)
Disclosed2026-05-05
Patch gap36 days — fix public in source before the advisory

Background

Channel layout
the mapping describing which audio channels (front-left, LFE, surround, etc.) a stream carries; on macOS this is translated into Core Audio AudioChannelLayout/channel descriptions.
`ChannelOrder`
a helper returning the positional index of a given Channels enum value within a layout, or -1 if that channel is absent from the layout.
`descriptions` array
a buffer of per-channel description entries (each with an mChannelLabel) allocated to hold only input_channels elements, indexed by the returned order.
`UNSAFE_TODO`
a Chromium marker wrapping a raw, unchecked pointer/array access that has not yet been migrated to a bounds-safe span, signaling the write at descriptions[order] is not bounds-checked.

Root Cause Analysis

The loop (enclosing function not named in the @@ -144 header) iterates ch from 0 to CHANNELS_MAX, computes order = ChannelOrder(input_layout, static_cast<Channels>(ch)), and — unless order == -1 — writes to descriptions[order].mChannelLabel. The descriptions buffer is sized to input_channels, but ChannelOrder can return a valid (non--1) index that is >= input_channels when the layout describes a channel position beyond the number of channels actually allocated. Pre-patch the only skip condition was order == -1, so such an out-of-range order passed the guard and the code performed an out-of-bounds write into descriptions[order] (the access is flagged UNSAFE_TODO, i.e. unbounded).

The fix widens the skip condition to order == -1 || order >= input_channels, matching the comment “We only allocate up to input_channels, skip if past what was allocated for,” so any index beyond the allocation is now continued past before the write. This is a tightening: the added order >= input_channels clause runs before the sink, preventing the OOB store.

Key insight
The code bounds-checked only for the “channel absent” sentinel (-1) but not against the actual allocation size, so a ChannelOrder result >= input_channels indexed past the descriptions buffer; the fix adds the order >= input_channels skip so writes stay within the allocated range.

Attack Path

  1. Supply a mismatched layout Media with a channel layout whose channel positions map to an order larger than the stream’s input_channels is processed on macOS.
  2. Compute an out-of-range order The loop calls ChannelOrder for each channel; for the offending position it returns a valid index >= input_channels rather than -1.
  3. Pass the insufficient guard Pre-patch, only order == -1 is skipped, so the out-of-range index proceeds to the write.
  4. Out-of-bounds write The code stores into descriptions[order].mChannelLabel, writing past the end of the input_channels-sized buffer (a heap/stack OOB write; the exact adjacent object corrupted is inferred, as the diff shows only the unbounded index).

Impact Assessment

An attacker able to influence the audio channel layout processed on macOS could trigger an out-of-bounds write into the descriptions buffer, corrupting adjacent memory in the process performing the conversion. The precondition is reaching this macOS channel-layout conversion path with a layout whose ChannelOrder exceeds input_channels. The vendor advisory rates this medium (out-of-bounds write in Media) and does not report exploitation in the wild; escalation from the OOB write to control-flow hijack or code execution is (inferred) and not demonstrated by the diff.

Changed Functions

FunctionChangeNotes
for
media/base/mac/channel_layout_util_mac.cc
modified
if
media/base/mac/channel_layout_util_mac.cc
modified

Files Changed

  • media/base/mac/channel_layout_util_mac.cc

Audit Directions

  • Sentinel-only guards on computed indices
    Audit loops that check an index helper only against its “not found”/-1 sentinel but not against the target buffer’s size, especially where the index and the allocation come from different sources.
  • `UNSAFE_TODO` / raw array sinks
    Review remaining UNSAFE_TODO-wrapped array accesses in media conversion code, confirming every index is bounded by the allocation before the store.
  • Channel/format count mismatches
    Look across audio/video layout conversion (per-platform) for places where a positional index derived from a layout can exceed the per-stream channel/plane count used to size buffers.
From 497faf3e01dbeb2bf1e584c161afb6cda46f2eec Mon Sep 17 00:00:00 2001
From: Syed AbuTalib <[email protected]>
Date: Mon, 30 Mar 2026 16:50:15 -0700
Subject: [PATCH] Skip orders past input_channels

It is unnecessary to test for orders past the total # of channels,
continue if so.

Bug: 496607380
Change-Id: I1525fdec20d49978233f995900b9a865523203dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7706421
Commit-Queue: Syed AbuTalib <[email protected]>
Reviewed-by: Thomas Guilbert <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1607459}
---

diff --git a/media/base/mac/channel_layout_util_mac.cc b/media/base/mac/channel_layout_util_mac.cc
index 88d4b100..82424db 100644
--- a/media/base/mac/channel_layout_util_mac.cc
+++ b/media/base/mac/channel_layout_util_mac.cc
@@ -144,7 +144,9 @@
   } else {
     for (int ch = 0; ch <= CHANNELS_MAX; ++ch) {
       const int order = ChannelOrder(input_layout, static_cast<Channels>(ch));
-      if (order == -1) {
+      // We only allocate up to `input_channels`, skip if past what was
+      // allocated for.
+      if (order == -1 || order >= input_channels) {
         continue;
       }
       UNSAFE_TODO(descriptions[order].mChannelLabel) =
Loading diff…

Original Bug Report

reported by [email protected]

Potential Heap OOB Write in ChannelLayoutToAudioChannelLayout via CHANNEL_LAYOUT_5_1_4_DOWNMIX

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: A compromised renderer can bypass audio parameter validation by requesting the CHANNEL_LAYOUT_5_1_4_DOWNMIX layout with a mismatched channel count (e.g., 1). This bypasses standard validation and reaches the audio service on macOS, where ChannelLayoutToAudioChannelLayout allocates an undersized buffer based on the requested channel count but attempts to write up to 6 channels’ worth of data. This results in a fixed-stride out-of-bounds write on the heap, potentially leading to arbitrary code execution within the sandboxed audio service utility process.

Affected files:

  • media/base/mac/channel_layout_util_mac.cc
  • media/base/channel_layout.cc
  • media/base/audio_parameters.cc
  • media/audio/apple/audio_auhal.cc
  • media/audio/mac/audio_manager_mac.cc
  • media/audio/mac/avfoundation_output_stream.mm

Estimated timestamp from git blame: 2023-04-27

Summary

A potential heap-based out-of-bounds (OOB) write vulnerability exists in ChannelLayoutToAudioChannelLayout within media/base/mac/channel_layout_util_mac.cc. A compromised renderer can leverage a special-case validation bypass for the CHANNEL_LAYOUT_5_1_4_DOWNMIX layout to send crafted AudioParameters that trigger this overflow in the audio service utility process on macOS.

(Note: This report is generated by an LLM agent that cannot yet run code. The steps below are a potential attack path based on static analysis.)

Vulnerability Details

In media/base/mac/channel_layout_util_mac.cc, the function ChannelLayoutToAudioChannelLayout allocates an AudioChannelLayout structure using a std::vector<uint8_t> as backing storage. The size of this allocation is determined by the input_channels parameter:

int output_layout_size =
    offsetof(AudioChannelLayout, mChannelDescriptions[input_channels]);
auto new_layout =
    std::make_unique<ScopedAudioChannelLayout>(output_layout_size);

However, in the subsequent loop that populates the mChannelDescriptions array, the code uses ChannelOrder(input_layout, ch) to determine the destination index for each channel. For CHANNEL_LAYOUT_5_1_4_DOWNMIX, the predefined channel ordering in media/base/channel_layout.cc maps channels to indices up to 5 (mapping to 6 channels). If input_channels is less than 6 (e.g., input_channels=1), the loop writes to descriptions[order] without any bounds checking, resulting in an OOB write beyond the allocated buffer.

Specifically, when input_channels=1, the vector is sized for a single AudioChannelDescription. The loop then writes to descriptions[0..5] based on the ordering for CHANNEL_LAYOUT_5_1_4_DOWNMIX. Since each description is 20 bytes and the code writes the first 8 bytes (label and flags), this results in multiple 8-byte OOB writes at a 20-byte stride, extending up to approximately 88 bytes past the allocated buffer.

Validation Bypass

The malicious AudioParameters are able to reach the vulnerable function due to explicit special-casing of CHANNEL_LAYOUT_5_1_4_DOWNMIX in media/base/audio_parameters.cc. The AudioParameters::IsValid() function exempts this layout from the standard check that channels() must match ChannelLayoutToChannelCount(channel_layout()):

         (channel_layout() == CHANNEL_LAYOUT_DISCRETE ||
          channel_layout() == CHANNEL_LAYOUT_5_1_4_DOWNMIX ||
          channels() == ChannelLayoutToChannelCount(channel_layout()));

This bypass allows a compromised renderer to pass Mojo validation (ParamTraits<media::AudioParameters>::Read) with an AudioParameters object containing CHANNEL_LAYOUT_5_1_4_DOWNMIX and channels=1.

Potential Attack Path

  1. Mojo Connection: A compromised renderer process obtains a Mojo connection to blink::mojom::RendererAudioOutputStreamFactory.
  2. Requesting Audio Stream: The renderer calls RequestDeviceAuthorization for the default output device to receive an AudioOutputStreamProvider.
  3. Crafting Malicious AudioParameters: The renderer calls Acquire() with crafted media::AudioParameters: format=AUDIO_PCM_LOW_LATENCY, channel_layout=CHANNEL_LAYOUT_5_1_4_DOWNMIX (enum value 33), and channels=1 (or any value < 6).
  4. Forwarding to Audio Service: The browser process deserializes the parameters, validates them successfully (due to the bypass), and forwards the stream creation request to the Audio Service via an AudioOutputStreamBroker.
  5. Audio Service Parameter Handling: In the Audio Service on macOS, AudioManagerMac::GetPreferredOutputStreamParameters preserves the malicious layout because channels=1 is typically less than or equal to the hardware channel count.
  6. Triggering the Bug: AUHALStream::Open (or AVFoundationOutputStream::Open) calls ChannelLayoutToAudioChannelLayout to configure the layout. This triggers the OOB write in the sandboxed audio service utility process.

Impact

An attacker with control over a renderer process can trigger a linear heap OOB write in the audio service utility process. While the values written are fixed (specific AudioChannelLabel constants and zeros), the attacker controls the size of the initial heap allocation (by varying channels from 1 to 5) and the timing. This allows them to place the buffer in different PartitionAlloc buckets and potentially overwrite specific fields in adjacent objects, leading to memory corruption and potentially arbitrary code execution within the sandboxed audio service process.

Of note, the presence of #pragma allow_unsafe_buffers in media/base/mac/channel_layout_util_mac.cc suppressed diagnostic warnings from the clang unsafe-buffers plugin that might have otherwise flagged this issue.

Suggested Fix

  1. Fix Validation: The bypass in AudioParameters::IsValid() should be re-evaluated. If CHANNEL_LAYOUT_5_1_4_DOWNMIX requires a custom number of channels, the acceptable range should be strictly enforced.
  2. Add Bounds Checking: In ChannelLayoutToAudioChannelLayout, add a bounds check to ensure that order is strictly less than input_channels before writing to the descriptions array. For example:
    const int order = ChannelOrder(input_layout, static_cast<Channels>(ch));
    if (order == -1 || order >= input_channels) {
      continue;
    }
    

Evaluated with Chrome root at commit: bb48272cafb7e24c93f55ef40da398cd206ee651


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. Please feel free to reach out to me if you have concerns or feedback.

View on issue tracker