Skip to content

DSP ring buffer write loop livelocks when a decoded frame is larger than ring buffer capacity #387

Description

@forkwright

Finding

The DSP task writes processed samples to the ring buffer in a spin loop at engine.rs:496-504. RingBuffer::push_frame returns false whenever used + n >= self.capacity (ring_buffer.rs:79). When a decoded frame's sample count n is greater than or equal to the ring buffer's total capacity, the predicate 0 + n >= capacity is true no matter how far the consumer drains, so push_frame can never succeed. The loop yields forever via tokio::task::yield_now(). Its only exit is STATE_STOPPED, which nothing in this path sets automatically.

Evidence

crates/akouo-core/src/engine.rs:500 — the spin-loop write:

if ring.push_frame(&samples) {

crates/akouo-core/src/ring_buffer.rs:79 — the capacity check that can never pass for an oversized frame:

if used + n >= self.capacity { return false; }

Why this matters

A decoder that emits a single frame at least as large as the ring buffer capacity — from a large Symphonia decode buffer, a malformed container, or a deliberately contrived media file — permanently livelocks the DSP task. The output callback drains the buffer to empty and plays silence while the DSP thread spins at 100% CPU; no error event is emitted and no watchdog fires. Under the counter-surveillance threat model a single hostile audio file is enough to wedge the audio engine and burn CPU and battery indefinitely with no user-visible cause — a silent, file-triggered denial of service.

Desired correction

Guard the write loop. If samples.len() >= ring.capacity, split the frame into capacity-sized chunks before writing; if the loop still cannot make progress, emit an error event and exit rather than spinning forever.

Done when: a frame whose sample count exceeds ring_buffer_capacity causes playback to stop with an error event (or to be chunked and played through) rather than hanging the DSP task in an unbounded spin.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions