//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts //! The example harness is built for libpng. use core::time::Duration; #[cfg(feature = "crash")] use std::ptr; use std::{env, path::PathBuf}; use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{tcp::setup_restarting_mgr_tcp, EventConfig, EventRestarter}, executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, mutators::{ havoc_mutations::havoc_mutations, scheduled::{tokens_mutations, StdScheduledMutator}, token_mutations::Tokens, }, observers::{CanTrack, HitcountsMapObserver, StdMapObserver, TimeObserver}, schedulers::{ powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler, }, stages::{calibrate::CalibrationStage, power::StdPowerMutationalStage}, state::{HasCorpus, StdState}, Error, HasMetadata, }; use libafl_bolts::{ rands::StdRand, tuples::{tuple_list, Merge}, AsSlice, }; use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_FOUND}; use mimalloc::MiMalloc; #[global_allocator] static GLOBAL: MiMalloc = MiMalloc; /// The main fn, `no_mangle` as it is a C main #[no_mangle] pub extern "C" fn libafl_main() { // Registry the metadata types used in this fuzzer // Needed only on no_std // unsafe { RegistryBuilder::register::(); } println!( "Workdir: {:?}", env::current_dir().unwrap().to_string_lossy().to_string() ); fuzz( &[PathBuf::from("./corpus")], PathBuf::from("./crashes"), 1337, ) .expect("An error occurred while fuzzing"); } /// The actual fuzzer fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { // 'While the stats are state, they are usually used in the broker - which is likely never restarted let monitor = MultiMonitor::new(|s| println!("{s}")); // The restarting state will spawn the same process again as child, then restarted it each time it crashes. let (state, mut restarting_mgr) = match setup_restarting_mgr_tcp(monitor, broker_port, EventConfig::AlwaysUnique) { Ok(res) => res, Err(err) => match err { Error::ShuttingDown => { return Ok(()); } _ => { panic!("Failed to setup the restarter: {err}"); } }, }; // 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_FOUND, )) .track_indices() }; // Create an observation channel to keep track of the execution time let time_observer = TimeObserver::new("time"); let map_feedback = MaxMapFeedback::new(&edges_observer); let calibration = CalibrationStage::new(&map_feedback); // 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::new(&time_observer) ); // A feedback to choose if an input is a solution or not let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new()); // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { StdState::new( // RNG StdRand::new(), // 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(objective_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() }); println!("We're a client, let's fuzz :)"); // Create a PNG dictionary if not existing if state.metadata_map().get::().is_none() { state.add_metadata(Tokens::from([ vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header "IHDR".as_bytes().to_vec(), "IDAT".as_bytes().to_vec(), "PLTE".as_bytes().to_vec(), "IEND".as_bytes().to_vec(), ])); } // Setup a basic mutator with a mutational stage let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); let power: StdPowerMutationalStage<_, _, BytesInput, _, _> = StdPowerMutationalStage::new(mutator); let mut stages = tuple_list!(calibration, power); // A minimization+queue policy to get testcasess from the corpus let scheduler = IndexesLenTimeMinimizerScheduler::new( &edges_observer, StdWeightedScheduler::with_schedule( &mut state, &edges_observer, Some(PowerSchedule::fast()), ), ); // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); // 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(); #[cfg(feature = "crash")] if buf.len() > 4 && buf[4] == 0 { unsafe { eprintln!("Crashing (for testing purposes)"); let addr = ptr::null_mut(); *addr = 1; } } unsafe { libfuzzer_test_one_input(buf); } ExitKind::Ok }; // Create the executor for an in-process function with one observer for edge coverage and one for the execution time let mut executor = InProcessExecutor::with_timeout( &mut harness, tuple_list!(edges_observer, time_observer), &mut fuzzer, &mut state, &mut restarting_mgr, Duration::new(10, 0), )?; // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. let args: Vec = env::args().collect(); if unsafe { libfuzzer_initialize(&args) } == -1 { println!("Warning: LLVMFuzzerInitialize failed with -1"); } // 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 restarting_mgr, corpus_dirs) .unwrap_or_else(|_| panic!("Failed to load initial corpus at {:?}", &corpus_dirs)); println!("We imported {} inputs from disk.", state.corpus().count()); } // This fuzzer restarts after 1 mio `fuzz_one` executions. // Each fuzz_one will internally do many executions of the target. // If your target is very instable, setting a low count here may help. // However, you will lose a lot of performance that way. let iters = 1_000_000; fuzzer.fuzz_loop_for( &mut stages, &mut executor, &mut state, &mut restarting_mgr, iters, )?; // It's important, that we store the state before restarting! // Else, the parent will not respawn a new child and quit. restarting_mgr.on_restart(&mut state)?; Ok(()) }