`mutatis` is a library for writing custom, structure-aware test-case mutators for fuzzers in Rust.

Overview

mutatis

Easily create custom, structure-aware mutators for fuzzing.

crates.io docs.rs supported rustc stable

About

The most popular fuzzers — including libfuzzer and AFL — are coverage-guided and mutation-based.

Coverage-guided means that the fuzzer observes which code is dynamically executed while running an input through the system under test. When creating new inputs, it will try to make inputs that execute new code paths, maximizing the amount of code that's been explored. If a new input triggers new code paths to be executed, then it is added to the corpus. If a new input only exercises code paths that have already been discovered, then it is thrown away.

Mutation-based means that, when creating a new input, the fuzzer modifies an existing input from its corpus. The idea is that, if the existing input triggered interesting behavior in the system under test, then a modification of that input probably will as well, but might additionally trigger some new behavior as well. Consider the scenario where we are fuzzing a compiler: if some input made it all the way through the parser, type checker, and into code generation — rather than bouncing off early due to an invalid token — then a new input derived from this one is also likely to go deep into the compiler's pipeline. At least it is more likely to do so than a completely new, random string.

But what happens when we aren't fuzzing a text or binary interface? What happens when we have a custom input type that the fuzzer's built-in mutation strategies aren't very good at targeting? Many fuzzers will expose a hook for customizing the routine for mutating an existing input from its corpus to create a new candidate input, for example libfuzzer has the fuzz_mutator! hook.

mutatis exists to make writing these custom mutators easy and efficient.

Using Default Mutators

To randomly mutate a value with its default, off-the-shelf mutator:

Here's a simple example of using mutatis and its default mutators to randomly mutate a value:

# fn foo() -> mutatis::Result<()> {
let mut point = (42, 36);

let mut session = mutatis::Session::new();
for _ in 0..3 {
    session.mutate(&mut point)?;
    println!("mutated point is {point:?}");
}

// Example output:
//
//     mutated point is (-565504428, 36)
//     mutated point is (-565504428, 49968845)
//     mutated point is (-1854163941, 49968845)
# Ok(())
# }
# foo().unwrap()

Combining and Customizing Mutators

You can use the mutator combinators in the mutatis::mutators module to build more complex mutators from simpler ones or to customize mutation strategies to, for example, maintain a type's internal invariants or bound the resulting values into a particular range. The mutatis::mutators module is typically imported under the alias m.

To randomly mutate a value with a custom mutator:

Here's an example of using mutatis to define a custom mutator for a custom struct type that has multiple fields, and maintains a relationship between the fields' values:

# fn foo() -> mutatis::Result<()> {
use mutatis::{mutators as m, Mutate, Session};

/// A scary monster type.
#[derive(Debug)]
pub struct Monster {
    pos: [i32; 2],
    hp: u16,

    // Invariant: ghost's are already dead, so when `is_ghost = true` it must
    // always be the case that `hp = 0`.
    is_ghost: bool,
}

/// A mutator that mutates one of a monster's fields, while maintaining our
/// invariant that ghosts always have zero HP.
let mut mutator =
    // Mutate the `pos` field...
    m::array(m::i32()).proj(|x: &mut Monster| &mut x.pos)
        // ...or mutate the `hp` field...
        .or(
            m::u16()
                .proj(|x: &mut Monster| &mut x.hp)
                .map(|_ctx, monster| {
                    // If we mutated the `hp` such that it is non-zero, then the
                    // monster cannot be a ghost.
                    if monster.hp > 0 {
                        monster.is_ghost = false;
                    }
                    Ok(())
                }),
        )
        // ...or mutate the `is_ghost` field.
        .or(
            m::bool()
                .proj(|x: &mut Monster| &mut x.is_ghost)
                .map(|_ctx, monster| {
                    // If we turned this monster into a ghost, then its `hp`
                    // must be zero.
                    if monster.is_ghost {
                        monster.hp = 0;
                    }
                    Ok(())
                }),
        );

