added restarts to libfuzzer example, docu cleanup

This commit is contained in:
Dominik Maier 2021-04-29 10:55:31 +02:00
parent 454932ff91
commit dadc486452
8 changed files with 76 additions and 30 deletions

View File

@ -1 +1,2 @@
*.tar.gz
*.tar.gz*
mozjpeg-4.0.3

View File

@ -10,23 +10,30 @@ To build this example, run `cargo build --release`.
This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer. the SanitizerCoverage runtime functions for edges and value-profile feedbacks and the `hook_allocs.c` C file that hooks the allocator to report the size to the fuzzer.
In addition, it will build also two C and C++ compiler wrappers (bin/c(c/xx).rs) that you must use to compile the target.
Then download the mozjpeg source tarball from https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz and unpack the archive.
Then download the mozjpeg source tarball from and unpack the archive:
```bash
wget https://github.com/mozilla/mozjpeg/archive/v4.0.3.tar.gz
tar -xzvf v4.0.3.tar.gz
```
Now compile it with:
```
cd mozjpeg-4.0.3
cmake --disable-shared . -DCMAKE_C_COMPILER=/path/to/libfuzzer_mozjpeg/target/release/cc -DCMAKE_CXX_COMPILER=/path/to/libfuzzer_mozjpeg/target/release/cxx -G "Unix Makefiles"
cmake --disable-shared . -DCMAKE_C_COMPILER=$(realpath ../target/release/libafl_cc) -DCMAKE_CXX_COMPILER=$(realpath ../target/release/libafl_cxx) -G "Unix Makefiles"
make -j `nproc`
cd ..
```
Now, we have to build the libfuzzer harness and link all togheter to create our fuzzer binary.
Now, we have to build the libfuzzer harness and link all together to create our fuzzer binary.
```
/path/to/libfuzzer_libpng/target/debug/cxx /path/to/libfuzzer_libpng/harness.cc mozjpeg-4.0.3/*.a -I mozjpeg-4.0.3/ -o fuzzer
./target/debug/cxx ./harness.cc ./mozjpeg-4.0.3/*.a -I ./mozjpeg-4.0.3/ -o fuzzer_mozjpeg
```
Afterwards, the fuzzer will be ready to run simply executing `./fuzzer`.
Afterward, the fuzzer will be ready to run by simply executing `./fuzzer_mozjpeg`.
Note that, unless you use the `launcher`, you will have to run the binary multiple times to actually start the fuzz process, see `Run` in the following.
This allows you to run multiple different builds of the same fuzzer alongside, for example, with and without ASAN (`-fsanitize=address`) or with different mutators.
## Run
@ -37,4 +44,4 @@ As this example uses in-process fuzzing, we added a Restarting Event Manager (`s
This means each client will start itself again to listen for crashes and timeouts.
By restarting the actual fuzzer, it can recover from these exit conditions.
In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet).
In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically, unless you use the `launcher`.

View File

@ -37,7 +37,9 @@ void *malloc(size_t size) {
// Hence, even realloc(NULL, size) would loop in an optimized build.
// We fall back to a stricter allocation function. Fingers crossed.
void *ret = NULL;
posix_memalign(&ret, 1<<6, size);
if (posix_memalign(&ret, 1<<6, size) != 0) {
return NULL;
}
return ret;
}
@ -52,8 +54,9 @@ void *calloc(size_t nmemb, size_t size) {
libafl_alloc_map[k] = MAX(libafl_alloc_map[k], size);
void *ret = NULL;
posix_memalign(&ret, 1<<6, size);
memset(ret, 0, size);
if (posix_memalign(&ret, 1<<6, size) != 0) {
return NULL;
};
return ret;
}

View File

@ -1,6 +1,11 @@
# Libfuzzer for libpng
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection.
In contrast to other fuzzer examples, this setup uses `fuzz_loop_for`, to occasionally respawn the fuzzer executor.
While this costs performance, it can be useful for targets with memory leaks or other instabilities.
If your target is really instable, however, consider exchanging the `InProcessExecutor` for a `ForkserverExecutor` instead.
To show off crash detection, we added a `ud2` instruction to the harness, edit harness.cc if you want a non-crashing example.
It has been tested on Linux.
@ -15,6 +20,8 @@ cargo build --release
This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback.
In addition, it will also build two C and C++ compiler wrappers (bin/libafl_c(libafl_c/xx).rs) that you must use to compile the target.
The compiler wrappers, `libafl_cc` and libafl_cxx`, will end up in `./target/release/` (or `./target/debug`, in case you did not build with the `--release` flag).
Then download libpng, and unpack the archive:
```bash
wget https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
@ -26,7 +33,7 @@ Now compile libpng, using the libafl_cc compiler wrapper:
```bash
cd libpng-1.6.37
./configure
make CC=../target/release/libafl_cc CXX=../target/release/libafl_cxx -j `nproc`
make CC=$(realpath ../target/release/libafl_cc) CXX=$(realpath ../target/release/libafl_cxx) -j `nproc`
```
You can find the static lib at `libpng-1.6.37/.libs/libpng16.a`.
@ -38,7 +45,9 @@ cd ..
./target/release/libafl_cxx ./harness.cc libpng-1.6.37/.libs/libpng16.a -I libpng-1.6.37/ -o fuzzer_libpng -lz -lm
```
Afterwards, the fuzzer will be ready to run.
Afterward, the fuzzer will be ready to run.
Note that, unless you use the `launcher`, you will have to run the binary multiple times to actually start the fuzz process, see `Run` in the following.
This allows you to run multiple different builds of the same fuzzer alongside, for example, with and without ASAN (`-fsanitize=address`) or with different mutators.
## Run

View File

@ -10,7 +10,7 @@ use libafl::{
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
QueueCorpusScheduler,
},
events::setup_restarting_mgr_std,
events::{setup_restarting_mgr_std, EventManager},
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer},
@ -51,18 +51,17 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
let stats = SimpleStats::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_std(stats, broker_port) {
Ok(res) => res,
Err(err) => match err {
Error::ShuttingDown => {
return Ok(());
}
_ => {
panic!("Failed to setup the restarter: {}", err);
}
},
};
let (state, mut restarting_mgr) = match setup_restarting_mgr_std(stats, broker_port) {
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 = HitcountsMapObserver::new(unsafe {
@ -149,8 +148,22 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
println!("We imported {} inputs from disk.", state.corpus().count());
}
fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr, &scheduler)?;
// 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 state,
&mut executor,
&mut restarting_mgr,
&scheduler,
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)?;
// Never reached
Ok(())
}

