diff --git a/fuzzers/libfuzzer_libmozjpeg/.gitignore b/fuzzers/libfuzzer_libmozjpeg/.gitignore index 335ec9573d..377532b6d2 100644 --- a/fuzzers/libfuzzer_libmozjpeg/.gitignore +++ b/fuzzers/libfuzzer_libmozjpeg/.gitignore @@ -1 +1,2 @@ -*.tar.gz +*.tar.gz* +mozjpeg-4.0.3 \ No newline at end of file diff --git a/fuzzers/libfuzzer_libmozjpeg/README.md b/fuzzers/libfuzzer_libmozjpeg/README.md index b302a1b2bb..826af53365 100644 --- a/fuzzers/libfuzzer_libmozjpeg/README.md +++ b/fuzzers/libfuzzer_libmozjpeg/README.md @@ -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`. diff --git a/fuzzers/libfuzzer_libmozjpeg/hook_allocs.c b/fuzzers/libfuzzer_libmozjpeg/hook_allocs.c index 2249ab6ea1..62ab077570 100644 --- a/fuzzers/libfuzzer_libmozjpeg/hook_allocs.c +++ b/fuzzers/libfuzzer_libmozjpeg/hook_allocs.c @@ -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; } diff --git a/fuzzers/libfuzzer_libmozjpeg/src/bin/cc.rs b/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cc.rs similarity index 100% rename from fuzzers/libfuzzer_libmozjpeg/src/bin/cc.rs rename to fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cc.rs diff --git a/fuzzers/libfuzzer_libmozjpeg/src/bin/cxx.rs b/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cxx.rs similarity index 100% rename from fuzzers/libfuzzer_libmozjpeg/src/bin/cxx.rs rename to fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cxx.rs diff --git a/fuzzers/libfuzzer_libpng/README.md b/fuzzers/libfuzzer_libpng/README.md index 8d88900306..922667e8fe 100644 --- a/fuzzers/libfuzzer_libpng/README.md +++ b/fuzzers/libfuzzer_libpng/README.md @@ -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 diff --git a/fuzzers/libfuzzer_libpng/src/lib.rs b/fuzzers/libfuzzer_libpng/src/lib.rs index 54914ecf0b..4d1b78ecd5 100644 --- a/fuzzers/libfuzzer_libpng/src/lib.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -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, 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, 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(()) } diff --git a/libafl/src/fuzzer.rs b/libafl/src/fuzzer.rs index 9016198f14..e1eeb08c06 100644 --- a/libafl/src/fuzzer.rs +++ b/libafl/src/fuzzer.rs @@ -47,6 +47,10 @@ where pub trait Fuzzer { /// 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 { /// 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( &mut self, state: &mut S, executor: &mut E, manager: &mut EM, scheduler: &CS, iters: u64, - ) -> Result { + ) -> Result + where + EM: EventManager, + I: Input, + { if iters == 0 { return Err(Error::IllegalArgument( "Cannot fuzz for 0 iterations!".to_string(), @@ -100,6 +108,11 @@ pub trait Fuzzer { 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) }