| --- |
| license: mit |
| tags: [security, poc, huntr, model-file-vulnerability] |
| --- |
| # PoC — koboldcpp legacy ggml loaders: unchecked `n_dims` → `ne[2]` stack buffer overflow |
| |
| huntr MFV PoC vs [`LostRuins/koboldcpp`](https://github.com/LostRuins/koboldcpp) `otherarch/*`. |
| Authorized security research (responsible disclosure via huntr). |
| |
| koboldcpp's legacy ggml loaders read a per-tensor `int32_t n_dims` from the model file and loop |
| `for (i=0;i<n_dims;++i) fin.read(&ne[i], ...)` into a fixed `int32_t ne[2]` — with no `n_dims<=2` check. |
| n_dims>=3 writes attacker-controlled bytes past the 2-element stack array (CWE-787). Present in gpt2_v1/ |
| v2/v3, gptj_v1/v2/v3, mpt_v3, llama_v2/v3, neox_v2/v3, rwkv_v2 (reached via FileFormat detection). |
| |
| ## Files |
| - `ndims_repro.cpp` / `CRASH_PROVEN.md` — isolated repro; EXC_BAD_ACCESS at the `ne[i]` store. |
| |
| ## Reproduce |
| ``` |
| c++ -O0 -g -fstack-protector-all ndims_repro.cpp -o ndims_repro && ./ndims_repro 100000 # -> SIGSEGV |
| ``` |
| ## Fix |
| Add `if (n_dims < 0 || n_dims > 2) return ModelLoadResult::FAIL;` before the ne-fill loop in each loader. |
| |