diff --git a/Cargo.toml b/Cargo.toml index bc3ba2904a..38b637badc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,11 +9,15 @@ members = [ "libafl", "libafl_derive", "libafl_cc", + "libafl_targets", #example fuzzers - "fuzzers/libfuzzer_libpng", + #"fuzzers/libfuzzer_libpng", "fuzzers/frida_libpng", "fuzzers/libfuzzer_libmozjpeg", "fuzzers/libfuzzer_libpng_cmpalloc", "fuzzers/libfuzzer_windows", ] +exclude = [ + "fuzzers/libfuzzer_libpng", +] diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index 7c0615cfd0..b4892d868c 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -3,7 +3,6 @@ name = "libfuzzer_libpng" version = "0.1.0" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2018" -build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -11,21 +10,21 @@ build = "build.rs" default = ["std"] std = [] -#[profile.release] -#lto = true -#codegen-units = 1 -#opt-level = 3 -#debug = true - -[build-dependencies] -cc = { version = "1.0", features = ["parallel"] } -num_cpus = "1.0" +[profile.release] +lto = true +codegen-units = 1 +opt-level = 3 +debug = true [dependencies] libafl = { path = "../../libafl/" } +# TODO Include it only when building cc +libafl_cc = { path = "../../libafl_cc/" } -[[example]] +[lib] name = "libfuzzer_libpng" path = "./src/fuzzer.rs" test = false bench = false +crate-type = ["staticlib"] + diff --git a/fuzzers/libfuzzer_libpng/src/bin/cc.rs b/fuzzers/libfuzzer_libpng/src/bin/cc.rs new file mode 100644 index 0000000000..ad820b5904 --- /dev/null +++ b/fuzzers/libfuzzer_libpng/src/bin/cc.rs @@ -0,0 +1,13 @@ +use libafl_cc::{ClangWrapper, CompilerWrapper}; +use std::env; + +fn main() { + let args: Vec = env::args().collect(); + ClangWrapper::new("clang", "clang++") + .from_args(&args) + .unwrap() + .add_arg("-fsanitize=trace-pc-guard".into()) + .unwrap() + .run() + .unwrap(); +} diff --git a/fuzzers/libfuzzer_libpng_old/.gitignore b/fuzzers/libfuzzer_libpng_old/.gitignore new file mode 100644 index 0000000000..a977a2ca5b --- /dev/null +++ b/fuzzers/libfuzzer_libpng_old/.gitignore @@ -0,0 +1 @@ +libpng-* \ No newline at end of file diff --git a/fuzzers/libfuzzer_libpng_old/Cargo.toml b/fuzzers/libfuzzer_libpng_old/Cargo.toml new file mode 100644 index 0000000000..7c0615cfd0 --- /dev/null +++ b/fuzzers/libfuzzer_libpng_old/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "libfuzzer_libpng" +version = "0.1.0" +authors = ["Andrea Fioraldi ", "Dominik Maier "] +edition = "2018" +build = "build.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["std"] +std = [] + +#[profile.release] +#lto = true +#codegen-units = 1 +#opt-level = 3 +#debug = true + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } +num_cpus = "1.0" + +[dependencies] +libafl = { path = "../../libafl/" } + +[[example]] +name = "libfuzzer_libpng" +path = "./src/fuzzer.rs" +test = false +bench = false diff --git a/fuzzers/libfuzzer_libpng_old/README.md b/fuzzers/libfuzzer_libpng_old/README.md new file mode 100644 index 0000000000..f56138c2b5 --- /dev/null +++ b/fuzzers/libfuzzer_libpng_old/README.md @@ -0,0 +1,25 @@ +# Libfuzzer for libpng + +This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. +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. + +## Build + +To build this example, run `cargo build --example libfuzzer_libpng --release`. +This will call (the build.rs)[./builld.rs], which in turn downloads a libpng archive from the web. +Then, it will link (the fuzzer)[./src/fuzzer.rs] against (the C++ harness)[./harness.cc] and the instrumented `libpng`. +Afterwards, the fuzzer will be ready to run, from `../../target/examples/libfuzzer_libpng`. + +## Run + +The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. + +Each following execution will run a fuzzer client. +As this example uses in-process fuzzing, we added a Restarting Event Manager (`setup_restarting_mgr`). +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). + +For convenience, you may just run `./test.sh` in this folder to test it. diff --git a/fuzzers/libfuzzer_libpng/build.rs b/fuzzers/libfuzzer_libpng_old/build.rs similarity index 100% rename from fuzzers/libfuzzer_libpng/build.rs rename to fuzzers/libfuzzer_libpng_old/build.rs diff --git a/fuzzers/libfuzzer_libpng_old/corpus/not_kitty.png b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty.png new file mode 100644 index 0000000000..eff7c1707b Binary files /dev/null and b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty.png differ diff --git a/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_alpha.png b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_alpha.png new file mode 100644 index 0000000000..2fb8da2c8f Binary files /dev/null and b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_alpha.png differ diff --git a/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_gamma.png b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_gamma.png new file mode 100644 index 0000000000..939d9d29a9 Binary files /dev/null and b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_gamma.png differ diff --git a/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_icc.png b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_icc.png new file mode 100644 index 0000000000..f0c7804d99 Binary files /dev/null and b/fuzzers/libfuzzer_libpng_old/corpus/not_kitty_icc.png differ diff --git a/fuzzers/libfuzzer_libpng/harness.cc b/fuzzers/libfuzzer_libpng_old/harness.cc similarity index 100% rename from fuzzers/libfuzzer_libpng/harness.cc rename to fuzzers/libfuzzer_libpng_old/harness.cc diff --git a/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs b/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs new file mode 100644 index 0000000000..163a4c43ed --- /dev/null +++ b/fuzzers/libfuzzer_libpng_old/src/fuzzer.rs @@ -0,0 +1,179 @@ +//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts +//! The example harness is built for libpng. + +#[cfg(unix)] +use core::time::Duration; +use std::{env, path::PathBuf}; + +#[cfg(unix)] +use libafl::{ + bolts::{shmem::UnixShMem, tuples::tuple_list}, + corpus::{ + Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, + QueueCorpusScheduler, + }, + events::setup_restarting_mgr, + executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, + feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, + fuzzer::{Fuzzer, HasCorpusScheduler, StdFuzzer}, + mutators::{scheduled::HavocBytesMutator, token_mutations::Tokens}, + observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, + stages::mutational::StdMutationalStage, + state::{HasCorpus, HasMetadata, State}, + stats::SimpleStats, + utils::{current_nanos, StdRand}, + Error, +}; + +/// We will interact with a C++ target, so use external c functionality +#[cfg(unix)] +extern "C" { + /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) + fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; + + // afl_libfuzzer_init calls LLVMFUzzerInitialize() + fn afl_libfuzzer_init() -> i32; + + static __lafl_edges_map: *mut u8; + static __lafl_cmp_map: *mut u8; + static __lafl_max_edges_size: u32; +} + +/// The main fn, usually parsing parameters, and starting the fuzzer +pub fn main() { + // Registry the metadata types used in this fuzzer + // Needed only on no_std + //RegistryBuilder::register::(); + + println!( + "Workdir: {:?}", + env::current_dir().unwrap().to_string_lossy().to_string() + ); + fuzz( + vec![PathBuf::from("./corpus")], + PathBuf::from("./crashes"), + 1337, + ) + .expect("An error occurred while fuzzing"); +} + +/// Not supported on windows right now +#[cfg(windows)] +fn fuzz(_corpus_dirs: Vec, _objective_dir: PathBuf, _broker_port: u16) -> Result<(), ()> { + todo!("Example not supported on Windows"); +} + +/// The actual fuzzer +#[cfg(unix)] +fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { + // 'While the stats are state, they are usually used in the broker - which is likely never restarted + 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::<_, _, UnixShMem, _>(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 { + StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize) + }); + + // If not restarting, create a State from scratch + let mut state = state.unwrap_or_else(|| { + State::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::new(), + // Feedbacks to rate the interestingness of an input + tuple_list!( + MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), + TimeFeedback::new() + ), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + OnDiskCorpus::new(objective_dir).unwrap(), + // Feedbacks to recognize an input as solution + tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()), + ) + }); + + println!("We're a client, let's fuzz :)"); + + // Create a PNG dictionary if not existing + if state.metadata().get::().is_none() { + state.add_metadata(Tokens::new(vec![ + vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header + "IHDR".as_bytes().to_vec(), + "IDAT".as_bytes().to_vec(), + "PLTE".as_bytes().to_vec(), + "IEND".as_bytes().to_vec(), + ])); + } + + // Setup a basic mutator with a mutational stage + let mutator = HavocBytesMutator::default(); + let stage = StdMutationalStage::new(mutator); + + // A fuzzer with just one stage and a minimization+queue policy to get testcasess from the corpus + let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); + let fuzzer = StdFuzzer::new(scheduler, tuple_list!(stage)); + + // The wrapped harness function, calling out to the LLVM-style harness + let mut harness = |buf: &[u8]| { + unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; + ExitKind::Ok + }; + + // Create the executor for an in-process function with just one observer for edge coverage + let mut executor = TimeoutExecutor::new( + InProcessExecutor::new( + "in-process(edges)", + &mut harness, + tuple_list!(edges_observer, TimeObserver::new("time")), + &mut state, + &mut restarting_mgr, + )?, + // 10 seconds timeout + Duration::new(10, 0), + ); + + // The actual target run starts here. + // Call LLVMFUzzerInitialize() if present. + unsafe { + if afl_libfuzzer_init() == -1 { + println!("Warning: LLVMFuzzerInitialize failed with -1") + } + } + + // In case the corpus is empty (on first run), reset + if state.corpus().count() < 1 { + state + .load_initial_inputs( + &mut executor, + &mut restarting_mgr, + fuzzer.scheduler(), + &corpus_dirs, + ) + .expect(&format!( + "Failed to load initial corpus at {:?}", + &corpus_dirs + )); + println!("We imported {} inputs from disk.", state.corpus().count()); + } + + fuzzer.fuzz_loop(&mut state, &mut executor, &mut restarting_mgr)?; + + // Never reached + Ok(()) +} diff --git a/fuzzers/libfuzzer_libpng/test.sh b/fuzzers/libfuzzer_libpng_old/test.sh similarity index 100% rename from fuzzers/libfuzzer_libpng/test.sh rename to fuzzers/libfuzzer_libpng_old/test.sh diff --git a/libafl_cc/src/bin/libafl-cc.rs b/libafl_cc/src/bin/libafl-cc.rs deleted file mode 100644 index f3e95c9cc1..0000000000 --- a/libafl_cc/src/bin/libafl-cc.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - todo!("libafl-cc"); -} diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index e366d8cae0..85ad5b5cbc 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -75,7 +75,7 @@ impl CompilerWrapper for ClangWrapper { let mut linking = true; // Detect stray -v calls from ./configure scripts. - if args.len() == 1 && args[1] == "-v" { + if args.len() > 1 && args[1] == "-v" { linking = false; } diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml new file mode 100644 index 0000000000..f7c5fb3646 --- /dev/null +++ b/libafl_targets/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "libafl_targets" +version = "0.1.0" +authors = ["Andrea Fioraldi "] +edition = "2018" + +[features] +default = ["sancov", "libfuzzer_compatibility"] +sancov = [] +libfuzzer_compatibility = [] + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } + +[dependencies] diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs new file mode 100644 index 0000000000..5c2a27f6f3 --- /dev/null +++ b/libafl_targets/build.rs @@ -0,0 +1,22 @@ +// build.rs + +use std::env; + +fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + let out_dir = out_dir.to_string_lossy().to_string(); + //let out_dir_path = Path::new(&out_dir); + + #[cfg(feature = "libfuzzer_compatibility")] + { + println!("cargo:rerun-if-changed=libfuzzer_compatibility.c"); + + cc::Build::new() + .file("libfuzzer_compatibility.c") + .compile("libfuzzer-compatibility"); + } + + println!("cargo:rustc-link-search=native={}", &out_dir); + + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/libafl_targets/libfuzzer_compatibility.c b/libafl_targets/libfuzzer_compatibility.c new file mode 100644 index 0000000000..d5a304348a --- /dev/null +++ b/libafl_targets/libfuzzer_compatibility.c @@ -0,0 +1,25 @@ +static int orig_argc; +static char **orig_argv; +static char **orig_envp; + +static void save_main_args(int argc, char** argv, char** envp) { + orig_argc = argc; + orig_argv = argv; + orig_envp = envp; +} + +__attribute__((section(".init_array"))) +void (*p_libafl_targets_save_main_args)(int, char*[], char*[]) = &save_main_args; + +__attribute__((weak)) +int LLVMFuzzerInitialize(int *argc, char ***argv); + +int libafl_targets_libfuzzer_init() { + + if (LLVMFuzzerInitialize) { + return LLVMFuzzerInitialize(&orig_argc, &orig_argv); + } else { + return 0; + } + +} diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs new file mode 100644 index 0000000000..78dcf55923 --- /dev/null +++ b/libafl_targets/src/lib.rs @@ -0,0 +1,10 @@ +#[cfg(feature = "sancov")] +pub mod sancov; + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/libafl_targets/src/sancov.rs b/libafl_targets/src/sancov.rs new file mode 100644 index 0000000000..6b99a15124 --- /dev/null +++ b/libafl_targets/src/sancov.rs @@ -0,0 +1,26 @@ +// TODO compile time flag +pub const MAP_SIZE: usize = 65536; + +pub static mut EDGES_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; +pub static mut CMP_MAP: [u8; MAP_SIZE] = [0; MAP_SIZE]; +pub static mut MAX_EDGES_NUM: usize = 0; + +#[no_mangle] +pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { + let pos = *guard as usize; + let val = (EDGES_MAP[pos] as u8).wrapping_add(1); + EDGES_MAP[pos] = val; +} + +#[no_mangle] +pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, stop: *mut u32) { + if start == stop || *start != 0 { + return; + } + + while start < stop { + MAX_EDGES_NUM += 1; + *start = (MAX_EDGES_NUM & (MAP_SIZE - 1)) as u32; + start = start.offset(1); + } +}