Skip to content

Commit

Permalink
Improve LibAFL fuzzer
Browse files Browse the repository at this point in the history
  • Loading branch information
louismerlin committed Oct 19, 2023
1 parent 4af4de2 commit a963ff1
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 144 deletions.
2 changes: 1 addition & 1 deletion src/bin/cargo-ziggy/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ impl Build {
/// Build the fuzzers
pub fn build(&self) -> Result<(), anyhow::Error> {
// No fuzzers for you
if self.no_afl && self.no_honggfuzz {
if self.no_afl && self.no_honggfuzz && self.no_libafl {
return Err(anyhow!("Pick at least one fuzzer"));
}

Expand Down
56 changes: 27 additions & 29 deletions src/bin/cargo-ziggy/fuzz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ impl Fuzz {
// Spawns new fuzzers
pub fn spawn_new_fuzzers(&self) -> Result<Vec<process::Child>, anyhow::Error> {
// No fuzzers for you
if self.no_afl && self.no_honggfuzz {
if self.no_afl && self.no_honggfuzz && self.no_libafl {
return Err(anyhow!("Pick at least one fuzzer"));
}

Expand Down Expand Up @@ -446,34 +446,32 @@ impl Fuzz {
.spawn()?
.wait()?;

for identifier in 0..libafl_jobs {
fuzzer_handles.push(
process::Command::new(format!(
"./target/libafl/x86_64-unknown-linux-gnu/release/{}",
self.target
))
.env("LIBAFL_TARGET_NAME", &self.target)
.env("LIBAFL_IDENTIFIER", &format!("{identifier}"))
.env("LIBAFL_SHARED_CORPUS", self.corpus())
.env(
"LIBAFL_CORPUS",
&format!("{}/libafl/corpus", self.output_target()),
)
.env(
"LIBAFL_CRASHES",
&format!("{}/libafl/crashes", self.output_target()),
)
.stderr(File::create(format!(
"{}/libafl/logs/{identifier}.log",
self.output_target(),
))?)
.stdout(File::create(format!(
"{}/libafl/logs/{identifier}.log",
self.output_target()
))?)
.spawn()?,
);
}
fuzzer_handles.push(
process::Command::new(format!(
"./target/libafl/x86_64-unknown-linux-gnu/release/{}",
self.target
))
.env("LIBAFL_TARGET_NAME", &self.target)
.env("LIBAFL_SHARED_CORPUS", self.corpus())
.env(
"LIBAFL_CORPUS",
&format!("{}/libafl/corpus", self.output_target()),
)
.env(
"LIBAFL_CRASHES",
&format!("{}/libafl/crashes", self.output_target()),
)
.env("LIBAFL_CORES", format!("{libafl_jobs}"))
.stderr(File::create(format!(
"{}/logs/libafl.log",
self.output_target(),
))?)
.stdout(File::create(format!(
"{}/logs/libafl.log",
self.output_target()
))?)
.spawn()?,
);
eprintln!(
"{} libafl ",
style(" Launched").green().bold()
Expand Down
274 changes: 160 additions & 114 deletions src/libafl_fuzzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ macro_rules! libafl_fuzz {
( $($x:tt)* ) => {
use ziggy::libafl::{
corpus::{InMemoryCorpus, OnDiskCorpus, Corpus},
events::{setup_restarting_mgr_std, EventConfig, EventRestarter, SimpleEventManager},
events::{launcher::Launcher, setup_restarting_mgr_std, EventConfig, EventRestarter, SimpleEventManager, LlmpRestartingEventManager},
executors::{inprocess::InProcessExecutor, InProcessForkExecutor, ExitKind, TimeoutExecutor},
feedback_or, feedback_or_fast,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
Expand All @@ -30,133 +30,179 @@ macro_rules! libafl_fuzz {
},
stages::{calibrate::CalibrationStage, power::StdPowerMutationalStage, sync::SyncFromDiskStage},
state::{HasCorpus, StdState},
Error
};
use ziggy::libafl_bolts::{
core_affinity::{Cores, CoreId}, current_nanos, rands::StdRand,shmem::{ShMemProvider, StdShMemProvider},
tuples::{Merge, tuple_list}, AsSlice
};
use ziggy::libafl_bolts::{current_nanos, rands::StdRand, tuples::{Merge, tuple_list}, AsSlice};
use core::time::Duration;
use std::{env, path::PathBuf, ptr::write};
use std::{env, path::PathBuf, ptr::write, str::FromStr, net::TcpListener};
use ziggy::libafl_targets::{EDGES_MAP, MAX_EDGES_NUM};

// Environement variables are passed from ziggy to LibAFL
let target_name = env::var("LIBAFL_TARGET_NAME").expect("Could not find LIBAFL_TARGET_NAME env variable");
let client_identifier = env::var("LIBAFL_IDENTIFIER").expect("Could not find LIBAFL_IDENTIFIER env variable").parse::<u8>().unwrap_or(0);
let shared_corpus: PathBuf = env::var("LIBAFL_SHARED_CORPUS").expect("Could not find LIBAFL_SHARED_CORPUS env variable").into();
let libafl_corpus: PathBuf = env::var("LIBAFL_CORPUS").expect("Could not find LIBAFL_CORPUS env variable").into();
let crashes_dir: PathBuf = env::var("LIBAFL_CRASHES").expect("Could not find LIBAFL_CRASHES env variable").into();
let num_of_cores = env::var("LIBAFL_CORES").expect("Could not find LIBAFL_CORES env variable").parse::<usize>().unwrap_or(1);

// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
// The closure that we want to fuzz
let inner_harness = $($x)*;
inner_harness(buf);
ExitKind::Ok
};
let broker_port = TcpListener::bind("127.0.0.1:0").map(|sock| {
let port = sock.local_addr().unwrap().port();
port
}).expect("Could not bind broker port");

// Create an observation channel using the coverage map
let edges_observer = unsafe {
HitcountsMapObserver::new(StdMapObserver::from_mut_ptr(
"edges",
EDGES_MAP.as_mut_ptr(),
MAX_EDGES_NUM,
))
};

// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");

// Feedback to rate the interestingness of an input
let mut map_feedback = MaxMapFeedback::tracking(&edges_observer, true, true);

let calibration = CalibrationStage::new(&map_feedback);

let sync = SyncFromDiskStage::with_from_file(shared_corpus.clone());

// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let mut feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
map_feedback,
// Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer)
);

// A feedback to choose if an input is a solution or not
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());

// create a State from scratch
let mut state = StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved
OnDiskCorpus::new(&libafl_corpus).unwrap(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(&crashes_dir).unwrap(),
// States of the feedbacks.
// The feedbacks can report the data that should persist in the State.
&mut feedback,
// Same for objective feedbacks
&mut objective,
)
.unwrap();
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");

// The Monitor trait define how the fuzzer stats are displayed to the user
let mon = SimpleMonitor::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(mon);

// We derive the strategy from the client identifier (given by ziggy)
let strategy = match client_identifier % 6 {
0 => PowerSchedule::EXPLORE,
1 => PowerSchedule::EXPLOIT,
2 => PowerSchedule::FAST,
3 => PowerSchedule::COE,
4 => PowerSchedule::LIN,
_ => PowerSchedule::QUAD,
let monitor = SimpleMonitor::new(|s| println!("{s}"));

/*
// Failed try at affinity
let all_cores: Cores = Cores::all().expect("Could not get all cores");
let mut num: usize = 0;
let core_ids: Vec<CoreId> = all_cores.ids.iter().filter(|core| {
if num >= num_of_cores {
return false;
}
if core.set_affinity().is_ok() {
num += 1;
return true;
}
return false;
}).cloned().collect();
*/

// TODO Change this to not pin on the same cores every time
let cores = Cores::from((0..num_of_cores).collect::<Vec<_>>());

let mut run_client = |state: Option<_>, mut mgr: LlmpRestartingEventManager<_, _>, core_id: CoreId| {
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
// The closure that we want to fuzz
let inner_harness = $($x)*;
inner_harness(buf);
ExitKind::Ok
};

// Create an observation channel using the coverage map
let edges_observer = unsafe {
HitcountsMapObserver::new(StdMapObserver::from_mut_ptr(
"edges",
EDGES_MAP.as_mut_ptr(),
MAX_EDGES_NUM,
))
};

// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");

// Feedback to rate the interestingness of an input
let mut map_feedback = MaxMapFeedback::tracking(&edges_observer, true, true);

let calibration = CalibrationStage::new(&map_feedback);

let sync = SyncFromDiskStage::with_from_file(shared_corpus.clone());

// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let mut feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
map_feedback,
// Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer)
);

// A feedback to choose if an input is a solution or not
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());

// create a State from scratch
let mut state = StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved
OnDiskCorpus::new(&libafl_corpus.clone()).unwrap(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(&crashes_dir).unwrap(),
// States of the feedbacks.
// The feedbacks can report the data that should persist in the State.
&mut feedback,
// Same for objective feedbacks
&mut objective,
)
.unwrap();

// We derive the strategy from the client identifier (given by ziggy)
let strategy = match core_id.0 % 6 {
0 => PowerSchedule::EXPLORE,
1 => PowerSchedule::EXPLOIT,
2 => PowerSchedule::FAST,
3 => PowerSchedule::COE,
4 => PowerSchedule::LIN,
_ => PowerSchedule::QUAD,
};

// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerScheduler::new(StdWeightedScheduler::with_schedule(
&mut state,
&edges_observer,
Some(strategy),
));

// 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!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut mgr,
)
.expect("Failed to create the Executor");

let corpus_dirs = &[libafl_corpus.clone(), shared_corpus.clone()];

// In case the corpus is empty (on first run), reset
if state.must_load_initial_inputs() {
state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, corpus_dirs)
.unwrap_or_else(|_| panic!("Failed to load initial corpus at {:?}", &corpus_dirs));
println!("We imported {} inputs from disk.", state.corpus().count());
}

// Setup a basic mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));

let power = StdPowerMutationalStage::new(mutator);

let mut stages = tuple_list!(calibration, power, sync);

fuzzer
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
.expect("Error in the fuzzing loop");

Ok(())
};

// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerScheduler::new(StdWeightedScheduler::with_schedule(
&mut state,
&edges_observer,
Some(strategy),
));

// 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!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut mgr,
)
.expect("Failed to create the Executor");

let corpus_dirs = &[libafl_corpus, shared_corpus];

// In case the corpus is empty (on first run), reset
if state.must_load_initial_inputs() {
state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, corpus_dirs)
.unwrap_or_else(|_| panic!("Failed to load initial corpus at {:?}", &corpus_dirs));
println!("We imported {} inputs from disk.", state.corpus().count());
}

// Setup a basic mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));

let power = StdPowerMutationalStage::new(mutator);

let mut stages = tuple_list!(calibration, power, sync);

fuzzer
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
.expect("Error in the fuzzing loop");
match Launcher::builder()
.shmem_provider(shmem_provider)
.configuration(EventConfig::from_name(&target_name))
.monitor(monitor)
.run_client(&mut run_client)
.cores(&cores)
.broker_port(broker_port)
.stdout_file(Some("/tmp/libafl.log"))
.build()
.launch()
{
Ok(()) => (),
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
Err(e) => panic!("Error in fuzzer: {e}"),
};
};
}

0 comments on commit a963ff1

Please sign in to comment.