View File

@ -47,6 +47,10 @@ where
pub trait Fuzzer<E, EM, S, CS> {
/// Fuzz for a single iteration
/// Returns the index of the last fuzzed corpus item
///
/// If you use this fn in a restarting scenario to only run for `n` iterations,
/// before exiting, make sure you call `event_mgr.on_restart(&mut state)?;`.
/// This way, the state will be available in the next, respawned, iteration.
fn fuzz_one(
&mut self,
state: &mut S,
@ -75,16 +79,20 @@ pub trait Fuzzer<E, EM, S, CS> {
/// Returns the index of the last fuzzed corpus item
///
/// If you use this fn in a restarting scenario to only run for `n` iterations,
/// before exiting, make sure you call `event_mgr.on_restart(state)?;`.
/// before exiting, make sure you call `event_mgr.on_restart(&mut state)?;`.
/// This way, the state will be available in the next, respawned, iteration.
fn fuzz_loop_for(
fn fuzz_loop_for<I>(
&mut self,
state: &mut S,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
iters: u64,
) -> Result<usize, Error> {
) -> Result<usize, Error>
where
EM: EventManager<I, S>,
I: Input,
{
if iters == 0 {
return Err(Error::IllegalArgument(
"Cannot fuzz for 0 iterations!".to_string(),
@ -100,6 +108,11 @@ pub trait Fuzzer<E, EM, S, CS> {
last = Self::maybe_report_stats(state, manager, last, stats_timeout)?;
}
// If we would assume the fuzzer loop will always exit after this, we could do this here:
// manager.on_restart(state)?;
// But as the state may grow to a few megabytes,
// for now we won' and the user has to do it (unless we find a way to do this on `Drop`).
Ok(ret)
}