Skip to content

Mutation Strategies

Crucible ships 60 mutation strategies across two structure-aware mutators: 46 targeting GGUF file format parsing (6 categories) and 14 targeting RPC GRAPH_COMPUTE wire format (6 categories). Each strategy targets a specific structural component to trigger a known class of bugs.

Category weights

The mutator selects categories using weighted random sampling: Metadata (35%), TensorInfo (35%), Header (10%), Consistency (10%), Alignment (5%), Data (5%). Higher weights target sections with the most parser complexity and historical bug density.


Header Strategies

Target the 24-byte fixed header that every GGUF parser reads first.

Strategy Description Bug Pattern
header.version Fuzz version field: 0, 1, 2, UINT32_MAX, random Version handling bugs
header.tensor_count Mismatch count with actual tensor info blocks Buffer overflow on allocation
header.metadata_kv_count Mismatch count with actual KV pairs Over-read past metadata section
header.magic_corrupt Partial magic corruption (keep 1-2 valid bytes) Error path bugs
header.version_mismatch Set version that disagrees with field sizes used Version-dependent parsing bugs

Metadata Strategies

Target the variable-length key-value section. This is the largest attack surface in most GGUF parsers.

Strategy Description Bug Pattern
metadata.key_length Empty keys, 1MB+ keys, embedded null bytes, declared-length mismatches (encoded length != actual key bytes) String handling overflow, parser field desync
metadata.key_content Non-UTF8 sequences, null sleds, path traversal strings, surrogate pairs Encoding bugs
metadata.value_type Invalid enum values (14+, UINT32_MAX), type confusion between similar types Type confusion / wrong getter
metadata.string_value Empty strings, 10MB strings, embedded nulls, non-UTF8 String processing overflow
metadata.array Empty arrays, nested arrays, element type mismatch, large arrays (100K+ elements) Array handling overflow
metadata.int_overflow UINT32_MAX, UINT64_MAX, INT64_MIN in integer fields Integer overflow
metadata.alignment_poison Set general.alignment to 0, 1, 3, 7, UINT32_MAX Division by zero, huge allocation
metadata.key_shadow Duplicate keys like general.architecture with conflicting value types Duplicate key confusion
metadata.add_extra Inject 50-250 extra KV pairs with random types and large values Parser stress / OOM
metadata.invalid_utf8 Inject non-UTF-8 byte sequences in string values Encoding validation bugs
metadata.reorder Randomize the order of metadata key-value pairs Order-dependent parsing bugs
metadata.deep_array Create arrays nested to extreme depth; arrays whose declared element count exceeds actual data (1M declared, 2 emitted) Stack overflow in recursive parsing, read past end of array
metadata.string_truncated Declared string length exceeds actual bytes available (via on-wire length override) Read past end of metadata section

TensorInfo Strategies

Target the per-tensor descriptor blocks that parsers use to locate and allocate tensor data.

Strategy Description Bug Pattern
tensorinfo.n_dims Set dimensions to 0, 5+, UINT32_MAX (spec allows 1-4) Out-of-bounds dimension read
tensorinfo.dim_overflow Set individual dimension values to 0 or UINT64_MAX Allocation size bugs
tensorinfo.type Invalid ggml_type enum values (5, 15, 255, UINT32_MAX) Type lookup crash
tensorinfo.offset Offset beyond file size, UINT64_MAX, overlapping with other tensors Out-of-bounds read
tensorinfo.name Empty names, 1MB names, embedded nulls, non-UTF8, duplicate names String overflow, dedup bugs
tensorinfo.dim_product_overflow Dimension values whose product overflows uint64 Undersized alloc + oversized read/write
tensorinfo.name_collision Give two tensors the same name Deduplication and lookup bugs
tensorinfo.offset_wraparound Offset + size wraps uint64, bypassing bounds checks Out-of-bounds memory access

Alignment Strategies

Target the padding calculations between the metadata section and tensor data.

Strategy Description Bug Pattern
alignment.padding Set alignment to 0, prime numbers, UINT32_MAX, OS page size Padding calculation crash
alignment.extra_padding Insert random non-zero bytes before tensor data section Offset miscalculation
alignment.missing_padding Metadata claims alignment but padding bytes are absent Missing padding handling

Data Strategies

Target the raw tensor data blob at the end of the file.

Strategy Description Bug Pattern
data.truncate Truncate data section mid-tensor Read past end of file
data.overlap Multiple tensors pointing to the same offset Double-read, data confusion
data.zero_length Empty data section with non-zero tensor count Null pointer / zero-size alloc
data.shorter Data section shorter than the sum of all tensor sizes Partial read overflow
data.garbage_fill Fill data section with random bytes Data corruption handling
data.nan_inf Inject NaN and Infinity values into tensor data Special float handling bugs

Consistency Strategies

Violate cross-section invariants that parsers may assume hold true.

Strategy Description Bug Pattern
consistency.tensor_count Header tensor count != actual tensor info block count Over-read / under-read
consistency.metadata_count Header metadata count != actual KV pair count Parser desync
consistency.offset_beyond Tensor offset + tensor data size > total file size Out-of-bounds read
consistency.tensor_size Dimensions claim X bytes but actual data region is Y bytes Size mismatch overflow
consistency.duplicate_offset Multiple tensors claim the same offset range Aliased memory access
consistency.alignment_disagree Metadata alignment value != actual file padding alignment Offset miscalculation

Model-Loader Strategies

Target the model-loading path (llama_model_load) beyond raw GGUF parsing. These strategies mutate architecture keys, hyperparameters, vocabulary fields, and layer counts to trigger integer overflows and assertion failures in the model loading and architecture dispatch code.

Category weighting

Model-loader strategies are registered under the Metadata category for weighting purposes (35% selection probability). They share the metadata budget because they operate on metadata key-value pairs that feed into model initialization.

Strategy Description Bug Pattern
model.architecture Set general.architecture to unknown, empty, or malformed values (null bytes, path traversal strings) Architecture dispatch crash, unhandled enum
model.hyperparam Set hyperparameter keys (embedding_length, head_count, block_count, etc.) to 0, UINT32_MAX, or other boundary values Integer overflow in allocation sizing
model.vocab Mutate tokenizer keys (tokenizer.ggml.model, bos_token_id, eos_token_id) with invalid strings or extreme integer values Tokenizer initialization crash
model.layer_count Set block_count to 0, UINT32_MAX, or values mismatched with actual tensor count Layer iteration overflow, assertion failure
model.tensor_name_schema Corrupt tensor names to break the blk.N.attn_q.weight naming convention parsers rely on Tensor lookup failure, null dereference

Strategy Summary

Category Count Weight Primary Target
Header 5 10% Fixed header fields
Metadata 13 35% Key-value parsing
TensorInfo 8 35% Tensor descriptor blocks
Alignment 3 5% Padding between sections
Data 6 5% Raw tensor data blob
Consistency 6 10% Cross-section invariants
Model-Loader 5 (shared with Metadata) Model initialization path
Total 46 100%

Composability

The mutator may apply multiple strategies in a single pass. Combined mutations (e.g., header.tensor_count + consistency.offset_beyond) often trigger bugs that no single mutation would reach alone.

Header count synchronization

By default, the mutator calls SyncCounts() after all mutations to ensure Header.MetadataKVCount and Header.TensorCount match the actual slice lengths. Strategies that append metadata or tensors (e.g., metadata.add_extra, metadata.key_shadow, tensorinfo.name_collision) automatically get correct header counts. Strategies that intentionally set wrong counts (consistency.tensor_count, consistency.metadata_count, header.tensor_count, header.metadata_kv_count, metadata.string_truncated) opt out of this sync via the CountKeeper interface.


