Skip to content

Specialized Harnesses

Crucible provides 37 libFuzzer harness variants (plus a coverage measurement driver), each targeting a different code path in llama.cpp, whisper.cpp, stable-diffusion.cpp, PyTorch, TensorFlow Lite, Apple MLX, and ONNX Runtime. Several harnesses also have a -mutator variant that uses Crucible's structure-aware custom mutator instead of libFuzzer's default byte-level mutations.

Harness Overview

GGUF Parsing (Core)

Harness Make Target What It Fuzzes Key Code Path
crucible-libfuzzer all GGUF parsing (shallow) gguf_init_from_file() with no_alloc=true
crucible-libfuzzer-deep deep GGUF parsing + tensor allocation gguf_init_from_file() with no_alloc=false
crucible-libfuzzer-model model Full model loading llama_model_load() → architecture dispatch
crucible-libfuzzer-clip clip Vision/audio model loading clip_init() via MTMD library
crucible-libfuzzer-quant quant Quantization + dequantization ggml_quantize_chunk() + type traits to_float
crucible-libfuzzer-roundtrip roundtrip GGUF write path gguf_write_to_file() after parse
crucible-libfuzzer-dequant dequant Raw dequantization dequantize_row_* functions directly
crucible-libfuzzer-tensor-size tensor-size Tensor size arithmetic ggml_nbytes() and related calculations
crucible-libfuzzer-b3561 b3561 Regression testing Same as primary, linked against pre-fix llama.cpp
crucible-libfuzzer-lora lora LoRA adapter loading llama_adapter_lora_init() with base model validation
crucible-libfuzzer-mutator mutator GGUF parsing + custom mutator Same as primary, with Go structure-aware mutator

Protocol & Network

Harness Make Target What It Fuzzes Key Code Path
crucible-libfuzzer-rpc rpc RPC protocol handling rpc_serve_client() via socketpair
crucible-libfuzzer-rpc-commands rpc-commands RPC command dispatch All 17 RPC commands with structured payloads
crucible-libfuzzer-rpc-graph rpc-graph RPC graph-compute (adversarial) RPC_CMD_GRAPH_COMPUTE with unclamped op/dims/flags
crucible-libfuzzer-rpc-race rpc-race Multi-connection race conditions 2 connections sharing rpc_serve_client() state
crucible-libfuzzer-tokenizer tokenizer Tokenizer (BPE/SPM) llama_tokenize() via vocab model

Template & Grammar Engines

Harness Make Target What It Fuzzes Key Code Path
crucible-libfuzzer-grammar grammar GBNF grammar parsing llama_grammar_parser::parse()
crucible-libfuzzer-grammar-compile grammar-compile Full grammar compilation pipeline llama_grammar_init_impl() (parse → stack → left-recursion)
crucible-libfuzzer-jinja jinja Jinja template engine Jinja lexer → parser → runtime pipeline
crucible-libfuzzer-json-schema json-schema JSON Schema → grammar conversion json_schema_to_grammar()
crucible-libfuzzer-unicode unicode Tokenizer regex engine unicode_regex_split()
crucible-libfuzzer-peg peg PEG grammar matching common_peg_arena::from_json() + parse()
crucible-libfuzzer-chat-parser chat-parser Tool-call parsing from LLM output tagged_peg_parser::parse_and_extract()
crucible-libfuzzer-chat-template chat-template Built-in chat template formatting llama_chat_apply_template() (~50 template variants)
crucible-libfuzzer-regex-partial regex-partial Regex partial matching regex_to_reversed_partial_regex() + common_regex

Server & API

Harness Make Target What It Fuzzes Key Code Path
crucible-libfuzzer-server server HTTP endpoint parsing pipeline oaicompat_chat_params_parse(), tokenize_mixed(), json_schema_to_grammar()

External Targets

Harness Make Target What It Fuzzes Key Code Path
crucible-libfuzzer-whisper whisper Whisper model loading whisper_init_from_buffer_with_params()
crucible-libfuzzer-whisper-audio whisper-audio Whisper inference (audio input) whisper_full() with fuzzed PCM samples
crucible-libfuzzer-sd-model sd-model Stable Diffusion model loading ModelLoader::init_from_file() (multi-format)

ML Framework Targets

