Dominik Maier e9e9c457d6
Move Bolts to libafl_bolts (#1335)
* sort memebers

* Building bolts

* fixing python, feature flags

* Cleanup bolts Cargo.toml

* Fix tests

* cleanup libafl

* removed duplicate examples

* Info text

* reenable agpl CI

* fix impl_serdeany

* new fmt

* Moved bolts

* fix some builds

* fix

* fix more fixes

* serdeany

* no_std

* Dependency cleanup

* Fix docs

* Docker

* add python bolts bindings

* no_std test fix

* merge fail

* typo fix

* add bolts dependency to fuzzers

* tiny fixes

* merge fun

* clippy

* link no longer exists

* make sure python gets rebuilt

* fix pybind

* doc fix

* remove bolts ref

* LibAFL bolts

* More info

* deprecation notice for launcher

* fix python

* cargo fmt

* fix concolic

* fix

* clippy

* fix libafl_cc

* fix tutorial, clippy

* fix concolic fuzzer

* fix push_stage_harness fuzzer

* prelude

* fix testcase post-merge

* mute clippy
2023-08-02 17:36:26 +02:00

217 lines
7.2 KiB
Rust

use core::time::Duration;
use std::path::PathBuf;
use clap::{self, Parser};
use libafl::{
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
events::SimpleEventManager,
executors::{
forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
HasObservers,
},
feedback_and_fast, feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::BytesInput,
monitors::SimpleMonitor,
mutators::{scheduled::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens},
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, StdState},
};
use libafl_bolts::{
current_nanos,
rands::StdRand,
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
tuples::{tuple_list, MatchName, Merge},
AsMutSlice, Truncate,
};
use nix::sys::signal::Signal;
/// The commandline args this fuzzer accepts
#[derive(Debug, Parser)]
#[command(
name = "forkserver_simple",
about = "This is a simple example fuzzer to fuzz a executable instrumented by afl-cc.",
author = "tokatoka <tokazerkje@outlook.com>"
)]
struct Opt {
#[arg(
help = "The instrumented binary we want to fuzz",
name = "EXEC",
required = true
)]
executable: String,
#[arg(
help = "The directory to read initial inputs from ('seeds')",
name = "INPUT_DIR",
required = true
)]
in_dir: PathBuf,
#[arg(
help = "Timeout for each individual execution, in milliseconds",
short = 't',
long = "timeout",
default_value = "1200"
)]
timeout: u64,
#[arg(
help = "If not set, the child's stdout and stderror will be redirected to /dev/null",
short = 'd',
long = "debug-child",
default_value = "false"
)]
debug_child: bool,
#[arg(
help = "Arguments passed to the target",
name = "arguments",
num_args(1..),
allow_hyphen_values = true,
)]
arguments: Vec<String>,
#[arg(
help = "Signal used to stop child",
short = 's',
long = "signal",
value_parser = str::parse::<Signal>,
default_value = "SIGKILL"
)]
signal: Signal,
}
#[allow(clippy::similar_names)]
pub fn main() {
const MAP_SIZE: usize = 65536;
let opt = Opt::parse();
let corpus_dirs: Vec<PathBuf> = [opt.in_dir].to_vec();
// The unix shmem provider supported by AFL++ for shared memory
let mut shmem_provider = UnixShMemProvider::new().unwrap();
// The coverage map shared between observer and executor
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
// let the forkserver know the shmid
shmem.write_to_env("__AFL_SHM_ID").unwrap();
let shmem_buf = shmem.as_mut_slice();
// Create an observation channel using the signals map
let edges_observer =
unsafe { HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf)) };
// 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
// 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
MaxMapFeedback::tracking(&edges_observer, true, false),
// 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
// We want to do the same crash deduplication that AFL does
let mut objective = feedback_and_fast!(
// Must be a crash
CrashFeedback::new(),
// Take it only if trigger new coverage over crashes
// Uses `with_name` to create a different history from the `MaxMapFeedback` in `feedback` above
MaxMapFeedback::with_name("mapfeedback_metadata_objective", &edges_observer)
);
// 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::<BytesInput>::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.
// The feedbacks can report the data that should persist in the State.
&mut feedback,
// Same for objective feedbacks
&mut objective,
)
.unwrap();
// The Monitor trait define how the fuzzer stats are reported to the user
let monitor = 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(monitor);
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// If we should debug the child
let debug_child = opt.debug_child;
// Create the executor for the forkserver
let args = opt.arguments;
let mut tokens = Tokens::new();
let mut forkserver = ForkserverExecutor::builder()
.program(opt.executable)
.debug_child(debug_child)
.shmem_provider(&mut shmem_provider)
.autotokens(&mut tokens)
.parse_afl_cmdline(args)
.coverage_map_size(MAP_SIZE)
.build(tuple_list!(time_observer, edges_observer))
.unwrap();
if let Some(dynamic_map_size) = forkserver.coverage_map_size() {
forkserver
.observers_mut()
.match_name_mut::<HitcountsMapObserver<StdMapObserver<'_, u8, false>>>("shared_mem")
.unwrap()
.truncate(dynamic_map_size);
}
let mut executor = TimeoutForkserverExecutor::with_signal(
forkserver,
Duration::from_millis(opt.timeout),
opt.signal,
)
.expect("Failed to create the executor.");
// 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(|err| {
panic!(
"Failed to load initial corpus at {:?}: {:?}",
&corpus_dirs, err
)
});
println!("We imported {} inputs from disk.", state.corpus().count());
}
state.add_metadata(tokens);
// Setup a mutational stage with a basic bytes mutator
let mutator =
StdScheduledMutator::with_max_stack_pow(havoc_mutations().merge(tokens_mutations()), 6);
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");
}