Medium CVSS 5.5 webkit OOB 🔧 Commit mapped

Overview

Medium
Severity
5.5
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may lead to an unexpected process crash
ComponentWebCore WebAudio
Bug ClassOOB
Tracker275431
Fix commitff52ff7cb64e (WebKit/WebKit)
CWECWE-125 (Out-of-bounds read)
CVSS vectorCVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H
CISA KEVNot listed
CreditedHuang Xilin of Ant Group Light-Year Security Lab
Disclosed2024-07-29

Background

Linear interpolation resampling
Producing an output sample by blending two adjacent source frames (readIndex and readIndex2) weighted by the fractional playback position, used when the playback rate is not a whole number.
Reverse playback
A negative playbackRate that walks the source buffer backward, which reverses the direction readIndex moves and stresses the boundary-wrap logic.
readIndex / readIndex2
The two integer source-frame indices bracketing the current fractional read position in the interpolation branch.
bufferLength / maxFrame
The number of valid frames in the source channel; indices must remain strictly below it to be in bounds.
Out-of-bounds read
Reading memory beyond the allocated source channel array, here dereferencing source[readIndex] with an index that escaped the valid frame range.

Root Cause Analysis

This is an out-of-bounds read in the interpolating (resampling) branch of AudioBufferSourceNode::renderFromBuffer in Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp. That branch performs linear interpolation between two adjacent source frames, readIndex and readIndex2, when the playback rate is non-integral or reversed. Just above the patched lines the code already conditionally rewinds/wraps readIndex2 (if (readIndex2 >= maxFrame) readIndex2 = m_isLooping ? minFrame : readIndex;), showing the intended invariant that both interpolation indices must stay within the buffer’s frame range. The invariant was not fully enforced: under certain rate/offset combinations readIndex (and/or readIndex2) could still be at or beyond bufferLength when the interpolation loop dereferences source[readIndex] and source[readIndex2], producing an OOB read of the source channel data. The added layout test triggers it with a reverse playback (playbackRate.value = -1) grain started at a fractional offset (start(undefined, 0.5)) on an 8192-frame buffer, i.e. the resampler runs backward and the computed indices fall outside the valid window.

The fix inserts a final in-loop guard — if (readIndex >= bufferLength || readIndex2 >= bufferLength) break; — that bails out of the interpolation loop before any out-of-bounds dereference; the accompanying FIXME comment explicitly frames it as a last-ditch sanity check to be later replaced by pre-loop assertions/guards. This restores the invariant that both interpolation indices are valid before the source buffer is read. Note the diff shows only the guard being added, not the full computation of readIndex/readIndex2/maxFrame/minFrame, so the exact arithmetic that let the index escape (interaction of reverse rate, fractional start offset, and virtualReadIndex advancement) is inferred from the surrounding wrap logic and the reverse-playback trigger rather than shown line-by-line.

Key insight
The interpolation branch enforced its buffer-boundary invariant only partially (it wrapped readIndex2 but not the general case), so reverse playback at a fractional start offset could still index past the buffer; the fix is a defensive per-iteration bounds check on both interpolation indices before dereferencing the source.

Attack Path

  1. Set up context and buffer source Attacker web content creates new AudioContext() and new AudioBufferSourceNode(ctx), and assigns a buffer via ctx.createBuffer(1, 8192, 44100).
  2. Start the grain at a fractional offset Call src.start(undefined, 0.5) so playback begins at a non-frame-aligned offset, forcing the interpolating (resampling) code path rather than a plain integer copy.
  3. Reverse the playback rate Set src.playbackRate.value = -1 so rendering proceeds in reverse, driving the readIndex/readIndex2 computation toward the buffer boundary in a way the pre-patch wrap logic did not fully cover.
  4. Connect to force rendering src.connect(ctx.destination, 0, 0) schedules the node so the audio thread calls renderFromBuffer and enters the linear-interpolation loop.
  5. Trigger the OOB read The loop dereferences source[readIndex]/source[readIndex2] with an index at/beyond bufferLength, reading past the channel buffer. The observable result is an out-of-bounds read leading to an unexpected process crash.

Impact Assessment

The primitive is an out-of-bounds read of source-buffer-adjacent heap memory on the WebContent audio rendering thread; the read values feed the interpolation output, not an attacker-controlled arbitrary-offset leak, so the realistic and CVE-stated outcome is an unexpected crash rather than a robust info-disclosure or corruption primitive. It is contained within the sandboxed WebContent process. Turning this into RCE would require chaining with other bugs; the guard’s break-on-overflow shows the fix targets crash prevention rather than a demonstrated corruption channel.

Changed Functions

FunctionChangeNotes
AudioBufferSourceNode::renderFromBuffer
Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp
modified Adds a final in-loop bounds check in the linear-interpolation branch — `if (readIndex >= bufferLength || readIndex2 >= bufferLength) break;` — before the per-channel source reads, preventing OOB access when reverse/fractional playback pushes an index past the buffer; a FIXME notes it should later be hoisted to pre-loop guards.

Audit Directions

  • Same branch, both indices and all wrap paths
    Re-examine renderFromBuffer’s interpolation branch: verify the new break covers every dereference and that the earlier readIndex2 = m_isLooping ? minFrame : readIndex; reassignment can’t leave readIndex itself out of range; grep for readIndex2, maxFrame, minFrame, m_isLooping.
  • Reverse and looping grain math
    Audit the reverse-playback and looping code paths in the webaudio module for index computations that assume forward motion; grep for reverse, playbackRate, floorf, and negative-rate handling where virtualReadIndex is advanced.
  • Resamplers indexing without pre-loop bounds
    Across WebCore audio DSP (e.g. AudioResampler, SincResampler, multichannel resamplers), grep for interpolation loops that read source[index]/source[index+1] and confirm a bounds check exists before the loop rather than relying on inner-loop patches.

Original Bug Report

The reporter's bug is still restricted on the tracker.