RPC Graph-Compute Strategies

A separate structure-aware mutator targets the RPC_CMD_GRAPH_COMPUTE wire format — the GraphComputePayload message containing tensor descriptors and computation graph metadata. These strategies are implemented in Go (pkg/mutator/rpc/) and exported as a CGo c-archive linked into the -rpc-graph-mutator harness variant.

Category weights

The RPC mutator selects categories using weighted random sampling: Op (25%), OpParams (25%), Dimensions (20%), Graph (15%), Strides (10%), Flags (5%). Higher weights target the op dispatch and parameter passing paths where function-pointer RCE (CRUCIBLE-2026-005) and block-size assertion failures (CRUCIBLE-2026-012) were found.

Op Strategies

Target the ggml_op enum field that controls which computation kernel executes.

Strategy Description Bug Pattern
op.custom Sets Op to CUSTOM/MAP_CUSTOM variants (87–90) + COMPUTE flag Function-pointer RCE via ggml_compute_forward_custom() (005)
op.invalid Sets Op to out-of-range/boundary values (OP_BOUND, 0xFFFFFFFF) GGML_ABORT in default case of ggml_compute_forward()

OpParams Strategies

Target the op_params array (32 × int32) passed to computation kernels.

Strategy Description Bug Pattern
opparams.funcptr Sets Op=CUSTOM and plants function-pointer-shaped 64-bit values in OpParams[0:1] Direct RCE via function pointer struct layout
opparams.random Fills all OpParams slots with random/boundary int32 values Bugs in op handlers reading beyond index 0

Dimensions Strategies

Target the ne[] (number of elements) array that controls tensor shape.

Strategy Description Bug Pattern
dimensions.blck_misalign Sets NE[0] to non-multiple of block size for quantized types Assert in ggml_row_size(), heap undersizing (012)
dimensions.overflow Sets NE[] entries to extreme values (0, 0xFFFFFFFF, 0x7FFFFFFF) Size-calculation overflow in ggml_nbytes()
dimensions.zero_dim Sets NE[1..3] to zero Division-by-zero (same class as 053)

Graph Strategies

Target the graph structure: node count, tensor count, and source references.

Strategy Description Bug Pattern
graph.count_mismatch Sets NNodes > NTensors (1–16 phantom nodes) OOB memory access in graph traversal loop
graph.large Adds 4–32 cloned tensors with randomized op/type Stress graph_compute() and cgraph allocation
graph.src_chain Wires Src[] to create dependency chains, self-references, cycles, garbage handles Null-deref or UAF in tensor lookup during cgraph construction

Strides Strategies

Target the nb[] (number of bytes) stride array.

Strategy Description Bug Pattern
strides.inconsistent Sets NB[0] inconsistent with type's element size Stride-based OOB access
strides.extreme Sets all NB[] to extreme boundary values (0, 1, 0xFFFFFFFF) Unsigned stride arithmetic overflow

Flags Strategies

Target the tensor flags bitfield.

Strategy Description Bug Pattern
flags.compute Sets GGML_TENSOR_FLAG_COMPUTE on a random tensor Enabler — makes ops actually execute
flags.all Sets flags to extreme/boundary values (0, 0xFF, 0x7FFFFFFF, -1) Flag-gated code path bugs

RPC Mutator Summary

Category Count Weight Primary Target
Op 2 25% Op dispatch / kernel selection
OpParams 2 25% Function pointer / parameter passing
Dimensions 3 20% Tensor shape / size calculations
Graph 3 15% Graph structure / traversal
Strides 2 10% Stride arithmetic / memory layout
Flags 2 5% Tensor flags / execution gating
Total 14 100%

Mutation behavior

The RPC mutator applies 1–3 random strategies per iteration. CrossOver transplants one tensor from a second input into the first at a random position. On parse failure, the mutator falls back to a minimal default (single F32 tensor) to maintain valid structure.