Harness Make Target What It Fuzzes Key Code Path
crucible-libfuzzer-torchscript torchscript PyTorch TorchScript deserialization torch::jit::load() (ZIP + pickle + tensor loading)
crucible-libfuzzer-torch-load torch-load PyTorch ZIP container parsing PyTorchStreamReader with bounds-checked adapter
crucible-libfuzzer-tflite tflite TensorFlow Lite model loading FlatBufferModel::BuildFromBuffer()Interpreter::Invoke()
crucible-libfuzzer-onnx onnx ONNX Runtime model loading Ort::Session() (protobuf deserialization + graph optimization)
crucible-libfuzzer-mlx-gguf mlx-gguf Apple MLX GGUF loader mlx::core::load_gguf() with preflight validation
crucible-libfuzzer-mlx-safetensors mlx-safetensors Apple MLX SafeTensors loader mlx::core::load_safetensors() (JSON header + tensor data)
crucible-libfuzzer-safetensors safetensors Standalone SafeTensors header parser JSON header extraction + tensor metadata validation (nlohmann/json only)
crucible-libfuzzer-tokenizer-vocab tokenizer-vocab Tokenizer vocabulary loading Fuzzes GGUF tokenizer metadata (tokens, scores, merges, types) via llama_model_load_from_file() vocab_only

Every GGUF harness except b3561, tensor-size, and lora also has a -mutator variant (e.g., deep-mutator, model-mutator).

Harness Feature Matrix

Several harnesses include hardening features beyond basic fuzzing:

Feature Harnesses Purpose
OOM wall (pre_check_gguf_header) deep, model Rejects inputs with n_tensors > 10000 or n_kv > 10000 before parsing to prevent OOM-killing workers
try/catch wrapper sd-model Catches std::exception around ModelLoader::init_from_file(). Set CRUCIBLE_STRICT=1 to disable exception suppression for targeted investigation
Expanded op range rpc-commands Uses tensor.op % 96 (matching GGML_OP_COUNT) instead of % 80, covering 16 additional operations including GGML_OP_CUSTOM (90)

When to Use Each Harness

Shallow (crucible-libfuzzer)

The default starting point. Exercises gguf_init_from_file() with no_alloc=true, which parses the header, metadata, and tensor info blocks but does not allocate or read tensor data. Fast and lightweight — ideal for finding parsing bugs in the metadata and tensor descriptor sections.

Best for: Initial fuzzing campaigns, metadata bugs, header parsing bugs.

Deep (crucible-libfuzzer-deep)

Sets no_alloc=false, causing the parser to allocate memory for tensor data and read the binary blob from the file. This exercises allocation sizing, offset validation, and memory mapping code paths that the shallow harness skips entirely.

Best for: Memory allocation bugs, offset-based out-of-bounds reads, tensor data handling.

Model (crucible-libfuzzer-model)

Exercises the full llama model loading stack: GGUF parse → tensor weight accumulation → architecture dispatch → hyperparameter loading. This reaches deep into llama_model_load() and the per-architecture initialization code.

Best for: Model-loader assertion failures, architecture dispatch bugs, hyperparameter overflow.

Resource usage

The model harness may attempt large allocations. Use ASAN_OPTIONS="allocator_may_return_null=1:halt_on_error=0:detect_leaks=0" and -rss_limit_mb=8192.

Clip (crucible-libfuzzer-clip)

Targets clip_init() in the MTMD (multimodal) library, which loads vision and audio models. This exercises a completely separate parsing path from the main llama model loader.

Best for: Vision model bugs, CLIP-specific parsing, multimodal attack surface.

Quant (crucible-libfuzzer-quant)

Targets ggml_quantize_chunk() and the type-trait to_float (dequantize) functions. These process raw tensor data and are reachable any time a model with quantized weights is loaded.

Best for: Quantization arithmetic bugs, type-specific dequantization overflows. Uses -max_len=4096 since inputs are raw data blocks, not full GGUF files.

Roundtrip (crucible-libfuzzer-roundtrip)

Exercises the GGUF write path by parsing fuzz input via gguf_init_from_file(), then calling gguf_write_to_file() on the result.

Best for: Write-path bugs, serialization crashes, uninitialized field access.

Dequant (crucible-libfuzzer-dequant)

Fuzzes dequantize_row_* functions directly — no GGUF parsing involved. Raw bytes are fed straight to the dequantization routines for each quantization type. This targets a completely unexplored attack surface with zero prior CVEs.

Best for: Low-level dequantization bugs, type-specific arithmetic errors.

Tensor Size (crucible-libfuzzer-tensor-size)

Targets ggml_nbytes() and related tensor size arithmetic. VulDB rates the integer overflow in this path at CVSS 10.0.

Best for: Integer overflow in size calculations, allocation sizing bugs.

b3561 Regression (crucible-libfuzzer-b3561)

Simplified version of the primary harness linked against a pre-fix version of llama.cpp (commit b3561). Used to verify that Crucible can rediscover CVE-2024-23496.

Best for: Regression testing, CVE rediscovery validation.

RPC (crucible-libfuzzer-rpc)

Targets the ggml-rpc server-side protocol handler via a socketpair that feeds fuzzer bytes directly into rpc_serve_client(). This exercises the full RPC command dispatch — tensor creation, buffer operations, and compute graph execution — without network overhead.

Best for: Protocol parsing bugs, deserialization of untrusted tensor metadata, server-side memory corruption.

RPC Commands (crucible-libfuzzer-rpc-commands)

Targets all 17 RPC commands with structured fuzzing — performs a proper HELLO handshake, then dispatches per-command payloads including tensor creation, buffer operations, SET_TENSOR with crafted dimensions/types, and GRAPH_COMPUTE with multi-tensor graphs. Unlike the basic RPC harness which feeds raw bytes, this harness generates protocol-valid messages that exercise deep command handling logic.

Best for: Per-command input validation bugs, tensor dimension misalignment, memory corruption in specific RPC operations.

Tokenizer (crucible-libfuzzer-tokenizer)

Fuzzes llama_tokenize() with arbitrary UTF-8 and non-UTF-8 text input against loaded vocabulary models (BPE or SentencePiece). Requires CRUCIBLE_VOCAB environment variable pointing to a vocabulary GGUF file. Exercises the full tokenization pipeline: text normalization, regex pre-tokenization (unicode_regex_split), and BPE/SPM token encoding.

Best for: Tokenizer crashes from malformed text input, Unicode handling bugs, regex engine failures. Reachable via any API endpoint that tokenizes user text.

Grammar (crucible-libfuzzer-grammar)

Fuzzes the GBNF grammar parser (llama_grammar_parser::parse()), a recursive descent parser that converts grammar strings into internal rule representations used for constrained generation.

Best for: Grammar parsing crashes, recursive descent stack overflow, malformed rule handling.

Jinja (crucible-libfuzzer-jinja)

Exercises the full Jinja template engine pipeline: lexer (tokenization), parser (AST construction), and runtime (template rendering with context). Zero external dependencies — pure C++ implementation.

Best for: Template injection, lexer/parser crashes, runtime evaluation bugs. Templates are user-controlled in chat applications.

JSON Schema (crucible-libfuzzer-json-schema)

Targets json_schema_to_grammar(), which converts JSON Schema documents into GBNF grammar strings. This exercises complex schema traversal including $ref resolution, oneOf/anyOf combinators, and pattern compilation.

Best for: Schema traversal crashes, regex pattern compilation bugs, recursive reference handling.

Unicode (crucible-libfuzzer-unicode)

Fuzzes unicode_regex_split(), the custom regex engine used for BPE/GPT2/Llama3 tokenizer pre-tokenization. This engine processes arbitrary UTF-8 text input during tokenization.

Best for: Regex engine crashes, Unicode boundary bugs, malformed UTF-8 handling. Reachable via any tokenization code path.

PEG (crucible-libfuzzer-peg)

Targets the PEG (Parsing Expression Grammar) engine used for structured output parsing and tool-call detection. Fuzzes both JSON deserialization of grammar definitions (from_json()) and the matching engine (parse()).

Best for: Grammar deserialization crashes, matching engine bugs, recursive grammar handling.

Chat Parser (crucible-libfuzzer-chat-parser)

Fuzzes tagged_peg_parser::parse_and_extract(), which parses tool calls and structured output from raw LLM response text. This is reachable during inference when the model generates text matching tool-call patterns.

Best for: Tool-call extraction bugs, parser state machine errors, malformed output handling.

