Skip to content

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.

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.

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_imatrix checks 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.