Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a barebones v8 executor #744

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions fuzzers/baby_fuzzer_minimizing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
corpus
minimized
solutions
23 changes: 23 additions & 0 deletions fuzzers/baby_fuzzer_minimizing/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "baby_fuzzer_minimizing"
version = "0.8.1"
authors = ["Andrea Fioraldi <[email protected]>", "Dominik Maier <[email protected]>", "Addison Crump <[email protected]>"]
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/" }
9 changes: 9 additions & 0 deletions fuzzers/baby_fuzzer_minimizing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Baby fuzzer

This is a minimalistic example about how to create a libafl based fuzzer which leverages minimisation.

The fuzzer steps until a crash occurs, minimising each corpus entry as it is discovered. Then, once a
solution is found, it attempts to minimise that as well.

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.
142 changes: 142 additions & 0 deletions fuzzers/baby_fuzzer_minimizing/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use std::path::PathBuf;
#[cfg(windows)]
use std::ptr::write_volatile;

use libafl::prelude::*;

/// Coverage map with explicit assignments due to the lack of instrumentation
static mut SIGNALS: [u8; 16] = [0; 16];

/// Assign a signal to the signals map
fn signals_set(idx: usize) {
unsafe { SIGNALS[idx] = 1 };
}

#[allow(clippy::similar_names)]
pub fn main() -> Result<(), Error> {
// The closure that we want to fuzz
let mut harness = |input: &BytesInput| {
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 ExitKind::Crash;
}
}
}
ExitKind::Ok
};

// Create an observation channel using the signals map
let observer =
unsafe { StdMapObserver::new_from_ptr("signals", SIGNALS.as_mut_ptr(), SIGNALS.len()) };

let factory = MapEqualityFactory::new_from_observer(&observer);

// 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 = CrashFeedback::new();

// The Monitor trait define how the fuzzer stats are displayed to the user
let mon = SimpleMonitor::new(|s| println!("{}", s));

let mut mgr = SimpleEventManager::new(mon);

let corpus_dir = PathBuf::from("./corpus");
let solution_dir = PathBuf::from("./solutions");

// 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
OnDiskCorpus::new(&corpus_dir).unwrap(),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is a OnDiskCorpus kept in memory?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that'd need a CachedOnDiskCorpus or a InMemoryCorpus

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the comment is outdated here

// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(&solution_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();

// 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 mut executor = InProcessExecutor::new(
&mut harness,
tuple_list!(observer),
&mut fuzzer,
&mut state,
&mut mgr,
)
.expect("Failed to create the Executor");

// Generator of printable bytearrays of max size 32
let mut generator = RandPrintablesGenerator::new(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 minimizer = StdScheduledMutator::new(havoc_mutations());
let mut stages = tuple_list!(
StdMutationalStage::new(mutator),
StdTMinMutationalStage::new(minimizer, factory, 128)
);

while state.solutions().is_empty() {
fuzzer.fuzz_one(&mut stages, &mut executor, &mut state, &mut mgr)?;
}

let minimized_dir = PathBuf::from("./minimized");

let mut state = StdState::new(
StdRand::with_seed(current_nanos()),
OnDiskCorpus::new(&minimized_dir).unwrap(),
InMemoryCorpus::new(),
&mut (),
&mut (),
)
.unwrap();

// The Monitor trait define how the fuzzer stats are displayed to the user
let mon = SimpleMonitor::new(|s| println!("{}", s));

let mut mgr = SimpleEventManager::new(mon);

let minimizer = StdScheduledMutator::new(havoc_mutations());
let mut stages = tuple_list!(StdTMinMutationalStage::new(
minimizer,
CrashFeedbackFactory::default(),
1 << 10
));

let scheduler = QueueScheduler::new();

// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, (), ());

// Create the executor for an in-process function with just one observer
let mut executor = InProcessExecutor::new(&mut harness, (), &mut fuzzer, &mut state, &mut mgr)?;

state.load_initial_inputs_forced(&mut fuzzer, &mut executor, &mut mgr, &[solution_dir])?;
stages.perform_all(&mut fuzzer, &mut executor, &mut state, &mut mgr, 0)?;

Ok(())
}
2 changes: 1 addition & 1 deletion libafl/src/corpus/cached.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ where

/// Replaces the testcase at the given idx
#[inline]
fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<(), Error> {
fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<Testcase<I>, Error> {
// TODO finish
self.inner.replace(idx, testcase)
}
Expand Down
5 changes: 2 additions & 3 deletions libafl/src/corpus/inmemory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,11 @@ where

/// Replaces the testcase at the given idx
#[inline]
fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<(), Error> {
fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<Testcase<I>, Error> {
if idx >= self.entries.len() {
return Err(Error::key_not_found(format!("Index {} out of bounds", idx)));
}
self.entries[idx] = RefCell::new(testcase);
Ok(())
Ok(self.entries[idx].replace(testcase))
}

/// Removes an entry from the corpus, returning it if it was present.
Expand Down
10 changes: 7 additions & 3 deletions libafl/src/corpus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ where
/// Add an entry to the corpus and return its index
fn add(&mut self, testcase: Testcase<I>) -> Result<usize, Error>;

/// Replaces the testcase at the given idx
fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<(), Error>;
/// Replaces the testcase at the given idx, returning the existing.
fn replace(&mut self, idx: usize, testcase: Testcase<I>) -> Result<Testcase<I>, Error>;

/// Removes an entry from the corpus, returning it if it was present.
fn remove(&mut self, idx: usize) -> Result<Option<Testcase<I>>, Error>;
Expand Down Expand Up @@ -177,7 +177,11 @@ pub mod pybind {
}

#[inline]
fn replace(&mut self, idx: usize, testcase: Testcase<BytesInput>) -> Result<(), Error> {
fn replace(
&mut self,
idx: usize,
testcase: Testcase<BytesInput>,
) -> Result<Testcase<BytesInput>, Error> {
unwrap_me_mut!(self.wrapper, c, { c.replace(idx, testcase) })
}

Expand Down
Loading