Regex Partial (crucible-libfuzzer-regex-partial)

Targets regex_to_reversed_partial_regex() and common_regex, which transform and match regular expressions for grammar-constrained generation. The regex-to-partial transformation involves complex string parsing logic.

Best for: Regex transformation bugs, iterator invalidation, out-of-bounds reads on malformed patterns.

Whisper (crucible-libfuzzer-whisper)

Targets whisper.cpp model loading via whisper_init_from_buffer_with_params(). Exercises the GGML model binary format parser including header parsing, hyperparameter loading, and tensor deserialization. External target — requires a local whisper.cpp build.

Best for: Whisper model format parsing bugs, cross-project validation of shared gguf.cpp code paths.

RPC Graph (crucible-libfuzzer-rpc-graph)

Targets RPC_CMD_GRAPH_COMPUTE (command 10) in adversarial mode — no clamping or sanitization of op codes, tensor dimensions, strides, or flags. The harness sends a HELLO handshake followed by the raw fuzz input as a GRAPH_COMPUTE payload via a socketpair, exercising the full graph deserialization → cgraph construction → ggml_graph_compute() path. Uses longjmp recovery to survive GGML_ASSERT failures.

Best for: Function-pointer RCE via GGML_OP_CUSTOM (CRUCIBLE-2026-005), block-size misalignment heap undersize (CRUCIBLE-2026-012), graph traversal OOB from count mismatches, src-chain null-deref/UAF from garbage tensor handles, stride-based OOB access, division-by-zero in tensor size calculations.

The -mutator variant uses a Go structure-aware mutator with 14 strategies across 6 categories (Op 25%, OpParams 25%, Dimensions 20%, Graph 15%, Strides 10%, Flags 5%) that generate protocol-valid GraphComputePayload messages targeting known vulnerability patterns.

Campaign results

First campaign found crashes within seconds at ggml.c:7038 (null pointer offset in tensor computation) and ggml.c:1502 (null deref via ggml_is_empty). Corpus grew from 8 seeds to 2,000+ in minutes.

RPC Race (crucible-libfuzzer-rpc-race)

Creates two concurrent connections to a single rpc_serve_client() instance via socketpairs, each serviced by its own thread sharing the same g_backends vector. The fuzz input is split 50/50 between the two connections, with commands sent in alternating rounds to maximize interleaving. Focuses on state-mutating operations: ALLOC_BUFFER, FREE_BUFFER, SET_TENSOR, GET_TENSOR, COPY_TENSOR, and GRAPH_COMPUTE.

Best for: Data races, use-after-free from concurrent buffer operations, state confusion between connections. Build with ThreadSanitizer (-fsanitize=thread) for data race detection.

Attack patterns

  • ALLOC_BUFFER on conn A → FREE_BUFFER on conn B → SET_TENSOR on conn A (use-after-free)
  • GRAPH_COMPUTE on both connections simultaneously (racy compute graph execution)
  • SET_TENSOR on conn A while GET_TENSOR on conn B (torn read/write)

Whisper Audio (crucible-libfuzzer-whisper-audio)

Two-stage inference harness: loads a known-good whisper model once at startup (from CRUCIBLE_WHISPER_MODEL env var), then interprets each fuzz input as raw PCM float32 audio samples and feeds them to whisper_full(). This reaches 225+ functions in the inference pipeline that model-loading fuzzing cannot touch:

  • Mel spectrogram computation (whisper_pcm_to_mel)
  • Encoder forward pass (conv1d, GELU, layer norm, self-attention)
  • Decoder forward pass (cross-attention, token prediction)
  • Greedy sampling and token detokenization

Best for: Inference-path bugs, numerical instability, NaN/Inf propagation, decoder assertion failures. Caps input at 5 seconds of audio (5 × 16000 × 4 bytes = 320KB).

Resource usage

Each worker loads the whisper model (~77MB for ggml-tiny.bin) into memory. Use -workers=2 to limit memory consumption. Requires CRUCIBLE_WHISPER_MODEL pointing to a downloaded model file.

Stable Diffusion Model (crucible-libfuzzer-sd-model)

