added restarts to libfuzzer example, docu cleanup
This commit is contained in:
parent
454932ff91
commit
dadc486452
3
fuzzers/libfuzzer_libmozjpeg/.gitignore
vendored
3
fuzzers/libfuzzer_libmozjpeg/.gitignore
vendored
@ -1 +1,2 @@
|
||||
*.tar.gz
|
||||
*.tar.gz*
|
||||
mozjpeg-4.0.3
|
@ -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`.
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,8 +51,7 @@ 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) {
|
||||
let (state, mut restarting_mgr) = match setup_restarting_mgr_std(stats, broker_port) {
|
||||
Ok(res) => res,
|
||||
Err(err) => match err {
|
||||
Error::ShuttingDown => {
|
||||
@ -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(())
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user