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.
|
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.
|
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:
|
Now compile it with:
|
||||||
|
|
||||||
```
|
```
|
||||||
cd mozjpeg-4.0.3
|
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`
|
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
|
## 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.
|
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.
|
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.
|
// Hence, even realloc(NULL, size) would loop in an optimized build.
|
||||||
// We fall back to a stricter allocation function. Fingers crossed.
|
// We fall back to a stricter allocation function. Fingers crossed.
|
||||||
void *ret = NULL;
|
void *ret = NULL;
|
||||||
posix_memalign(&ret, 1<<6, size);
|
if (posix_memalign(&ret, 1<<6, size) != 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -52,8 +54,9 @@ void *calloc(size_t nmemb, size_t size) {
|
|||||||
libafl_alloc_map[k] = MAX(libafl_alloc_map[k], size);
|
libafl_alloc_map[k] = MAX(libafl_alloc_map[k], size);
|
||||||
|
|
||||||
void *ret = NULL;
|
void *ret = NULL;
|
||||||
posix_memalign(&ret, 1<<6, size);
|
if (posix_memalign(&ret, 1<<6, size) != 0) {
|
||||||
memset(ret, 0, size);
|
return NULL;
|
||||||
|
};
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
# Libfuzzer for libpng
|
# Libfuzzer for libpng
|
||||||
|
|
||||||
This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection.
|
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.
|
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.
|
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.
|
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.
|
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:
|
Then download libpng, and unpack the archive:
|
||||||
```bash
|
```bash
|
||||||
wget https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
|
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
|
```bash
|
||||||
cd libpng-1.6.37
|
cd libpng-1.6.37
|
||||||
./configure
|
./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`.
|
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
|
./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
|
## Run
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ use libafl::{
|
|||||||
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
|
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
|
||||||
QueueCorpusScheduler,
|
QueueCorpusScheduler,
|
||||||
},
|
},
|
||||||
events::setup_restarting_mgr_std,
|
events::{setup_restarting_mgr_std, EventManager},
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
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));
|
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.
|
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
||||||
let (state, mut restarting_mgr) =
|
let (state, mut restarting_mgr) = match setup_restarting_mgr_std(stats, broker_port) {
|
||||||
match setup_restarting_mgr_std(stats, broker_port) {
|
Ok(res) => res,
|
||||||
Ok(res) => res,
|
Err(err) => match err {
|
||||||
Err(err) => match err {
|
Error::ShuttingDown => {
|
||||||
Error::ShuttingDown => {
|
return Ok(());
|
||||||
return Ok(());
|
}
|
||||||
}
|
_ => {
|
||||||
_ => {
|
panic!("Failed to setup the restarter: {}", err);
|
||||||
panic!("Failed to setup the restarter: {}", err);
|
}
|
||||||
}
|
},
|
||||||
},
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
let edges_observer = HitcountsMapObserver::new(unsafe {
|
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());
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,10 @@ where
|
|||||||
pub trait Fuzzer<E, EM, S, CS> {
|
pub trait Fuzzer<E, EM, S, CS> {
|
||||||
/// Fuzz for a single iteration
|
/// Fuzz for a single iteration
|
||||||
/// Returns the index of the last fuzzed corpus item
|
/// 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(
|
fn fuzz_one(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
@ -75,16 +79,20 @@ pub trait Fuzzer<E, EM, S, CS> {
|
|||||||
/// Returns the index of the last fuzzed corpus item
|
/// Returns the index of the last fuzzed corpus item
|
||||||
///
|
///
|
||||||
/// If you use this fn in a restarting scenario to only run for `n` iterations,
|
/// 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.
|
/// This way, the state will be available in the next, respawned, iteration.
|
||||||
fn fuzz_loop_for(
|
fn fuzz_loop_for<I>(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
scheduler: &CS,
|
scheduler: &CS,
|
||||||
iters: u64,
|
iters: u64,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error>
|
||||||
|
where
|
||||||
|
EM: EventManager<I, S>,
|
||||||
|
I: Input,
|
||||||
|
{
|
||||||
if iters == 0 {
|
if iters == 0 {
|
||||||
return Err(Error::IllegalArgument(
|
return Err(Error::IllegalArgument(
|
||||||
"Cannot fuzz for 0 iterations!".to_string(),
|
"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)?;
|
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)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user