Crucible Findings¶
Real memory-safety and denial-of-service bugs found by Crucible's structure-aware fuzzing across ML model parsers, loaders, and inference kernels, disclosed responsibly. This page lists the publicly resolved findings: those with an assigned CVE or a merged upstream fix. Each one has a real crash, replayed on a HEAD build and read at the source before it was named.
Disclosure policy
Crucible follows coordinated disclosure. A finding appears here only after it is public: a CVE is assigned, or a maintainer has merged a fix. Additional findings are under coordinated disclosure and are not detailed until that process completes. See Responsible Disclosure.
Prior art
For previously-published vulnerabilities across this ecosystem (found by other researchers), which map the surface Crucible targets, see Known CVEs.
Summary¶
| Metric | Count |
|---|---|
| CVEs assigned | 6 |
| Findings fixed upstream (merged PRs) | 15 |
| Projects with merged fixes | 9 |
| Distinct findings identified | 184 across 72 projects |
Counts here are generated from the repository by
tools/ledger/build-ledger.py, not maintained by hand. The figure carries a stated uncertainty of +0 to +5 pending resolution of five historical IDs, and every exclusion from the count is enumerated with a written reason. See Evidence and Validation.
The full set spans heap out-of-bounds reads and writes, integer overflows leading to unbounded allocation, out-of-bounds index panics, and reachable assertions. Only the publicly resolved subset is detailed below.
Assigned CVEs¶
CVE-2026-14647 — Heap over-read in ONNX shape inference¶
A crafted ONNX model triggers a heap buffer over-read during shape inference. convPoolShapeInference loops over kernel_shape.size() but indexes a dilations[] array sized to the number of input dimensions, so a weight tensor with more spatial dimensions than the input reads past the end of the buffer during Load().
- Class: Heap out-of-bounds read (CWE-125), CVSS 7.5 High
- Fixed upstream: onnx/onnx#8037 (merged, milestone 1.22), closing #8036
- CVE: CVE-2026-14647 (VulDB VDB-376160)
CVE-2026-10298 — Null dereference and abort in whisper.cpp¶
A crafted GGUF audio model with n_audio_state = 0 produces zero-dimension tensors, leading to a null memcpy and a GGML_ASSERT abort in whisper_model_load. Denial of service on model load.
- Class: Null dereference and reachable assertion (CWE-476, CWE-617), CVSS 5.5 Medium
- Public report: ggml-org/whisper.cpp#3807
- CVE: CVE-2026-10298 (VulDB VDB-367591)
CVE-2026-17512 - Heap over-read in the whisper.cpp mel spectrogram¶
A crafted model reaches log_mel_spectrogram in src/whisper.cpp with parameters that drive a read past the end of the intended buffer.
- Class: Heap out-of-bounds read (CWE-125)
- Public report: ggml-org/whisper.cpp#3923
- CVE: CVE-2026-17512 (VulDB VDB-383382)
CVE-2026-17513 - Reachable assertion in ggml type conversion¶
A crafted ftype value reaches ggml_ftype_to_ggml_type in ggml/src/ggml.c and trips an assertion, aborting the process during model load.
- Class: Reachable assertion (CWE-617)
- Public report: ggml-org/whisper.cpp#3924
- CVE: CVE-2026-17513 (VulDB VDB-383383)
CVE-2026-17500 - Null dereference in llama.cpp grammar construction¶
A crafted JSON schema reaches _visit_pattern in common/json-schema-to-grammar.cpp and dereferences a null pointer during grammar construction. Reachable remotely on a server exposing the json_schema parameter.
- Class: Null pointer dereference (CWE-476)
- Public report: ggml-org/llama.cpp#25284
- CVE: CVE-2026-17500 (VulDB VDB-383353)
CVE-2026-17501 - Uncontrolled recursion in JSON-Schema-to-GBNF conversion¶
A deeply nested JSON schema drives unbounded recursion in the pattern transform in common/json-schema-to-grammar.cpp, exhausting the stack.
- Class: Uncontrolled recursion (CWE-674)
- Public report: ggml-org/llama.cpp#25283
- CVE: CVE-2026-17501 (VulDB VDB-383354)
Fixed upstream¶
stable-diffusion.cpp¶
Four findings in stable-diffusion.cpp model and imatrix loading, all fixed by the maintainer.
| ID | Bug class | Severity | Site | Issue / Fix |
|---|---|---|---|---|
| 097 | Heap out-of-bounds write in the imatrix loader (a duplicate tensor name skips a resize, so the accumulation loop writes past a smaller-sized vector) | High (CWE-787, CWE-1284) | src/runtime/imatrix.cpp load_imatrix | #1749 + PR #1750 (merged) |
| 015 | Uncaught JSON exception to process crash | Medium (CWE-248) | is_safetensors_file() | #1395 |
| 029 | Reachable assertion to process abort | Medium (CWE-617) | init_from_safetensors_file() | #1396 |
| 030 | Unbounded allocation to OOM crash | High (CWE-789) | GGUFReader::read_metadata() | #1397 |
015, 029, and 030 were fixed in one hardening PR (#1404). 097 is the first memory-corruption finding of the four, a heap out-of-bounds write; its fix is merged. The maintainer requested a CVE for it in July 2026; none has been assigned as of this writing.
ONNX Runtime¶
| ID | Bug class | Severity | Site | Issue / Fix |
|---|---|---|---|---|
| 067 | Unbounded output allocation (OOM) in constant folding | Medium (CWE-770) | ConstantOfShape constant folding | #28730 + PR #28751 (merged) |
TensorFlow Lite¶
| ID | Bug class | Severity | Site | Issue / Fix |
|---|---|---|---|---|
| 022 | Out-of-bounds read from unverified FlatBuffer offsets | High (CWE-125) | BuildFromAllocation / ValidateModelBuffers | #115308 + PR #121112 (merged) |
mistral.rs¶
| ID | Bug class | Severity | Site | Issue / Fix |
|---|---|---|---|---|
| 081 | Out-of-bounds index panic (unvalidated GGUF special-token ids used to index the vocabulary) | High (CWE-125) | GGUF token-id load | #2225 + PR #2282 (merged) |
Ollama¶
| ID | Bug class | Severity | Issue / Fix |
|---|---|---|---|
| 037 | Uncontrolled memory allocation in the GGUF parser | High (CWE-770) | #17032 |
| 038 | Integer-underflow panic on a GGUF v1 string | High (CWE-191) | #17033, fixed by #17062 |
| 065 | Runtime panic on a malformed registry WWW-Authenticate header | Low (CWE-129) | #17034 |
tract¶
| ID | Bug class | Severity | Site | Fix |
|---|---|---|---|---|
| 082 (residual) | Integer overflow to unbounded allocation in the NNEF tensor reader | High (CWE-190, CWE-789) | read_block_quant_value | sonos/tract#2425 (merged), a fix authored by the Crucible project |
The differential, in one finding¶
The recurring tell across these bugs is a sibling that gets it right. Finding 097 is the clean example. The imatrix loader has two entry points that both read the same untrusted file:
collect_imatrixchecks that a repeated tensor entry has a consistent value count before it writes.load_imatrix, a few functions away, does not. A duplicate name skips the resize, and the accumulation loop writes past the end of a smaller vector.
The maintainer's own sibling path is the specification for the fix, and the merged patch (#1750) is exactly that check, ported to the vulnerable path. This is why a differential turns a fuzzing crash into a directed, explainable finding: the divergence proves the missing check is an oversight, not a design decision.
Discovery method¶
Every finding here came from a Crucible libFuzzer or offline campaign with AddressSanitizer and UndefinedBehaviorSanitizer, then a manual assessment: replay the crash, stack-hash and deduplicate, read the source at the crash site, and re-verify on a HEAD build before naming it. Crucible's structure-aware mutation engine produces files that are valid enough to pass format detection and reach deep parser, loader, and kernel code, while injecting the targeted corruptions that expose the bug.