Targets ModelLoader::init_from_file() in stable-diffusion.cpp, a multi-format model loader that auto-detects and parses GGUF, SafeTensors, and Checkpoint (.ckpt) formats. (Diffusers format requires a directory structure and is unreachable from this file-based harness.) Wraps the loader in a try/catch block — set CRUCIBLE_STRICT=1 to disable exception suppression for targeted investigation. External target — requires a local stable-diffusion.cpp build.

Best for: Multi-format parsing bugs, format auto-detection confusion, cross-project GGUF vulnerability validation.

LoRA (crucible-libfuzzer-lora)

Fuzzes llama_adapter_lora_init(), which parses a LoRA adapter GGUF file and validates it against a base model's metadata and tensor shapes. Requires CRUCIBLE_VOCAB environment variable pointing to a vocabulary-only GGUF file as the base model. Exercises the LoRA-specific parsing path including adapter weight validation and shape compatibility checks.

Best for: LoRA adapter parsing bugs, shape validation bypasses, metadata compatibility checking errors.

Grammar Compile (crucible-libfuzzer-grammar-compile)

Fuzzes the full GBNF grammar compilation pipeline via llama_grammar_init_impl() — goes beyond the grammar parser harness to also exercise rule stack building and left-recursion detection. Uses vocab=nullptr to focus on grammar compilation rather than token matching.

Best for: Grammar compilation crashes, left-recursion detection bugs, stack building errors. Complements the grammar parser harness which only exercises parsing.

Chat Template (crucible-libfuzzer-chat-template)

Fuzzes llama_chat_apply_template() across ~50 built-in chat template format variants. Pure string manipulation — no model loading required. Tests template rendering with arbitrary input text to find formatting bugs.

Best for: Chat template formatting crashes, buffer handling bugs in template rendering, edge cases in template selection logic.

Server (crucible-libfuzzer-server)

Exercises the llama-server HTTP parsing pipeline without a running HTTP server. Fuzzes oaicompat_chat_params_parse() (OpenAI-compatible chat completions), tokenize_mixed() (mixed content tokenization), json_schema_to_grammar() (schema constraint compilation), and llama_chat_apply_template() (template rendering).

Model requirements:

  • Vocab-only GGUF (CRUCIBLE_VOCAB): required for tokenization paths (tokenize_mixed, EP_TOKENIZE 0x40-0x5F).
  • Template-bearing GGUF: required for chat-completions paths (oaicompat_chat_params_parse, EP_CHAT_COMPLETIONS 0x00-0x3F). A vocab-only file will cause those paths to be silently skipped during fuzzing and will produce a hard error during replay (CRUCIBLE_REPLAY=1). Use a full instruction-tuned model (e.g. Llama-3.1-8B-Instruct) when you need chat-completions coverage.

Best for: API endpoint parsing bugs, JSON request handling crashes, schema-to-grammar conversion errors reachable via HTTP. Tests the most exposed network-facing attack surface of llama.cpp.

TorchScript (crucible-libfuzzer-torchscript)

Targets torch::jit::load(), PyTorch's TorchScript model deserialization. This exercises the full model loading pipeline: ZIP container parsing, pickle deserialization (which historically has been a rich source of RCE vulnerabilities), tensor data loading, and JIT compilation. External target — requires a local libtorch build.

Best for: TorchScript deserialization crashes, pickle parsing bugs, ZIP container handling errors, tensor loading overflows. High-value bounty target via huntr.com.

Resource usage

Set GLOG_minloglevel=3 and TORCH_SHOW_CPP_STACKTRACES=0 to suppress verbose PyTorch logging that otherwise drowns libFuzzer output.

Torch Load (crucible-libfuzzer-torch-load)

Targets PyTorchStreamReader with a bounds-checked SafeMemoryAdapter that catches OOB reads the stock MemoryReadAdapter misses. Focuses on the low-level ZIP container parsing layer that handles model archive structure before pickle deserialization begins.

Best for: ZIP container parsing OOB reads, archive structure validation gaps, memory safety bugs in the model loading infrastructure layer.

TFLite (crucible-libfuzzer-tflite)

Targets TensorFlow Lite model loading via FlatBufferModel::BuildFromBuffer()InterpreterBuilderAllocateTensors()Invoke(). Exercises FlatBuffer parsing, operator kernel dispatch, and tensor allocation. External target — requires a local TensorFlow Lite build.

