#fuzzing #lib-fuzzer

nightly fazi

A drop-in replacement for libFuzzer

4 releases

0.1.3 Apr 11, 2022
0.1.2 Apr 11, 2022
0.1.1 Apr 11, 2022
0.1.0 Apr 11, 2022

#318 in Testing

MIT/Apache

470KB
2K SLoC

crates.io docs.rs

Fazi

fazi

A reimplementation of libfuzzer in Rust with some improvements

Supported Features

  • libFuzzer's mutations
  • SanCov feedback
  • Building without a main() entry point
  • Crash replaying
  • Recoverage
  • Custom user initialization
  • Scaling/forking support
  • Timeout detection
  • Custom dictionaries
  • Custom user-defined mutations

Anything else that's missing from libFuzzer's featureset will likely not be supported. Feel free to file an issue if you'd like to voice support for something.

Usage

Step 1: Build fazi (requires Rust nightly):

$ cargo build --release

Note: Fazi can also be built without the main entrypoint by providing the --no-default-features flag

Step 2: Build your harness:

$ FAZI_DIR="../path-to-fazi" clang ./main.c -fsanitize=fuzzer-no-link -fsanitize=address -lfazi -L$FAZI_DIR/target/release/

Step 3: Run the harness:

$ ./a.out

fazi_running

You can list command-line options with the --help flag:

fazi

USAGE:
    a.out [OPTIONS] [SUBCOMMAND]

OPTIONS:
        --corpus-dir <CORPUS_DIR>
            Location at which inputs that cause new coverage will be saved [default: ./corpus]

        --crashes-dir <CRASHES_DIR>
            Location at which crashing inputs will be saved [default: ./crashes]

    -h, --help
            Print help information

        --len-control <LEN_CONTROL>
            Length control is used in an algorithm for deciding how quickly the input size grows. A
            larger value will result in faster growth while a smaller value will result in slow
            growth [default: 100]

        --max-input-len <MAX_INPUT_LEN>
            The maximum size (in bytes) that an input can extend to [default: 65000]

        --max-iters <MAX_ITERS>
            Maximum number of fuzzing iterations before the fuzzer should exit

        --max-mutation-depth <MAX_MUTATION_DEPTH>
            The maximum number of times to mutate a single input before moving on to another
            [default: 15]

        --seed <SEED>
            RNG seed

SUBCOMMANDS:
    help     Print this message or the help of the given subcommand(s)
    repro    Reproduce some crash

Why

While libfuzzer can be used as a library, engaging with it from some environments may be difficult to setup. Fazi provides similar functionality to libfuzzer, but gives greater flexibility into how you can use it. For instance, a native application which requires its own main entry point may be setup like:

/// compiled with -fsanitize=fuzer-no-link

extern "C" int LLVMFuzzerRunDriver(int *argc, char ***argv,
                  int (*UserCb)(const uint8_t *Data, size_t Size));

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
    // fuzz
}

int main(int *argc, char ***argv) {
    // Do my own thing
    LLVMFuzzerRunDriver(argc, argv, LLVMFuzzerTestOneInput);
}

This model is difficult when integrating into an application with a lot of state or its own runtime environment, such as the JVM. Instead of providing a callback, Fazi lets you just ask it for data and tell it when the testcase is done:

extern "C" void fazi_start_iteration(char** data, size_t* size);
extern "C" void fazi_end_iteration(bool need_more_data);
extern "C" void fazi_initialize();
extern "C" void fazi_set_corpus_dir(const char*);
extern "C" void fazi_set_crashes_dir(const char*);

int main() {
    // Setup fazi globals
    fazi_initialize();

    while (true) {
        const char* data = nullptr;
        size_t len = 0;
        fazi_start_iteration(&data, &len);

        bool need_more_data = some_api(data, len);

        fazi_end_iteration(need_more_data);
    }
}

Dependencies

~4MB
~76K SLoC