ybgwon96's picture
Upload 6 files
e120616 verified
|
Raw
History Blame Contribute Delete
2.34 kB
# PoC: stable-diffusion.cpp custom-embedding heap out-of-bounds write (malicious .safetensors)
**Target:** `leejet/stable-diffusion.cpp` β€” `Conditioner::load_embedding`
(`src/conditioning/conditioner.hpp:250-253`).
**Format:** SafeTensors (`.safetensors`) textual-inversion embedding (also `.pt`/`.ckpt`/`.gguf`).
**Class:** CWE-787 heap out-of-bounds write (attacker-controlled length + content), CWE-131 / CWE-682
(incorrect buffer-offset calculation across mixed tensor dtypes).
**Verified:** source @ `68f3d6df9f1964e7f942dd242cc9c21b76fa273d`; SIGSEGV reproduced (`CRASH_PROVEN.md`).
## What it is
Custom embeddings are accumulated into one shared `std::vector<uint8_t> token_embed_custom`, with
`num_custom_embeddings` counting the total rows added so far β€” **both persist across calls**. Each
`load_embedding` grows the buffer by `ggml_nbytes(embd)` (this file's dtype) but computes the write
offset as `num_custom_embeddings * hidden_size * ggml_type_size(embd->type)` β€” using **this** file's
dtype size for **all** previously-added rows. If an earlier embedding used a smaller dtype (F16, 2 B)
and the current one a larger dtype (F32, 4 B), the offset over-counts the earlier rows and the
`memcpy` writes past the freshly-resized buffer β€” an OOB heap write whose size scales with the first
embedding's row count (attacker-controlled) and whose bytes are the second embedding's contents.
## Files
- `embd_f16.safetensors` β€” first embedding, dtype F16, shape `[8000, 768]`.
- `embd_f32.safetensors` β€” second embedding, dtype F32, shape `[1, 768]`.
- `make_embeddings.py` β€” regenerates both (`python make_embeddings.py <R0> <outdir>`); `R0` sets the
overflow size.
- `embd_dtype_oob_repro.cpp` / `embd_dtype_oob_repro` β€” isolated reproduction of the exact
`resize`+`memcpy` arithmetic; SIGSEGVs at `-O0`.
- `CRASH_PROVEN.md` β€” run output + lldb backtrace + the offset math.
## Trigger (real sd.cpp)
Place both files in the embeddings dir and reference both tokens in one prompt, e.g.
`sd -m model.safetensors --embd-dir . -p "a photo, embd_f16 embd_f32"` β€” `load_embedding` is called
for `embd_f16` (F16) then `embd_f32` (F32); the second call performs the OOB write.
Coordinated-disclosure security PoC for the huntr AI/ML bug-bounty program. No payload; it only
demonstrates the memory-safety flaw.