Package: src:whisper.cpp Version: 1.8.4+dfsg-1 Severity: grave Tags: patch security
Hi, Please find attached a patch that fixes CVE-2025-14569, a use-after-free vulnerability in the read_audio_data() function in common-whisper.cpp. The fix adds a decoder_initialized flag, verifies ma_decoder_init_file() return value explicitly, only calls ma_decoder_uninit() when the decoder was successfully initialized, and adds proper cleanup on error paths. The upstream (ggml-org) was notified via GitHub issue #3501 in November 2025 but has not responded. This patch is ready for Debian packaging. Best regards, Claudio Ferreira
From: Claudio Filho <[email protected]> Date: Sun, 11 May 2026 14:54:00 -0300 Subject: Fix use-after-free in read_audio_data (CVE-2025-14569) The read_audio_data() function in common-whisper.cpp declares the ma_decoder struct on the stack. When ma_decoder_init_file() internally tries multiple backends, a partial init followed by postinit failure can leave dangling pointers (pBackend, pInputCache) in the stack struct. Subsequent operations may then call ma_free() on these dangling pointers, triggering an invalid-free / use-after-free crash (CVE-2025-14569). Fix by: - Tracking decoder initialization state with a bool flag - Checking ma_decoder_init_file() return value explicitly - Only calling ma_decoder_uninit() when the decoder was successfully initialized - Cleaning up decoder resources before early returns on error paths - Removing the nonsensical fallback that used the filename string as audio data (ma_decoder_init_memory(fname.c_str(), fname.size(), ...)) Forwarded: not-yet Last-Update: 2026-05-11 --- examples/common-whisper.cpp | 46 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/examples/common-whisper.cpp b/examples/common-whisper.cpp index baf5801..3420b52 100644 --- a/examples/common-whisper.cpp +++ b/examples/common-whisper.cpp @@ -45,6 +45,7 @@ bool read_audio_data(const std::string & fname, std::vector<float>& pcmf32, std: ma_result result; ma_decoder_config decoder_config; ma_decoder decoder; + bool decoder_initialized = false; decoder_config = ma_decoder_config_init(ma_format_f32, stereo ? 2 : 1, WHISPER_SAMPLE_RATE); @@ -63,52 +64,53 @@ bool read_audio_data(const std::string & fname, std::vector<float>& pcmf32, std: audio_data.insert(audio_data.end(), buf, buf + n); } - if ((result = ma_decoder_init_memory(audio_data.data(), audio_data.size(), &decoder_config, &decoder)) != MA_SUCCESS) { - + result = ma_decoder_init_memory(audio_data.data(), audio_data.size(), &decoder_config, &decoder); + if (result != MA_SUCCESS) { fprintf(stderr, "Error: failed to open audio data from stdin (%s)\n", ma_result_description(result)); - return false; } + decoder_initialized = true; fprintf(stderr, "%s: read %zu bytes from stdin\n", __func__, audio_data.size()); - } - else if (((result = ma_decoder_init_file(fname.c_str(), &decoder_config, &decoder)) != MA_SUCCESS)) { -#if defined(WHISPER_FFMPEG) - if (ffmpeg_decode_audio(fname, audio_data) != 0) { - fprintf(stderr, "error: failed to ffmpeg decode '%s'\n", fname.c_str()); - - return false; + } else { + result = ma_decoder_init_file(fname.c_str(), &decoder_config, &decoder); + if (result == MA_SUCCESS) { + decoder_initialized = true; } +#if defined(WHISPER_FFMPEG) + if (!decoder_initialized) { + if (ffmpeg_decode_audio(fname, audio_data) != 0) { + fprintf(stderr, "error: failed to ffmpeg decode '%s'\n", fname.c_str()); + return false; + } - if ((result = ma_decoder_init_memory(audio_data.data(), audio_data.size(), &decoder_config, &decoder)) != MA_SUCCESS) { - fprintf(stderr, "error: failed to read audio data as wav (%s)\n", ma_result_description(result)); - - return false; + result = ma_decoder_init_memory(audio_data.data(), audio_data.size(), &decoder_config, &decoder); + if (result != MA_SUCCESS) { + fprintf(stderr, "error: failed to read audio data as wav (%s)\n", ma_result_description(result)); + return false; + } + decoder_initialized = true; } -#else - if ((result = ma_decoder_init_memory(fname.c_str(), fname.size(), &decoder_config, &decoder)) != MA_SUCCESS) { - fprintf(stderr, "error: failed to read audio data as wav (%s)\n", ma_result_description(result)); - +#endif + if (!decoder_initialized) { + fprintf(stderr, "error: failed to read audio data from '%s'\n", fname.c_str()); return false; } -#endif - } + } ma_uint64 frame_count; ma_uint64 frames_read; if ((result = ma_decoder_get_length_in_pcm_frames(&decoder, &frame_count)) != MA_SUCCESS) { fprintf(stderr, "error: failed to retrieve the length of the audio data (%s)\n", ma_result_description(result)); - - return false; + goto cleanup; } pcmf32.resize(stereo ? frame_count*2 : frame_count); if ((result = ma_decoder_read_pcm_frames(&decoder, pcmf32.data(), frame_count, &frames_read)) != MA_SUCCESS) { fprintf(stderr, "error: failed to read the frames of the audio data (%s)\n", ma_result_description(result)); - - return false; + goto cleanup; } if (stereo) { @@ -128,9 +130,12 @@ bool read_audio_data(const std::string & fname, std::vector<float>& pcmf32, std: } } - ma_decoder_uninit(&decoder); +cleanup: + if (decoder_initialized) { + ma_decoder_uninit(&decoder); + } - return true; + return result == MA_SUCCESS; }

