Skip to content

crucible-mutator

CGo library that exposes Crucible's structure-aware mutator as a libFuzzer custom mutator and crossover implementation.

Purpose

When linked into a libFuzzer harness, crucible-mutator replaces libFuzzer's default byte-level mutation with Crucible's structure-aware mutation engine. This means every mutation operates on parsed structures rather than random bytes, dramatically improving code coverage in deep parsing paths.

The -mutator harness variants (e.g., crucible-libfuzzer-deep-mutator) use this library.

Build

# Build the C archive from the project root
CGO_ENABLED=1 go build -buildmode=c-archive -o harness/libfuzzer/libcruciblemut.a ./cmd/crucible-mutator

# Or via the harness Makefile
make -C harness/libfuzzer go-mutator

This produces libcruciblemut.a and libcruciblemut.h, which are linked into harness binaries at compile time.

Exported Functions

LLVMFuzzerCustomMutator

size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxSize, unsigned int seed);

Called by libFuzzer instead of its built-in mutator. Performs structure-aware mutation:

  1. Copies the input buffer into Go-managed memory
  2. Parses the bytes as a GGUF file
  3. Applies 1–3 weighted random mutations via mutator.MutateBytes()
  4. Writes the result back into libFuzzer's buffer (truncated to maxSize)
  5. Returns the output length

If mutation fails (e.g., input is not parseable as GGUF), returns the original input unchanged.

LLVMFuzzerCustomCrossOver

size_t LLVMFuzzerCustomCrossOver(
    const uint8_t *data1, size_t size1,
    const uint8_t *data2, size_t size2,
    uint8_t *out, size_t maxOutSize,
    unsigned int seed);

Called by libFuzzer to combine two corpus entries. Implements structure-aware crossover:

  1. Parses both inputs as GGUF files
  2. Applies one of 5 crossover strategies (header swap, metadata merge, tensor info swap, data splice, full recombination)
  3. Serializes the result into the output buffer

Falls back to mutation of the first input if crossover fails, or returns the first input verbatim if both fail.

Usage

The mutator library is not used directly. Instead, build a -mutator variant of any harness:

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

# Run it — libFuzzer automatically detects and uses the custom mutator
./harness/libfuzzer/crucible-libfuzzer-deep-mutator corpus/ \
  -artifact_prefix=crashes/ \
  -max_len=10485760 \
  -timeout=30

Available -mutator variants: mutator, deep-mutator, model-mutator, clip-mutator, quant-mutator, roundtrip-mutator, dequant-mutator.