Skip to content

Reset the audio converter before raising a codec error - #142

Open
romansorochak wants to merge 2 commits into
dimitris-c:mainfrom
StarGoApps:fix/reset-converter-on-codec-error-upstream
Open

romansorochak wants to merge 2 commits into
dimitris-c:mainfrom
StarGoApps:fix/reset-converter-on-codec-error-upstream

Conversation

@romansorochak

Copy link
Copy Markdown
Contributor

Problem

A failed AudioConverterFillComplexBuffer in AudioFileStreamProcessor.propertyPacketsProc raises .codecError but leaves the converter holding a span of its input. That span points into the packet data AudioFileStream owns only for the duration of the packets callback. The source keeps delivering data after the error, so on the next fill the converter consolidates the stale span with new input and reads freed memory:

memmove ← ACBaseAudioSpan::append ← ACSpanningInputBuffer::consolidateInput ← AudioConverterChain::ObtainInput

(or MP3DecoderWrapper::Fill), which ends in SIGSEGV / SIGBUS.

In our app (Radio Live, iOS) this is the second-largest playback crash family: 40 installs from 3.3.2 to 3.10.1, every one at the first FillComplexBuffer call on source.queue. In about 32 of the 40 the player was already in .error just before the crash.

Fix

Call AudioConverterReset before each of the three .codecError raises. processSeek() and createAudioConverter already reset the converter the same way. The change is +16 / −3 in AudioFileStreamProcessor.swift.

Evidence

I could not add a unit test. The defect is a use-after-free whose only observable effect is a crash on some inputs, and that crash takes the XCTest process down, so there is no seam to assert on. Instead I used a standalone harness that runs the library's exact parse → convert flow and unmaps each chunk after AudioFileStreamParseBytes returns. With MP3 input that switches format mid-stream, the converter returns 'bada' (kAudioCodecBadDataError):

  • without the reset: 10–11 of 40 seeds crash (SIGSEGV / SIGBUS), and the lldb backtrace matches the field stack frame for frame;
  • with the reset: 0 of 40.

swift test: Executed 59 tests, with 0 failures.

Thanks again for merging #138 and #140.

romansorochak and others added 2 commits October 14, 2026 01:00
A failed AudioConverterFillComplexBuffer can leave the converter holding
a span of its input, which points into memory AudioFileStream owns only
for the duration of the packets callback. The stream keeps delivering
data after .codecError, so the next fill consolidates that stale span
with new input and reads freed memory
(ACSpanningInputBuffer::consolidateInput -> memmove, SIGSEGV/SIGBUS).

Call AudioConverterReset before each .codecError raise in
AudioFileStreamProcessor, as processSeek() and createAudioConverter
already do. In a harness that unmaps each chunk after
AudioFileStreamParseBytes returns, MP3 input switching format
mid-stream crashed on 10-11 of 40 seeds without the reset and on 0 of 40
with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d5acc82)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant