Custom Executor Example (#2570)
* [WIP] Custom Executor Example * readme * src/main.rs * Finish * fix warnings * reame * CI
This commit is contained in:
parent
b5c9bffe50
commit
36a24ab418
1
.github/workflows/build_and_test.yml
vendored
1
.github/workflows/build_and_test.yml
vendored
@ -246,6 +246,7 @@ jobs:
|
||||
- ./fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_inprocess_executor
|
||||
- ./fuzzers/baby/backtrace_baby_fuzzers/command_executor
|
||||
- ./fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor
|
||||
- ./fuzzers/baby/baby_fuzzer_custom_executor
|
||||
|
||||
# Binary-only
|
||||
- ./fuzzers/binary_only/fuzzbench_fork_qemu
|
||||
|
2
fuzzers/baby/baby_fuzzer_custom_executor/.gitignore
vendored
Normal file
2
fuzzers/baby/baby_fuzzer_custom_executor/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
libpng-*
|
||||
corpus
|
27
fuzzers/baby/baby_fuzzer_custom_executor/Cargo.toml
Normal file
27
fuzzers/baby/baby_fuzzer_custom_executor/Cargo.toml
Normal file
@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "fuzzer_custom_executor"
|
||||
version = "0.13.2"
|
||||
authors = [
|
||||
"Andrea Fioraldi <andreafioraldi@gmail.com>",
|
||||
"Dominik Maier <domenukk@gmail.com>",
|
||||
]
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
tui = []
|
||||
std = []
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
debug = true
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../../libafl/" }
|
||||
libafl_bolts = { path = "../../../libafl_bolts/" }
|
50
fuzzers/baby/baby_fuzzer_custom_executor/Makefile.toml
Normal file
50
fuzzers/baby/baby_fuzzer_custom_executor/Makefile.toml
Normal file
@ -0,0 +1,50 @@
|
||||
# Variables
|
||||
[env]
|
||||
FUZZER_NAME = 'fuzzer_custom_executor'
|
||||
PROJECT_DIR = { script = ["pwd"] }
|
||||
CARGO_TARGET_DIR = { value = "target", condition = { env_not_set = [
|
||||
"CARGO_TARGET_DIR",
|
||||
] } }
|
||||
PROFILE = { value = "release" }
|
||||
PROFILE_DIR = { value = "release" }
|
||||
FUZZER = '${CARGO_TARGET_DIR}/${PROFILE_DIR}/${FUZZER_NAME}'
|
||||
|
||||
[tasks.build]
|
||||
alias = "fuzzer"
|
||||
|
||||
[tasks.fuzzer]
|
||||
description = "Build the fuzzer"
|
||||
script = "cargo build --profile=${PROFILE}"
|
||||
|
||||
[tasks.run]
|
||||
description = "Run the fuzzer"
|
||||
command = "${CARGO_TARGET_DIR}/${PROFILE_DIR}/${FUZZER_NAME}"
|
||||
dependencies = ["fuzzer"]
|
||||
|
||||
[tasks.test]
|
||||
description = "Run a short test"
|
||||
linux_alias = "test_unix"
|
||||
mac_alias = "test_unix"
|
||||
windows_alias = "unsupported"
|
||||
|
||||
[tasks.test_unix]
|
||||
script_runner = "@shell"
|
||||
script = '''
|
||||
timeout 30s ${CARGO_TARGET_DIR}/${PROFILE_DIR}/${FUZZER_NAME} | tee fuzz_stdout.log || true
|
||||
if grep -qa "objectives: 1" fuzz_stdout.log; then
|
||||
echo "Fuzzer is working"
|
||||
else
|
||||
echo "Fuzzer does not generate any testcases or any crashes"
|
||||
exit 1
|
||||
fi
|
||||
'''
|
||||
dependencies = ["fuzzer"]
|
||||
|
||||
# Clean up
|
||||
[tasks.clean]
|
||||
# Disable default `clean` definition
|
||||
clear = true
|
||||
script_runner = "@shell"
|
||||
script = '''
|
||||
cargo clean
|
||||
'''
|
12
fuzzers/baby/baby_fuzzer_custom_executor/README.md
Normal file
12
fuzzers/baby/baby_fuzzer_custom_executor/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Baby fuzzer with Custom Executor
|
||||
|
||||
This is a minimalistic example about how to create a LibAFL-based fuzzer.
|
||||
|
||||
In contrast to the normal baby fuzzer, this uses a (very simple) custom executor.
|
||||
|
||||
The custom executor won't catch any timeouts or actual errors (i.e., memory corruptions, etc.) in the target.
|
||||
|
||||
The tested program is a simple Rust function without any instrumentation.
|
||||
For real fuzzing, you will want to add some sort to add coverage or other feedback.
|
||||
|
||||
You can run this example using `cargo run`, and you can enable the TUI feature by running `cargo run --features tui`.
|
163
fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs
Normal file
163
fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs
Normal file
@ -0,0 +1,163 @@
|
||||
#[cfg(windows)]
|
||||
use std::ptr::write_volatile;
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
path::PathBuf,
|
||||
ptr::{addr_of, addr_of_mut, write},
|
||||
};
|
||||
|
||||
#[cfg(feature = "tui")]
|
||||
use libafl::monitors::tui::TuiMonitor;
|
||||
#[cfg(not(feature = "tui"))]
|
||||
use libafl::monitors::SimpleMonitor;
|
||||
use libafl::{
|
||||
corpus::{InMemoryCorpus, OnDiskCorpus},
|
||||
events::SimpleEventManager,
|
||||
executors::{Executor, ExitKind, WithObservers},
|
||||
feedback_and_fast,
|
||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
generators::RandPrintablesGenerator,
|
||||
inputs::HasTargetBytes,
|
||||
mutators::{havoc_mutations::havoc_mutations, scheduled::StdScheduledMutator},
|
||||
observers::StdMapObserver,
|
||||
schedulers::QueueScheduler,
|
||||
stages::mutational::StdMutationalStage,
|
||||
state::{HasExecutions, State, StdState, UsesState},
|
||||
};
|
||||
use libafl_bolts::{current_nanos, nonzero, rands::StdRand, tuples::tuple_list, AsSlice};
|
||||
|
||||
/// Coverage map with explicit assignments due to the lack of instrumentation
|
||||
static mut SIGNALS: [u8; 16] = [0; 16];
|
||||
static mut SIGNALS_PTR: *mut u8 = addr_of_mut!(SIGNALS) as _;
|
||||
static SIGNALS_LEN: usize = unsafe { (*addr_of!(SIGNALS)).len() };
|
||||
|
||||
/// Assign a signal to the signals map
|
||||
fn signals_set(idx: usize) {
|
||||
unsafe { write(SIGNALS_PTR.add(idx), 1) };
|
||||
}
|
||||
|
||||
struct CustomExecutor<S: State> {
|
||||
phantom: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S: State> CustomExecutor<S> {
|
||||
pub fn new(_state: &S) -> Self {
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: State> UsesState for CustomExecutor<S> {
|
||||
type State = S;
|
||||
}
|
||||
|
||||
impl<EM, S, Z> Executor<EM, Z> for CustomExecutor<S>
|
||||
where
|
||||
EM: UsesState<State = S>,
|
||||
S: State + HasExecutions,
|
||||
Z: UsesState<State = S>,
|
||||
Self::Input: HasTargetBytes,
|
||||
{
|
||||
fn run_target(
|
||||
&mut self,
|
||||
_fuzzer: &mut Z,
|
||||
state: &mut Self::State,
|
||||
_mgr: &mut EM,
|
||||
input: &Self::Input,
|
||||
) -> Result<ExitKind, libafl::Error> {
|
||||
// We need to keep track of the exec count.
|
||||
*state.executions_mut() += 1;
|
||||
|
||||
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' {
|
||||
return Ok(ExitKind::Crash);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(ExitKind::Ok)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names, clippy::manual_assert)]
|
||||
pub fn main() {
|
||||
// Create an observation channel using the signals map
|
||||
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS_LEN) };
|
||||
|
||||
// Feedback to rate the interestingness of an input
|
||||
let mut feedback = MaxMapFeedback::new(&observer);
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
let mut objective = feedback_and_fast!(
|
||||
// Look for crashes.
|
||||
CrashFeedback::new(),
|
||||
// We `and` the MaxMapFeedback to only end up with crashes that trigger new coverage.
|
||||
// We use the _fast variant to make sure it's not evaluated every time, even if the crash didn't trigger..
|
||||
// We have to give this one a name since it differs from the first map.
|
||||
MaxMapFeedback::with_name("on_crash", &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::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 displayed to the user
|
||||
#[cfg(not(feature = "tui"))]
|
||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
||||
#[cfg(feature = "tui")]
|
||||
let mon = TuiMonitor::builder()
|
||||
.title("Baby Fuzzer")
|
||||
.enhanced_graphics(false)
|
||||
.build();
|
||||
|
||||
// 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);
|
||||
|
||||
// A queue policy to get testcasess from the corpus
|
||||
let scheduler = QueueScheduler::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 executor = CustomExecutor::new(&state);
|
||||
|
||||
let mut executor = WithObservers::new(executor, tuple_list!(observer));
|
||||
|
||||
// Generator of printable bytearrays of max size 32
|
||||
let mut generator = RandPrintablesGenerator::new(nonzero!(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");
|
||||
}
|
@ -828,7 +828,7 @@ impl ExitKindLogic for GenericDiffLogic {
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ExitKindFeedback<L> {
|
||||
#[cfg(feature = "track_hit_feedbacks")]
|
||||
// The previous run's result of `Self::is_interesting`
|
||||
/// The previous run's result of [`Self::is_interesting`]
|
||||
last_result: Option<bool>,
|
||||
name: Cow<'static, str>,
|
||||
phantom: PhantomData<fn() -> L>,
|
||||
|
Loading…
x
Reference in New Issue
Block a user