From 01a98bf8fdc433d2ffd78480d91f5eaa017a56df Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 25 Sep 2021 22:54:46 +0200 Subject: [PATCH] Example how to build baby-fuzzer as push instead of pull, using Klo-routines (#227) * iyielding fuzzer * fixed klo example * docu, naming * more readme --- fuzzers/push_harness/.gitignore | 1 + fuzzers/push_harness/Cargo.toml | 23 ++++++ fuzzers/push_harness/README.md | 7 ++ fuzzers/push_harness/src/main.rs | 130 +++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 fuzzers/push_harness/.gitignore create mode 100644 fuzzers/push_harness/Cargo.toml create mode 100644 fuzzers/push_harness/README.md create mode 100644 fuzzers/push_harness/src/main.rs diff --git a/fuzzers/push_harness/.gitignore b/fuzzers/push_harness/.gitignore new file mode 100644 index 0000000000..a977a2ca5b --- /dev/null +++ b/fuzzers/push_harness/.gitignore @@ -0,0 +1 @@ +libpng-* \ No newline at end of file diff --git a/fuzzers/push_harness/Cargo.toml b/fuzzers/push_harness/Cargo.toml new file mode 100644 index 0000000000..7a0d92449c --- /dev/null +++ b/fuzzers/push_harness/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "push_harness" +version = "0.5.0" +authors = ["Andrea Fioraldi ", "Dominik Maier "] +edition = "2018" + +[features] +default = ["std"] +std = [] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" +lto = true +codegen-units = 1 +opt-level = 3 +debug = true + +[dependencies] +libafl = { path = "../../libafl/" } +klo-routines = { version = "0.1.0", git = "https://github.com/andreafioraldi/klo-routines.git" } \ No newline at end of file diff --git a/fuzzers/push_harness/README.md b/fuzzers/push_harness/README.md new file mode 100644 index 0000000000..504009c31d --- /dev/null +++ b/fuzzers/push_harness/README.md @@ -0,0 +1,7 @@ +# Klo-based Fuzzer with Push Harness + +*Linux only* + +This is a minimalistic example create a fuzzer for Linux that pulls data out of LibAFL, instead of being called by it repeatedly. +Use this only if there is absolutely no way to have a traditional harness function that gets called, but the target *needs* to call the fuzzer, instead. +This technique comes at some runtime overhead, and you should very likely not need it for everyday fuzzing. \ No newline at end of file diff --git a/fuzzers/push_harness/src/main.rs b/fuzzers/push_harness/src/main.rs new file mode 100644 index 0000000000..01298b1391 --- /dev/null +++ b/fuzzers/push_harness/src/main.rs @@ -0,0 +1,130 @@ +//! [`Klo-routines`](https://github.com/andreafioraldi/klo-routines/) based fuzzer. +//! The target loops and the harness pulls inputs out of `LibAFL`, instead of being called by `LibAFL`. +use klo_routines::*; +use libafl::inputs::{BytesInput, HasTargetBytes}; +use libafl::{ + bolts::{current_nanos, rands::StdRand, tuples::tuple_list}, + corpus::{InMemoryCorpus, OnDiskCorpus, QueueCorpusScheduler}, + events::SimpleEventManager, + executors::{inprocess::InProcessExecutor, ExitKind}, + feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + generators::RandPrintablesGenerator, + mutators::scheduled::{havoc_mutations, StdScheduledMutator}, + observers::StdMapObserver, + stages::mutational::StdMutationalStage, + state::StdState, + stats::SimpleStats, +}; +use std::path::PathBuf; + +/// Coverage map with explicit assignments due to the lack of instrumentation +static mut SIGNALS: [u8; 16] = [0; 16]; + +/// Assign a signal to the signals map +fn signals_set(idx: usize) { + unsafe { SIGNALS[idx] = 1 }; +} + +/// This generates the input, using klo-routines. +#[allow(clippy::similar_names)] +fn input_generator() { + // The closure that produced the input for the generator + let mut harness = |input: &BytesInput| { + // The `yield_` switches execution context back to the loop in `main`. + // When `resume` is called, we return to this function. + yield_(input); + ExitKind::Ok + }; + + // Create an observation channel using the signals map + let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS }); + + // The state of the edges feedback. + let feedback_state = MapFeedbackState::with_observer(&observer); + + // Feedback to rate the interestingness of an input + let feedback = MaxMapFeedback::new(&feedback_state, &observer); + + // A feedback to choose if an input is a solution or not + let objective = CrashFeedback::new(); + + // create a State from scratch + let mut state = StdState::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::new(), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(), + // States of the feedbacks. + // They are the data related to the feedbacks that you want to persist in the State. + tuple_list!(feedback_state), + ); + + // The Stats trait define how the fuzzer stats are reported to the user + let stats = SimpleStats::new(|s| println!("{}", s)); + + // The event manager handle the various events generated during the fuzzing loop + // such as the notification of the addition of a new item to the corpus + let mut mgr = SimpleEventManager::new(stats); + + // A queue policy to get testcasess from the corpus + let scheduler = QueueCorpusScheduler::new(); + + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + + // Create the executor for an in-process function with just one observer + let mut executor = InProcessExecutor::new( + &mut harness, + tuple_list!(observer), + &mut fuzzer, + &mut state, + &mut mgr, + ) + .expect("Failed to create the Executor"); + + // Generator of printable bytearrays of max size 32 + let mut generator = RandPrintablesGenerator::new(32); + + // Generate 8 initial inputs + state + .generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8) + .expect("Failed to generate the initial corpus"); + + // Setup a mutational stage with a basic bytes mutator + let mutator = StdScheduledMutator::new(havoc_mutations()); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); + + fuzzer + .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) + .expect("Error in the fuzzing loop"); +} + +/// the main function loops independently of the fuzzer. +/// `Klo` internally switches between the `LibAFL` and harness coroutines to generate the inputs. +pub fn main() { + // Set up the Klo-routines harness + let mut input_generator = input_generator; + let mut klo = + KloRoutine::<_, &BytesInput>::with_stack_size(&mut input_generator, 512 * 1024 * 1024); + + // Loop, calling `klo.resume` repeatedly. This will switch execution to the loop in the `input_generator` function. + while let Some(input) = klo.resume() { + let target = input.target_bytes(); + let buf = target.as_slice(); + signals_set(0); + if !buf.is_empty() && buf[0] == b'a' { + signals_set(1); + if buf.len() > 1 && buf[1] == b'b' { + signals_set(2); + if buf.len() > 2 && buf[2] == b'c' { + panic!("=)"); + } + } + } + } + println!("Flushed klo"); +}