Example how to build baby-fuzzer as push instead of pull, using Klo-routines (#227)
* iyielding fuzzer * fixed klo example * docu, naming * more readme
This commit is contained in:
parent
e17f4b846f
commit
01a98bf8fd
1
fuzzers/push_harness/.gitignore
vendored
Normal file
1
fuzzers/push_harness/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
libpng-*
|
23
fuzzers/push_harness/Cargo.toml
Normal file
23
fuzzers/push_harness/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "push_harness"
|
||||
version = "0.5.0"
|
||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||
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" }
|
7
fuzzers/push_harness/README.md
Normal file
7
fuzzers/push_harness/README.md
Normal file
@ -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.
|
130
fuzzers/push_harness/src/main.rs
Normal file
130
fuzzers/push_harness/src/main.rs
Normal file
@ -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");
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user