From 72e54ac2f4cdb4b9e6fc42172857633d172989a6 Mon Sep 17 00:00:00 2001 From: epi <43392618+epi052@users.noreply.github.com> Date: Wed, 12 Jul 2023 05:16:46 -0500 Subject: [PATCH] Update ForkserverBytesCoverageSugar to use parse_afl_cmdline, latest features (#1343) * update forkserversugar to use parse_afl_cmdline * added tokens/persistent/deferred to forkserver sugar * removed deferred option * added build_dynamic_map call; removed generic const * clippy --------- Co-authored-by: Dominik Maier --- libafl_sugar/src/forkserver.rs | 56 ++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/libafl_sugar/src/forkserver.rs b/libafl_sugar/src/forkserver.rs index 4db2b15164..44701180e7 100644 --- a/libafl_sugar/src/forkserver.rs +++ b/libafl_sugar/src/forkserver.rs @@ -24,7 +24,7 @@ use libafl::{ scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, token_mutations::Tokens, }, - observers::{ConstMapObserver, HitcountsMapObserver, TimeObserver}, + observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, stages::StdMutationalStage, state::{HasCorpus, HasMetadata, StdState}, @@ -39,7 +39,7 @@ pub const DEFAULT_MAP_SIZE: usize = 65536; /// Creates a Forkserver-based fuzzer. #[derive(Debug, TypedBuilder)] -pub struct ForkserverBytesCoverageSugar<'a, const MAP_SIZE: usize> { +pub struct ForkserverBytesCoverageSugar<'a> { /// Laucher configuration (default is random) #[builder(default = None, setter(strip_option))] configuration: Option, @@ -80,10 +80,15 @@ pub struct ForkserverBytesCoverageSugar<'a, const MAP_SIZE: usize> { } #[allow(clippy::similar_names)] -impl<'a, const MAP_SIZE: usize> ForkserverBytesCoverageSugar<'a, MAP_SIZE> { +impl<'a> ForkserverBytesCoverageSugar<'a> { /// Runs the fuzzer. #[allow(clippy::too_many_lines, clippy::similar_names)] pub fn run(&mut self) { + // a large initial map size that should be enough + // to house all potential coverage maps for our targets + // (we will eventually reduce the used size according to the actual map) + const MAP_SIZE: usize = 2_621_440; + let conf = match self.configuration.as_ref() { Some(name) => EventConfig::from_name(name), None => EventConfig::AlwaysUnique, @@ -121,13 +126,12 @@ impl<'a, const MAP_SIZE: usize> ForkserverBytesCoverageSugar<'a, MAP_SIZE> { shmem.write_to_env("__AFL_SHM_ID").unwrap(); let shmem_map = shmem.as_mut_slice(); + // To let know the AFL++ binary that we have a big map + std::env::set_var("AFL_MAP_SIZE", format!("{MAP_SIZE}")); + // Create an observation channel using the coverage map - let edges_observer = unsafe { - HitcountsMapObserver::new(ConstMapObserver::<_, MAP_SIZE>::from_mut_ptr( - "shared_mem", - shmem_map.as_mut_ptr(), - )) - }; + let edges_observer = + unsafe { HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_map)) }; // Create an observation channel to keep track of the execution time let time_observer = TimeObserver::new("time"); @@ -160,12 +164,8 @@ impl<'a, const MAP_SIZE: usize> ForkserverBytesCoverageSugar<'a, MAP_SIZE> { .unwrap() }); - // Create a dictionary if not existing - if let Some(tokens_file) = &self.tokens_file { - if state.metadata_map().get::().is_none() { - state.add_metadata(Tokens::from_file(tokens_file)?); - } - } + // Create an empty set of tokens, first populated by the target program + let mut tokens = Tokens::new(); // A minimization+queue policy to get testcasess from the corpus let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new()); @@ -176,18 +176,34 @@ impl<'a, const MAP_SIZE: usize> ForkserverBytesCoverageSugar<'a, MAP_SIZE> { let forkserver = if self.shmem_testcase { ForkserverExecutorBuilder::new() .program(self.program.clone()) - .args(self.arguments) + .parse_afl_cmdline(self.arguments) + .is_persistent(true) + .autotokens(&mut tokens) + .coverage_map_size(MAP_SIZE) .debug_child(self.debug_output) .shmem_provider(&mut shmem_provider_client) - .build(tuple_list!(edges_observer, time_observer)) + .build_dynamic_map(edges_observer, tuple_list!(time_observer)) } else { ForkserverExecutorBuilder::new() .program(self.program.clone()) - .args(self.arguments) + .parse_afl_cmdline(self.arguments) + .is_persistent(true) + .autotokens(&mut tokens) + .coverage_map_size(MAP_SIZE) .debug_child(self.debug_output) - .build(tuple_list!(edges_observer, time_observer)) + .build_dynamic_map(edges_observer, tuple_list!(time_observer)) }; + if let Some(tokens_file) = &self.tokens_file { + // if a token file is provided, load it into our set of tokens + tokens.add_from_file(tokens_file)?; + } + + if !tokens.is_empty() { + // add any known tokens to the state + state.add_metadata(tokens); + } + // Create the executor for an in-process function with one observer for edge coverage and one for the execution time let mut executor = TimeoutForkserverExecutor::new( forkserver.expect("Failed to create the executor."), @@ -346,7 +362,7 @@ pub mod pybind { /// Run the fuzzer #[allow(clippy::needless_pass_by_value)] pub fn run(&self, program: String, arguments: Vec) { - forkserver::ForkserverBytesCoverageSugar::<{ forkserver::DEFAULT_MAP_SIZE }>::builder() + forkserver::ForkserverBytesCoverageSugar::builder() .input_dirs(&self.input_dirs) .output_dir(self.output_dir.clone()) .broker_port(self.broker_port)