Best for: FlatBuffer parsing bugs, operator dispatch crashes, tensor allocation overflows, model validation bypasses.

ONNX (crucible-libfuzzer-onnx)

Targets ONNX Runtime model loading via Ort::Session(), which deserializes protobuf-encoded model files, builds the computation graph, and runs the optimization pipeline. External target — requires a local ONNX Runtime build.

Best for: Protobuf deserialization crashes, graph building errors, optimization pass crashes. Experimental — ONNX Runtime has extensive internal fuzzing.

MLX GGUF (crucible-libfuzzer-mlx-gguf)

Targets Apple MLX's GGUF loader via mlx::core::load_gguf(). Includes a preflight validation step that traverses KV and tensor sections before calling the loader, catching bounds-checking vulnerabilities in the underlying gguflib library. External target — requires a local MLX build.

Best for: MLX-specific GGUF parsing bugs, gguflib vulnerabilities (used by MLX internally), cross-project GGUF validation.

MLX SafeTensors (crucible-libfuzzer-mlx-safetensors)

Targets Apple MLX's SafeTensors format loader via mlx::core::load_safetensors(). The SafeTensors format uses a JSON header followed by raw tensor data, making it a simpler but still security-relevant attack surface. External target — requires a local MLX build.

Best for: SafeTensors JSON header parsing bugs, tensor data validation errors, allocation sizing overflows.

Coverage Driver (cov_driver.cpp)

Not a libFuzzer harness — a deterministic standalone tool that feeds corpus files through GGUF parsing one at a time to measure code coverage via LLVM source-based instrumentation. Used with crucible coverage report to generate HTML coverage reports.

Best for: Coverage measurement, not fuzzing. Run after a campaign to assess which code paths the corpus exercises.

Building

All harnesses are built via the harness/libfuzzer/Makefile.

Prerequisites: A local llama.cpp clone built with CMake into build-fuzz/ with ASan+UBSan+fuzzer-no-link instrumentation. External targets (whisper, sd-model) require their respective source builds.

# Build a specific harness
make -C harness/libfuzzer deep LLAMA_CPP=~/src/llama.cpp

# Build with custom mutator
make -C harness/libfuzzer deep-mutator LLAMA_CPP=~/src/llama.cpp

# Build protocol, grammar, and server harnesses
make -C harness/libfuzzer rpc rpc-commands rpc-race grammar grammar-compile jinja json-schema unicode peg chat-parser chat-template regex-partial server \
  LLAMA_CPP=~/src/llama.cpp

# Build external target harnesses
make -C harness/libfuzzer whisper WHISPER_CPP=~/src/whisper.cpp
make -C harness/libfuzzer whisper-audio WHISPER_CPP=~/src/whisper.cpp
make -C harness/libfuzzer sd-model SD_CPP=~/src/stable-diffusion.cpp

# Build ML framework harnesses
make -C harness/libfuzzer torchscript torch-load LIBTORCH=~/src/libtorch
make -C harness/libfuzzer tflite TFLITE=~/src/tensorflow
make -C harness/libfuzzer onnx ONNXRUNTIME=~/src/onnxruntime
make -C harness/libfuzzer mlx-gguf mlx-safetensors MLX=~/src/mlx

# Build all GGUF harnesses at once
make -C harness/libfuzzer all deep model clip quant roundtrip dequant tensor-size lora \
  LLAMA_CPP=~/src/llama.cpp

The -mutator variants require the Go custom mutator archive. Build it first with:

make -C harness/libfuzzer go-mutator

Running Campaigns

Each harness is a standard libFuzzer binary:

# Run the deep harness with 4 workers
./harness/libfuzzer/crucible-libfuzzer-deep corpus/generated \
  -artifact_prefix=crashes/deep/ \
  -max_len=10485760 \
  -timeout=30 \
  -jobs=4 -workers=4

# Run grammar engine harnesses (text input, smaller max_len)
./harness/libfuzzer/crucible-libfuzzer-json-schema corpus/json-schema-seeds \
  -artifact_prefix=crashes/json-schema/ \
  -max_len=65536 \
  -timeout=10 \
  -jobs=4 -workers=4