// Define a monster...
let mut monster = Monster {
    hp: 36,
    is_ghost: false,
    pos: [-8, 9000],
};

// ...and mutate it a bunch of times!
let mut session = Session::new();
for _ in 0..5 {
    session.mutate_with(&mut mutator, &mut monster)?;
    println!("mutated monster is {monster:?}");
}

// Example output:
//
//     mutated monster is Monster { pos: [-8, -1647191276], hp: 36, is_ghost: false }
//     mutated monster is Monster { pos: [-8, -1062708247], hp: 36, is_ghost: false }
//     mutated monster is Monster { pos: [-8, -1062708247], hp: 61401, is_ghost: false }
//     mutated monster is Monster { pos: [-8, -1062708247], hp: 0, is_ghost: true }
//     mutated monster is Monster { pos: [-8, 1487274938], hp: 0, is_ghost: true }
# Ok(())
# }
# foo().unwrap()

Automatically Deriving Mutators with #[derive(Mutate)]

First, enable this crate's derive feature, then slap #[derive(Mutate)] onto your type definitions:

# fn foo() -> mutatis::Result<()> {
#![cfg(feature = "derive")]
use mutatis::{Mutate, Session};

// An RGB color.
#[derive(Debug)]
#[derive(Mutate)] // Automatically derive a mutator for `Rgb`!
pub struct Rgb {
    r: u8,
    g: u8,
    b: u8,
}

// Create an RGB color: chartreuse.
let mut color = Rgb {
    r: 0x7f,
    g: 0xff,
    b: 0x00,
};

// ...and mutate it a bunch of times!
let mut session = Session::new();
for _ in 0..5 {
    session.mutate(&mut color)?;
    println!("mutated color is {color:?}");
}

// Example output:
//
//     mutated color is Rgb { r: 127, g: 45, b: 0 }
//     mutated color is Rgb { r: 127, g: 134, b: 0 }
//     mutated color is Rgb { r: 127, g: 10, b: 0 }
//     mutated color is Rgb { r: 127, g: 10, b: 29 }
//     mutated color is Rgb { r: 172, g: 10, b: 29 }
# Ok(())
# }
# #[cfg(feature = "derive")] foo().unwrap()

Writing Smoke Tests with mutatis::check

When you enable the check feature in Cargo.toml, the mutatis::check module provides a tiny property-based testing framework that is suitable for writing smoke tests that you use for local development and CI. It is not intended to replace a full-fledged, coverage-guided fuzzing engine that you'd use for in-depth, continuous fuzzing.

# #[cfg(feature = "check")]
#[cfg(test)]
mod tests {
    use mutatis::check::Check;

    #[test]
    fn test_that_addition_commutes() {
        Check::new()
            .iters(1000)
            .shrink_iters(1000)
            .run(|(a, b): &(i32, i32)| {
                if a + b == b + a {
                    Ok(())
                } else {
                    Err("addition is not commutative!")
                }
            })
            .unwrap();
    }
}

See the check module's documentation for more details.

Documentation

API Reference Documentation

The API reference documentation is available on docs.rs.

Guide

Check out the guide for tutorials, discussions, and recipes; everything else that doesn't fall into the API-reference category.

License

Licensed under dual MIT or Apache-2.0 at your choice.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

You might also like...
Test social media cards locally
Test social media cards locally

Share Preview Test social media cards locally Description Preview and debug websites metadata tags for social media share. Third Party Packages Distri

Competitive Programming Stress Test Tools
Competitive Programming Stress Test Tools

Competitive Programming Stress Test Tools 競技プログラミング用 ストレステストツール このプログラムの役割 のプログラムに対して,それより実行時間がかかるが確実に できる愚直プログラムと比較することで, となるテストケースを探し出す 最大コーナーケースに対し