# Run RPC harness (binary protocol, moderate size)
./harness/libfuzzer/crucible-libfuzzer-rpc corpus/rpc-seeds \
  -artifact_prefix=crashes/rpc/ \
  -max_len=1048576 \
  -timeout=10 \
  -jobs=4 -workers=4

# Run quant harness (smaller inputs)
./harness/libfuzzer/crucible-libfuzzer-quant corpus/quant-seeds \
  -artifact_prefix=crashes/quant/ \
  -max_len=4096 \
  -timeout=10 \
  -detect_leaks=0

# Run model harness with permissive ASAN settings
ASAN_OPTIONS="allocator_may_return_null=1:halt_on_error=0:detect_leaks=0" \
  ./harness/libfuzzer/crucible-libfuzzer-model-mutator corpus/generated \
  -artifact_prefix=crashes/model/ \
  -max_len=52428800 \
  -rss_limit_mb=8192 \
  -timeout=30 \
  -jobs=4 -workers=4

# Run external targets — whisper model loading
./harness/libfuzzer/crucible-libfuzzer-whisper corpus/generated \
  -artifact_prefix=crashes/whisper/ \
  -max_len=10485760 \
  -timeout=30 \
  -jobs=4 -workers=4

# Run whisper audio inference (requires model file)
CRUCIBLE_WHISPER_MODEL=~/src/whisper.cpp/models/ggml-tiny.bin \
  ./harness/libfuzzer/crucible-libfuzzer-whisper-audio corpus/whisper-audio \
  -artifact_prefix=crashes/whisper-audio/ \
  -max_len=320000 \
  -timeout=60 \
  -jobs=4 -workers=2

# Run RPC race condition harness
./harness/libfuzzer/crucible-libfuzzer-rpc-race corpus/rpc-seeds \
  -artifact_prefix=crashes/rpc-race/ \
  -max_len=1048576 \
  -timeout=30 \
  -jobs=4 -workers=2

# Run server endpoint harness (requires vocab file)
CRUCIBLE_VOCAB=corpus/real/tinyllama.gguf \
  ./harness/libfuzzer/crucible-libfuzzer-server corpus/server \
  -artifact_prefix=crashes/server/ \
  -max_len=65536 \
  -timeout=10 \
  -jobs=4 -workers=4

# Run TorchScript harness
GLOG_minloglevel=3 TORCH_SHOW_CPP_STACKTRACES=0 \
  ./harness/libfuzzer/crucible-libfuzzer-torchscript corpus/torchscript \
  -artifact_prefix=crashes/torchscript/ \
  -max_len=10485760 \
  -timeout=30 \
  -jobs=4 -workers=4

# Run TFLite harness
./harness/libfuzzer/crucible-libfuzzer-tflite corpus/tflite \
  -artifact_prefix=crashes/tflite/ \
  -max_len=10485760 \
  -timeout=30 \
  -jobs=4 -workers=4

# Run MLX GGUF harness
./harness/libfuzzer/crucible-libfuzzer-mlx-gguf corpus/generated \
  -artifact_prefix=crashes/mlx-gguf/ \
  -max_len=10485760 \
  -timeout=30 \
  -jobs=4 -workers=4

Mutator variants

The -mutator variants generally find bugs faster because every mutation is structurally valid. Use them when you have enough CPU budget for the slightly higher per-iteration cost of Go-based mutation.

Known Limitations

Tokenizer Harness Defaults to BPE

The tokenizer harness (crucible-libfuzzer-tokenizer) requires a vocabulary file via CRUCIBLE_VOCAB. Current campaigns use BPE and SPM vocabularies only. Other tokenizer types (WPM, UGM) are not covered. To fuzz additional tokenizer types, point CRUCIBLE_VOCAB at the appropriate vocabulary GGUF file.

Chat Parser Uses Synthetic Grammars

The chat-parser harness (crucible-libfuzzer-chat-parser) fuzzes tagged_peg_parser::parse_and_extract() with synthetic PEG grammars. Real-world tool-call templates may exercise different parser paths. Coverage could be improved by seeding with production chat templates.

SD Model — Diffusers Format Unreachable

The SD model harness writes fuzz input to a flat temporary file and calls ModelLoader::init_from_file(). The Diffusers format requires a directory tree with multiple files, making it unreachable from this file-based harness. Only GGUF, SafeTensors, and Checkpoint (.ckpt) formats are effectively fuzzed.