A heckin small test generator

heckcheck A heckin small test generator API Docs | Releases | Contributing Installation $ cargo add heckcheck Safety This crate uses #![deny(unsafe_co

atttribute macro for running a flaky test multiple times

flaky_test This attribute macro will register and run a test 3 times, erroring only if all three times fail. Useful for situations when a test is flak

Test for crate delay_timer

delay_timer-test some test for crate delay_timer crate link: https://github.com/BinChengZhao/delay-timer here some test for delay_timer,also used for

a test harness for embedded devices

defmt-test a test harness for embedded devices This crate has been moved to the defmt repository Support defmt-test is part of the Knurling project, F

Test cargo crates in different envs & via different methods
Test cargo crates in different envs & via different methods

Test cargo crates in different envs & via different methods

Hopper is a tool for generating fuzzing test cases for libraries automatically using interpretative fuzzing.

Hopper Hopper is an tool for generating fuzzing test cases for libraries automatically using interpretative fuzzing. It transforms the problem of libr

Hopper is a tool for generating fuzzing test cases for libraries automatically using interpretative fuzzing.

Hopper Hopper is an tool for generating fuzzing test cases for libraries automatically using interpretative fuzzing. It transforms the problem of libr

Owner
Nick Fitzgerald
Nick Fitzgerald
Structure-aware, in-process, coverage-guided, evolutionary fuzzing engine for Rust functions.

fuzzcheck Fuzzcheck is a structure-aware, in-process, coverage-guided, evolutionary fuzzing engine for Rust functions. Given a function test: (T) -> b

Loïc Lecrenier 394 Dec 20, 2022
Custom memory allocator that helps discover reads from uninitialized memory

libdiffuzz: security-oriented alternative to Memory Sanitizer This is a drop-in replacement for OS memory allocator that can be used to detect uses of

Sergey 155 Dec 3, 2022
Travis CI and AppVeyor template to test your Rust crate on 5 architectures and publish binary releases of it for Linux, macOS and Windows

trust Travis CI and AppVeyor template to test your Rust crate on 5 architectures and publish binary releases of it for Linux, macOS and Windows Featur

Jorge Aparicio 1.2k Dec 30, 2022
Cucumber testing framework for Rust. Fully native, no external test runners or dependencies.

An implementation of the Cucumber testing framework for Rust. Fully native, no external test runners or dependencies.

Brendan Molloy 394 Jan 1, 2023
Fluent test assertions for Rust.

This is a fork the unmaintained crate spectral. Spectral as not changed for five years and yet is still very usable, the goal of this fork is to add n

Paul Delafosse 24 Dec 20, 2022
A series of test cases to evaluate async Rust on the nrf52840 in terms of power usage and ergonomics.

A series of test cases to evaluate async Rust on the nrf52840 in terms of power usage and ergonomics. This is an experiment that uses unstable features only available on nightly rust.

Tweede golf 1 Oct 15, 2021
Test for rust-based plugin system for swc

rust-dylib-test Steps Run cargo build in plugin_a Ensure that plugin_a dynamically links to runtime/common by otool -L plugin_a/target/debug/libplugin

Donny/강동윤 1 Apr 6, 2022
Fixture-based test framework for Rust

Fixture-based test framework for Rust Introduction rstest uses procedural macros to help you on writing fixtures and table-based tests. To use it, add

Michele d'Amico 567 Dec 24, 2022
Verdun is a HTTP stress-test/benchmark tool written in Rust.

Verdun is a HTTP stress-test/benchmark tool written in Rust. ?? It supports testing a single URL, loading multiples URLs from a file or automatically navigating a website (auto discovery)

Alex Hortopan 2 Feb 23, 2022
Nextest is a next-generation test runner for Rust.

nextest Nextest is a next-generation test runner for Rust. For more, check out the website. This repository contains the source code for: cargo-nextes

null 1.3k Jan 8, 2023