diff --git a/fuzzers/forkserver_simple/Cargo.toml b/fuzzers/forkserver_simple/Cargo.toml index 929769791d..be189280b8 100644 --- a/fuzzers/forkserver_simple/Cargo.toml +++ b/fuzzers/forkserver_simple/Cargo.toml @@ -17,4 +17,4 @@ opt-level = 3 [dependencies] libafl = { path = "../../libafl/" } -clap = { version = "3.0.0-beta.2", features = ["default"] } \ No newline at end of file +clap = { version = "3.0.0-rc.4", features = ["default"] } \ No newline at end of file diff --git a/fuzzers/frida_libpng/Cargo.toml b/fuzzers/frida_libpng/Cargo.toml index 7a2a6fffe5..80b49f3bca 100644 --- a/fuzzers/frida_libpng/Cargo.toml +++ b/fuzzers/frida_libpng/Cargo.toml @@ -38,7 +38,7 @@ libc = "0.2" libloading = "0.7.0" num-traits = "0.2.14" rangemap = "0.1.10" -clap = "2.33" +structopt = "0.3.25" serde = "1.0" backtrace = "0.3" diff --git a/fuzzers/frida_libpng/build.rs b/fuzzers/frida_libpng/build.rs index 1eadfd1164..27af7786f6 100644 --- a/fuzzers/frida_libpng/build.rs +++ b/fuzzers/frida_libpng/build.rs @@ -47,34 +47,30 @@ fn main() { let zlib_path = Path::new(&zlib); let zlib_tar = format!("{}/zlib-1.2.11.tar.gz", &cwd); - if !libpng_path.is_dir() { - if !Path::new(&libpng_tar).is_file() { - println!("cargo:warning=Libpng not found, downloading..."); - // Download libpng - let mut resp = reqwest::blocking::get(LIBPNG_URL).expect("Libpng download failed"); - let mut out = File::create(&libpng_tar).expect("Libpng download failed"); - io::copy(&mut resp, &mut out).expect("Libpng downlaod failed"); + if !libpng_path.is_dir() && !Path::new(&libpng_tar).is_file() { + println!("cargo:warning=Libpng not found, downloading..."); + // Download libpng + let mut resp = reqwest::blocking::get(LIBPNG_URL).expect("Libpng download failed"); + let mut out = File::create(&libpng_tar).expect("Libpng download failed"); + io::copy(&mut resp, &mut out).expect("Libpng downlaod failed"); - let tar_xz = File::open(&libpng_tar).expect("Libpng extraction failed"); - let tar = XzDecoder::new(tar_xz); - let mut archive = Archive::new(tar); - archive.unpack(&cwd).expect("Libpng extraction failed"); - } + let tar_xz = File::open(&libpng_tar).expect("Libpng extraction failed"); + let tar = XzDecoder::new(tar_xz); + let mut archive = Archive::new(tar); + archive.unpack(&cwd).expect("Libpng extraction failed"); } - if !zlib_path.is_dir() { - if !Path::new(&zlib_tar).is_file() { - println!("cargo:warning=Zlib not found, downloading..."); - // Download Zlib - let mut resp = reqwest::blocking::get(ZLIB_URL).expect("Zlib download failed"); - let mut out = File::create(&zlib_tar).expect("Zlib download failed"); - io::copy(&mut resp, &mut out).expect("Zlib downlaod failed"); + if !zlib_path.is_dir() && !Path::new(&zlib_tar).is_file() { + println!("cargo:warning=Zlib not found, downloading..."); + // Download Zlib + let mut resp = reqwest::blocking::get(ZLIB_URL).expect("Zlib download failed"); + let mut out = File::create(&zlib_tar).expect("Zlib download failed"); + io::copy(&mut resp, &mut out).expect("Zlib downlaod failed"); - let tar_gz = File::open(&zlib_tar).expect("Zlib extraction failed"); - let tar = GzDecoder::new(tar_gz); - let mut archive = Archive::new(tar); - archive.unpack(&cwd).expect("Zlib extraction failed"); - rename(zlib_1_2_11, zlib).expect("Zlib extraction failed"); - } + let tar_gz = File::open(&zlib_tar).expect("Zlib extraction failed"); + let tar = GzDecoder::new(tar_gz); + let mut archive = Archive::new(tar); + archive.unpack(&cwd).expect("Zlib extraction failed"); + rename(zlib_1_2_11, zlib).expect("Zlib extraction failed"); } println!("cargo:warning=Now compile libpng with either visual studio or msys2"); @@ -128,7 +124,7 @@ fn main() { .arg(&libpng_tar) .status() .unwrap(); - Command::new(format!("{}/configure", &libpng)) + Command::new("./configure") .current_dir(&libpng_path) .args(&[ "--disable-shared", diff --git a/fuzzers/frida_libpng/src/fuzzer.rs b/fuzzers/frida_libpng/src/fuzzer.rs index 80d2378a72..a1c8c2e69b 100644 --- a/fuzzers/frida_libpng/src/fuzzer.rs +++ b/fuzzers/frida_libpng/src/fuzzer.rs @@ -1,13 +1,20 @@ //! A libfuzzer-like fuzzer with llmp-multithreading support and restarts //! The example harness is built for libpng. -use clap::{App, Arg}; +use frida_gum::Gum; +use std::{ + env, + net::SocketAddr, + path::{Path, PathBuf}, + time::Duration, +}; +use structopt::StructOpt; use libafl::{ bolts::{ current_nanos, launcher::Launcher, - os::parse_core_bind_arg, + os::Cores, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::{tuple_list, Merge}, @@ -34,75 +41,126 @@ use libafl::{ Error, }; -use frida_gum::Gum; - -use std::{ - env, - net::SocketAddr, - path::{Path, PathBuf}, -}; - +#[cfg(unix)] +use libafl_frida::asan_errors::{AsanErrorsFeedback, AsanErrorsObserver, ASAN_ERRORS}; use libafl_frida::{ coverage_rt::MAP_SIZE, executor::FridaInProcessExecutor, helper::{FridaHelper, FridaInstrumentationHelper}, FridaOptions, }; - -#[cfg(unix)] -use libafl_frida::asan_errors::{AsanErrorsFeedback, AsanErrorsObserver, ASAN_ERRORS}; - use libafl_targets::cmplog::{CmpLogObserver, CMPLOG_MAP}; +fn timeout_from_millis_str(time: &str) -> Result { + Ok(Duration::from_millis(time.parse()?)) +} + +#[derive(Debug, StructOpt)] +#[structopt( + name = "libafl_frida", + version = "0.1.0", + about = "A frida-based binary-only libfuzzer-style fuzzer for with llmp-multithreading support", + author = "s1341 , + Dongjia Zhang , Andrea Fioraldi , Dominik Maier " +)] +struct Opt { + #[structopt( + short, + long, + parse(try_from_str = Cores::from_cmdline), + help = "Spawn a client in each of the provided cores. Broker runs in the 0th core. 'all' to select all available cores. 'none' to run a client without binding to any core. eg: '1,2-4,6' selects the cores 1,2,3,4,6.", + name = "CORES" + )] + cores: Cores, + + #[structopt( + short = "p", + long, + help = "Choose the broker TCP port, default is 1337", + name = "PORT", + default_value = "1337" + )] + broker_port: u16, + + #[structopt( + parse(try_from_str), + short = "a", + long, + help = "Specify a remote broker", + name = "REMOTE" + )] + remote_broker_addr: Option, + + #[structopt( + parse(try_from_str), + short, + long, + help = "Set an initial corpus directory", + name = "INPUT" + )] + input: Vec, + + #[structopt( + short, + long, + parse(try_from_str), + help = "Set the output directory, default is ./out", + name = "OUTPUT", + default_value = "./out" + )] + output: PathBuf, + + #[structopt( + parse(try_from_str = timeout_from_millis_str), + short, + long, + help = "Set the exeucution timeout in milliseconds, default is 1000", + name = "TIMEOUT", + default_value = "1000" + )] + timeout: Duration, + + #[structopt( + parse(from_os_str), + short = "x", + long, + help = "Feed the fuzzer with an user-specified list of tokens (often called \"dictionary\"", + name = "TOKENS", + multiple = true + )] + tokens: Vec, + + #[structopt( + long, + help = "The configuration this fuzzer runs with, for multiprocessing", + name = "CONF", + default_value = "default launcher" + )] + configuration: String, + + #[structopt( + long, + help = "The file to redirect stdout input to (/dev/null if unset)" + )] + stdout_file: Option, + + #[structopt(help = "The harness")] + harness: String, + + #[structopt(help = "The symbol name to look up and hook")] + symbol: String, + + #[structopt(help = "The modules to instrument, separated by colons")] + modules_to_instrument: String, +} + /// 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::(); - let matches = App::new("libafl_frida") - .version("0.1.0") - .arg( - Arg::with_name("cores") - .short("c") - .long("cores") - .value_name("CORES") - .required(true) - .takes_value(true), - ) - .arg(Arg::with_name("harness").required(true).index(1)) - .arg(Arg::with_name("symbol").required(true).index(2)) - .arg( - Arg::with_name("modules_to_instrument") - .required(true) - .index(3), - ) - .arg( - Arg::with_name("configuration") - .required(false) - .value_name("CONF") - .takes_value(true), - ) - .arg( - Arg::with_name("output") - .short("o") - .long("output") - .value_name("OUTPUT") - .required(false) - .takes_value(true), - ) - .arg( - Arg::with_name("b2baddr") - .short("B") - .long("b2baddr") - .value_name("B2BADDR") - .required(false) - .takes_value(true), - ) - .get_matches(); - - let cores = parse_core_bind_arg(&matches.value_of("cores").unwrap().to_string()).unwrap(); - + let opt = Opt::from_args(); color_backtrace::install(); println!( @@ -110,30 +168,19 @@ pub fn main() { env::current_dir().unwrap().to_string_lossy().to_string() ); - let broker_addr = matches - .value_of("b2baddr") - .map(|addrstr| addrstr.parse().unwrap()); - unsafe { match fuzz( - matches.value_of("harness").unwrap(), - matches.value_of("symbol").unwrap(), - &matches - .value_of("modules_to_instrument") - .unwrap() - .split(':') - .collect::>(), + &opt.harness, + &opt.symbol, + &opt.modules_to_instrument.split(':').collect::>(), //modules_to_instrument, - &[PathBuf::from("./corpus")], - &PathBuf::from("./crashes"), - 1337, - &cores, - matches.value_of("output"), - broker_addr, - matches - .value_of("configuration") - .unwrap_or("default launcher") - .to_string(), + &opt.input, + &opt.output, + opt.broker_port, + &opt.cores, + opt.stdout_file.as_deref(), + opt.remote_broker_addr, + opt.configuration, ) { Ok(()) | Err(Error::ShuttingDown) => println!("\nFinished fuzzing. Good bye."), Err(e) => panic!("Error during fuzzing: {:?}", e), @@ -150,7 +197,7 @@ unsafe fn fuzz( corpus_dirs: &[PathBuf], objective_dir: &Path, broker_port: u16, - cores: &[usize], + cores: &Cores, stdout_file: Option<&str>, broker_addr: Option, configuration: String, diff --git a/fuzzers/fuzzbench/Cargo.toml b/fuzzers/fuzzbench/Cargo.toml index 817863ad64..e728c5d9c4 100644 --- a/fuzzers/fuzzbench/Cargo.toml +++ b/fuzzers/fuzzbench/Cargo.toml @@ -24,7 +24,7 @@ libafl = { path = "../../libafl/" } libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_cmplog", "libfuzzer"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } -clap = { version = "3.0.0-beta.2", features = ["default"] } +clap = { version = "3.0.0-rc.4", features = ["default"] } nix = "0.23.0" [lib] diff --git a/fuzzers/fuzzbench_gsoc/Cargo.toml b/fuzzers/fuzzbench_gsoc/Cargo.toml index 817863ad64..e728c5d9c4 100644 --- a/fuzzers/fuzzbench_gsoc/Cargo.toml +++ b/fuzzers/fuzzbench_gsoc/Cargo.toml @@ -24,7 +24,7 @@ libafl = { path = "../../libafl/" } libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_cmplog", "libfuzzer"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } -clap = { version = "3.0.0-beta.2", features = ["default"] } +clap = { version = "3.0.0-rc.4", features = ["default"] } nix = "0.23.0" [lib] diff --git a/fuzzers/fuzzbench_qemu/Cargo.toml b/fuzzers/fuzzbench_qemu/Cargo.toml index e45625f178..db80c0ae91 100644 --- a/fuzzers/fuzzbench_qemu/Cargo.toml +++ b/fuzzers/fuzzbench_qemu/Cargo.toml @@ -14,5 +14,5 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } libafl_qemu = { path = "../../libafl_qemu/" } -clap = { version = "3.0.0-beta.2", features = ["default"] } +clap = { version = "3.0.0-rc.4", features = ["default"] } nix = "0.23.0" diff --git a/fuzzers/generic_inmemory/Cargo.toml b/fuzzers/generic_inmemory/Cargo.toml index 9be2ef6b6f..c080a13135 100644 --- a/fuzzers/generic_inmemory/Cargo.toml +++ b/fuzzers/generic_inmemory/Cargo.toml @@ -24,7 +24,7 @@ libafl = { path = "../../libafl/" } libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_cmplog", "libfuzzer"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } -clap = { version = "3.0.0-beta.2", features = ["default", "yaml"] } +structopt = "0.3.25" [lib] name = "generic_inmemory" diff --git a/fuzzers/generic_inmemory/src/clap-config.yaml b/fuzzers/generic_inmemory/src/clap-config.yaml deleted file mode 100644 index d167a93439..0000000000 --- a/fuzzers/generic_inmemory/src/clap-config.yaml +++ /dev/null @@ -1,42 +0,0 @@ -name: generic_inmemory -author: "Andrea Fioraldi , Dominik Maier " -args: - - cores: - long: cores - about: "Spawn a client in each of the provided cores. Broker runs in the 0th core. 'all' to select all available cores. 'none' to run a client without binding to any core. eg: '1,2-4,6' selects the cores 1,2,3,4,6." - value_name: CORES - required: true - takes_value: true - - broker_port: - long: broker_port - about: "Choose the broker TCP port, default is 1337" - value_name: PORT - takes_value: true - - remote_broker_addr: - long: remote_broker_addr - about: "Specify a remote broker" - value_name: REMOTE - takes_value: true - - input: - long: input - about: "Set an initial corpus directory" - value_name: INPUT - multiple: true - takes_value: true - - output: - long: output - about: "Set the output directory, default is CWD" - value_name: OUTPUT - takes_value: true - - timeout: - long: timeout - about: "Set the execution timeout in milliseconds, default 10000" - value_name: TIMEOUT - takes_value: true - - tokens: - long: tokens - short: x - about: "Feed the fuzzer with an user-specified list of tokens (often called \"dictionary\")" - value_name: DICT - multiple: true - takes_value: true diff --git a/fuzzers/generic_inmemory/src/lib.rs b/fuzzers/generic_inmemory/src/lib.rs index 1000d9d5e0..cbc8d7c4f2 100644 --- a/fuzzers/generic_inmemory/src/lib.rs +++ b/fuzzers/generic_inmemory/src/lib.rs @@ -1,15 +1,15 @@ //! A libfuzzer-like fuzzer with llmp-multithreading support and restarts //! The `launcher` will spawn new processes for each cpu core. -use clap::{load_yaml, App}; use core::time::Duration; -use std::{env, path::PathBuf}; +use std::{env, net::SocketAddr, path::PathBuf}; +use structopt::StructOpt; use libafl::{ bolts::{ current_nanos, launcher::Launcher, - os::parse_core_bind_arg, + os::Cores, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::{tuple_list, Merge}, @@ -26,8 +26,10 @@ use libafl::{ generators::RandBytesGenerator, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, - mutators::scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, - mutators::token_mutations::{I2SRandReplace, Tokens}, + mutators::{ + scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, + token_mutations::{I2SRandReplace, Tokens}, + }, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, stages::{StdMutationalStage, TracingStage}, state::{HasCorpus, HasMetadata, StdState}, @@ -39,6 +41,84 @@ use libafl_targets::{ MAX_EDGES_NUM, }; +/// Parses a millseconds int into a [`Duration`], used for commandline arg parsing +fn timeout_from_millis_str(time: &str) -> Result { + Ok(Duration::from_millis(time.parse()?)) +} + +#[derive(Debug, StructOpt)] +#[structopt( + name = "generic_inmemory", + about = "A generic libfuzzer-like fuzzer with llmp-multithreading support", + author = "Andrea Fioraldi , Dominik Maier " +)] +struct Opt { + #[structopt( + short, + long, + parse(try_from_str = Cores::from_cmdline), + help = "Spawn a client in each of the provided cores. Broker runs in the 0th core. 'all' to select all available cores. 'none' to run a client without binding to any core. eg: '1,2-4,6' selects the cores 1,2,3,4,6.", + name = "CORES" + )] + cores: Cores, + + #[structopt( + short = "p", + long, + help = "Choose the broker TCP port, default is 1337", + name = "PORT" + )] + broker_port: u16, + + #[structopt( + parse(try_from_str), + short = "a", + long, + help = "Specify a remote broker", + name = "REMOTE" + )] + remote_broker_addr: Option, + + #[structopt( + parse(try_from_str), + short, + long, + help = "Set an initial corpus directory", + name = "INPUT" + )] + input: Vec, + + #[structopt( + short, + long, + parse(try_from_str), + help = "Set the output directory, default is ./out", + name = "OUTPUT", + default_value = "./out" + )] + output: PathBuf, + + #[structopt( + parse(try_from_str = timeout_from_millis_str), + short, + long, + help = "Set the exeucution timeout in milliseconds, default is 1000", + name = "TIMEOUT", + default_value = "1000" + )] + timeout: Duration, + + #[structopt( + parse(from_os_str), + short = "x", + long, + help = "Feed the fuzzer with an user-specified list of tokens (often called \"dictionary\"", + name = "TOKENS", + multiple = true + )] + tokens: Vec, +} + /// The main fn, `no_mangle` as it is a C symbol #[no_mangle] pub fn libafl_main() { @@ -48,34 +128,15 @@ pub fn libafl_main() { let workdir = env::current_dir().unwrap(); - let yaml = load_yaml!("clap-config.yaml"); - let matches = App::from(yaml).get_matches(); + let opt = Opt::from_args(); - let cores = parse_core_bind_arg(matches.value_of("cores").unwrap()) - .expect("No valid core count given!"); - let broker_port = matches - .value_of("broker_port") - .map(|s| s.parse().expect("Invalid broker port")) - .unwrap_or(1337); - let remote_broker_addr = matches - .value_of("remote_broker_addr") - .map(|s| s.parse().expect("Invalid broker address")); - let input_dirs: Vec = matches - .values_of("input") - .map(|v| v.map(PathBuf::from).collect()) - .unwrap_or_default(); - let output_dir = matches - .value_of("output") - .map(PathBuf::from) - .unwrap_or_else(|| workdir.clone()); - let token_files: Vec<&str> = matches - .values_of("tokens") - .map(|v| v.collect()) - .unwrap_or_default(); - let timeout_ms = matches - .value_of("timeout") - .map(|s| s.parse().expect("Invalid timeout")) - .unwrap_or(10000); + let cores = opt.cores; + let broker_port = opt.broker_port; + let remote_broker_addr = opt.remote_broker_addr; + let input_dirs = opt.input; + let output_dir = opt.output; + let token_files = opt.tokens; + let timeout_ms = opt.timeout; // let cmplog_enabled = matches.is_present("cmplog"); println!("Workdir: {:?}", workdir.to_string_lossy().to_string()); @@ -157,7 +218,7 @@ pub fn libafl_main() { &mut state, &mut mgr, )?, - Duration::from_millis(timeout_ms), + timeout_ms, ); // Secondary harness due to mut ownership diff --git a/fuzzers/libafl_atheris/src/lib.rs b/fuzzers/libafl_atheris/src/lib.rs index c80f006fe3..d2e8219f0e 100644 --- a/fuzzers/libafl_atheris/src/lib.rs +++ b/fuzzers/libafl_atheris/src/lib.rs @@ -15,7 +15,7 @@ use libafl::{ bolts::{ current_nanos, launcher::Launcher, - os::parse_core_bind_arg, + os::Cores, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::{tuple_list, Merge}, @@ -179,7 +179,7 @@ pub fn LLVMFuzzerRunDriver( env::current_dir().unwrap().to_string_lossy().to_string() ); - let cores = parse_core_bind_arg(matches.value_of("cores").unwrap()) + let cores = Cores::from_cmdline(matches.value_of("cores").unwrap()) .expect("No valid core count given!"); let broker_port = matches .value_of("broker_port") diff --git a/fuzzers/libfuzzer_libpng_ctx/Cargo.toml b/fuzzers/libfuzzer_libpng_ctx/Cargo.toml index 837048a85b..b589d6762b 100644 --- a/fuzzers/libfuzzer_libpng_ctx/Cargo.toml +++ b/fuzzers/libfuzzer_libpng_ctx/Cargo.toml @@ -24,7 +24,7 @@ libafl = { path = "../../libafl/", features = ["std", "anymap_debug", "derive", libafl_targets = { path = "../../libafl_targets/", features = ["libfuzzer"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } -clap = { version = "3.0.0-beta.2", features = ["yaml"] } +structopt = "0.3.25" [lib] name = "libfuzzer_libpng" diff --git a/fuzzers/libfuzzer_libpng_ctx/src/clap-config.yaml b/fuzzers/libfuzzer_libpng_ctx/src/clap-config.yaml deleted file mode 100644 index 387fe0fb54..0000000000 --- a/fuzzers/libfuzzer_libpng_ctx/src/clap-config.yaml +++ /dev/null @@ -1,12 +0,0 @@ -name: libfuzzer libpng -version: "0.1.0" -author: "Andrea Fioraldi , Dominik Maier " -about: A clone of libfuzzer using libafl for a libpng harness. -args: - - cores: - short: c - long: cores - about: "spawn a client in each of the provided cores. Broker runs in the 0th core. 'all' to select all available cores. 'none' to run a client without binding to any core. eg: '1,2-4,6' selects the cores 1,2,3,4,6." - value_name: CORES - required: true - takes_value: true diff --git a/fuzzers/libfuzzer_libpng_ctx/src/lib.rs b/fuzzers/libfuzzer_libpng_ctx/src/lib.rs index 68245ca092..f87417466c 100644 --- a/fuzzers/libfuzzer_libpng_ctx/src/lib.rs +++ b/fuzzers/libfuzzer_libpng_ctx/src/lib.rs @@ -3,15 +3,15 @@ //! In this example, you will see the use of the `launcher` feature. //! The `launcher` will spawn new processes for each cpu core. -use clap::{load_yaml, App}; use core::time::Duration; -use std::{env, path::PathBuf}; +use std::{env, net::SocketAddr, path::PathBuf}; +use structopt::StructOpt; use libafl::{ bolts::{ current_nanos, launcher::Launcher, - os::parse_core_bind_arg, + os::Cores, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::{tuple_list, Merge}, @@ -27,8 +27,10 @@ use libafl::{ fuzzer::{Fuzzer, StdFuzzer}, inputs::{BytesInput, HasTargetBytes}, monitors::MultiMonitor, - mutators::scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, - mutators::token_mutations::Tokens, + mutators::{ + scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, + token_mutations::Tokens, + }, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, stages::mutational::StdMutationalStage, state::{HasCorpus, HasMetadata, StdState}, @@ -37,19 +39,96 @@ use libafl::{ use libafl_targets::{edges_map_from_ptr, libfuzzer_initialize, libfuzzer_test_one_input}; +fn timeout_from_millis_str(time: &str) -> Result { + Ok(Duration::from_millis(time.parse()?)) +} + +#[derive(Debug, StructOpt)] +#[structopt( + name = "libfuzzer_libpng_ctx", + about = "A clone of libfuzzer using LibAFL for a libpng harness", + author = "Andrea Fioraldi , Dominik Maier " +)] +struct Opt { + #[structopt( + short, + long, + parse(try_from_str = Cores::from_cmdline), + help = "Spawn a client in each of the provided cores. Broker runs in the 0th core. 'all' to select all available cores. 'none' to run a client without binding to any core. eg: '1,2-4,6' selects the cores 1,2,3,4,6.", + name = "CORES" + )] + cores: Cores, + + #[structopt( + short = "p", + long, + help = "Choose the broker TCP port, default is 1337", + name = "PORT", + default_value = "1337" + )] + broker_port: u16, + + #[structopt( + parse(try_from_str), + short = "a", + long, + help = "Specify a remote broker", + name = "REMOTE" + )] + remote_broker_addr: Option, + + #[structopt( + parse(try_from_str), + short, + long, + help = "Set an initial corpus directory", + name = "INPUT" + )] + input: Vec, + + #[structopt( + short, + long, + parse(try_from_str), + help = "Set the output directory, default is ./out", + name = "OUTPUT", + default_value = "./out" + )] + output: PathBuf, + + #[structopt( + short, + long, + parse(try_from_str = timeout_from_millis_str), + help = "Set the exeucution timeout in milliseconds, default is 10000", + name = "TIMEOUT", + default_value = "10000", + )] + timeout: Duration, + /* + // The tokens are hardcoded in this example. + #[structopt( + parse(from_os_str), + short = "x", + long, + help = "Feed the fuzzer with an user-specified list of tokens (often called \"dictionary\"", + name = "TOKENS", + multiple = true + )] + tokens: Vec,*/ +} + /// The main fn, `no_mangle` as it is a C symbol #[no_mangle] pub fn libafl_main() { // Registry the metadata types used in this fuzzer // Needed only on no_std //RegistryBuilder::register::(); - let yaml = load_yaml!("clap-config.yaml"); - let matches = App::from(yaml).get_matches(); + let opt = Opt::from_args(); - let broker_port = 1337; + let broker_port = opt.broker_port; - let cores = parse_core_bind_arg(matches.value_of("cores").unwrap()) - .expect("No valid core count given!"); + let cores = opt.cores; println!( "Workdir: {:?}", @@ -61,9 +140,6 @@ pub fn libafl_main() { let monitor = MultiMonitor::new(|s| println!("{}", s)); let mut run_client = |state: Option>, mut restarting_mgr, _core_id| { - let corpus_dirs = &[PathBuf::from("./corpus")]; - let objective_dir = PathBuf::from("./crashes"); - // Create an observation channel using the coverage map let edges = edges_map_from_ptr(); let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("edges", edges)); @@ -95,7 +171,7 @@ pub fn libafl_main() { InMemoryCorpus::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(), + OnDiskCorpus::new(opt.output.clone()).unwrap(), // States of the feedbacks. // They are the data related to the feedbacks that you want to persist in the State. tuple_list!(feedback_state), @@ -143,7 +219,7 @@ pub fn libafl_main() { &mut restarting_mgr, )?, // 10 seconds timeout - Duration::new(10, 0), + opt.timeout, ); // The actual target run starts here. @@ -156,8 +232,8 @@ pub fn libafl_main() { // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs(&mut fuzzer, &mut executor, &mut restarting_mgr, corpus_dirs) - .unwrap_or_else(|_| panic!("Failed to load initial corpus at {:?}", corpus_dirs)); + .load_initial_inputs(&mut fuzzer, &mut executor, &mut restarting_mgr, &opt.input) + .unwrap_or_else(|_| panic!("Failed to load initial corpus at {:?}", &opt.input)); println!("We imported {} inputs from disk.", state.corpus().count()); } @@ -172,6 +248,7 @@ pub fn libafl_main() { .run_client(&mut run_client) .cores(&cores) .broker_port(broker_port) + .remote_broker_addr(opt.remote_broker_addr) .stdout_file(Some("/dev/null")) .build() .launch() diff --git a/fuzzers/libfuzzer_libpng_launcher/Cargo.toml b/fuzzers/libfuzzer_libpng_launcher/Cargo.toml index fe970cda07..b225e962c5 100644 --- a/fuzzers/libfuzzer_libpng_launcher/Cargo.toml +++ b/fuzzers/libfuzzer_libpng_launcher/Cargo.toml @@ -24,7 +24,7 @@ libafl = { path = "../../libafl/", features = ["std", "anymap_debug", "derive", libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } -clap = { version = "3.0.0-beta.2", features = ["yaml"] } +structopt = "0.3.25" [lib] name = "libfuzzer_libpng" diff --git a/fuzzers/libfuzzer_libpng_launcher/src/clap-config.yaml b/fuzzers/libfuzzer_libpng_launcher/src/clap-config.yaml deleted file mode 100644 index 387fe0fb54..0000000000 --- a/fuzzers/libfuzzer_libpng_launcher/src/clap-config.yaml +++ /dev/null @@ -1,12 +0,0 @@ -name: libfuzzer libpng -version: "0.1.0" -author: "Andrea Fioraldi , Dominik Maier " -about: A clone of libfuzzer using libafl for a libpng harness. -args: - - cores: - short: c - long: cores - about: "spawn a client in each of the provided cores. Broker runs in the 0th core. 'all' to select all available cores. 'none' to run a client without binding to any core. eg: '1,2-4,6' selects the cores 1,2,3,4,6." - value_name: CORES - required: true - takes_value: true diff --git a/fuzzers/libfuzzer_libpng_launcher/src/lib.rs b/fuzzers/libfuzzer_libpng_launcher/src/lib.rs index 4ea130b517..5d63d2c4e4 100644 --- a/fuzzers/libfuzzer_libpng_launcher/src/lib.rs +++ b/fuzzers/libfuzzer_libpng_launcher/src/lib.rs @@ -3,15 +3,15 @@ //! In this example, you will see the use of the `launcher` feature. //! The `launcher` will spawn new processes for each cpu core. -use clap::{load_yaml, App}; use core::time::Duration; -use std::{env, path::PathBuf}; +use std::{env, net::SocketAddr, path::PathBuf}; +use structopt::StructOpt; use libafl::{ bolts::{ current_nanos, launcher::Launcher, - os::parse_core_bind_arg, + os::Cores, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::{tuple_list, Merge}, @@ -37,19 +37,98 @@ use libafl::{ use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_NUM}; +/// Parse a millis string to a [`Duration`]. Used for arg parsing. +fn timeout_from_millis_str(time: &str) -> Result { + Ok(Duration::from_millis(time.parse()?)) +} + +/// The commandline args this fuzzer accepts +#[derive(Debug, StructOpt)] +#[structopt( + name = "libfuzzer_libpng_launcher", + about = "A libfuzzer-like fuzzer for libpng with llmp-multithreading support and a launcher", + author = "Andrea Fioraldi , Dominik Maier " +)] +struct Opt { + #[structopt( + short, + long, + parse(try_from_str = Cores::from_cmdline), + help = "Spawn a client in each of the provided cores. Broker runs in the 0th core. 'all' to select all available cores. 'none' to run a client without binding to any core. eg: '1,2-4,6' selects the cores 1,2,3,4,6.", + name = "CORES" + )] + cores: Cores, + + #[structopt( + short = "p", + long, + help = "Choose the broker TCP port, default is 1337", + name = "PORT", + default_value = "1337" + )] + broker_port: u16, + + #[structopt( + parse(try_from_str), + short = "a", + long, + help = "Specify a remote broker", + name = "REMOTE" + )] + remote_broker_addr: Option, + + #[structopt( + parse(try_from_str), + short, + long, + help = "Set an initial corpus directory", + name = "INPUT" + )] + input: Vec, + + #[structopt( + short, + long, + parse(try_from_str), + help = "Set the output directory, default is ./out", + name = "OUTPUT", + default_value = "./out" + )] + output: PathBuf, + + #[structopt( + parse(try_from_str = timeout_from_millis_str), + short, + long, + help = "Set the exeucution timeout in milliseconds, default is 10000", + name = "TIMEOUT", + default_value = "10000" + )] + timeout: Duration, + /* + /// This fuzzer has hard-coded tokens + #[structopt( + parse(from_os_str), + short = "x", + long, + help = "Feed the fuzzer with an user-specified list of tokens (often called \"dictionary\"", + name = "TOKENS", + multiple = true + )] + tokens: Vec, + */ +} + /// The main fn, `no_mangle` as it is a C symbol #[no_mangle] pub fn libafl_main() { // Registry the metadata types used in this fuzzer // Needed only on no_std //RegistryBuilder::register::(); - let yaml = load_yaml!("clap-config.yaml"); - let matches = App::from(yaml).get_matches(); + let opt = Opt::from_args(); - let broker_port = 1337; - - let cores = parse_core_bind_arg(matches.value_of("cores").unwrap()) - .expect("No valid core count given!"); + let broker_port = opt.broker_port; + let cores = opt.cores; println!( "Workdir: {:?}", @@ -61,9 +140,6 @@ pub fn libafl_main() { let monitor = MultiMonitor::new(|s| println!("{}", s)); let mut run_client = |state: Option>, mut restarting_mgr, _core_id| { - let corpus_dirs = &[PathBuf::from("./corpus")]; - let objective_dir = PathBuf::from("./crashes"); - // Create an observation channel using the coverage map let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] }; let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("edges", edges)); @@ -95,7 +171,7 @@ pub fn libafl_main() { InMemoryCorpus::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(), + OnDiskCorpus::new(&opt.output).unwrap(), // States of the feedbacks. // They are the data related to the feedbacks that you want to persist in the State. tuple_list!(feedback_state), @@ -143,7 +219,7 @@ pub fn libafl_main() { &mut restarting_mgr, )?, // 10 seconds timeout - Duration::new(10, 0), + opt.timeout, ); // The actual target run starts here. @@ -156,8 +232,8 @@ pub fn libafl_main() { // In case the corpus is empty (on first run), reset if state.corpus().count() < 1 { state - .load_initial_inputs(&mut fuzzer, &mut executor, &mut restarting_mgr, corpus_dirs) - .unwrap_or_else(|_| panic!("Failed to load initial corpus at {:?}", corpus_dirs)); + .load_initial_inputs(&mut fuzzer, &mut executor, &mut restarting_mgr, &opt.input) + .unwrap_or_else(|_| panic!("Failed to load initial corpus at {:?}", &opt.input)); println!("We imported {} inputs from disk.", state.corpus().count()); } @@ -172,6 +248,7 @@ pub fn libafl_main() { .run_client(&mut run_client) .cores(&cores) .broker_port(broker_port) + .remote_broker_addr(opt.remote_broker_addr) .stdout_file(Some("/dev/null")) .build() .launch() diff --git a/libafl/src/bolts/launcher.rs b/libafl/src/bolts/launcher.rs index c634803f63..134e06daa7 100644 --- a/libafl/src/bolts/launcher.rs +++ b/libafl/src/bolts/launcher.rs @@ -16,7 +16,7 @@ use crate::bolts::os::startable_self; use crate::bolts::os::{dup2, fork, ForkResult}; #[cfg(feature = "std")] use crate::{ - bolts::shmem::ShMemProvider, + bolts::{os::Cores, shmem::ShMemProvider}, events::{EventConfig, LlmpRestartingEventManager, ManagerKind, RestartingMgr}, inputs::Input, monitors::Monitor, @@ -67,7 +67,7 @@ where #[builder(default = 1337_u16)] broker_port: u16, /// The list of cores to run on - cores: &'a [usize], + cores: &'a Cores, /// A file name to write all client output to #[builder(default = None)] stdout_file: Option<&'a str>, @@ -114,7 +114,7 @@ where // Spawn clients let mut index = 0_u64; for (id, bind_to) in core_ids.iter().enumerate().take(num_cores) { - if self.cores.iter().any(|&x| x == id) { + if self.cores.ids.iter().any(|&x| x == id.into()) { index += 1; self.shmem_provider.pre_fork()?; match unsafe { fork() }? { @@ -237,7 +237,7 @@ where //spawn clients for (id, _) in core_ids.iter().enumerate().take(num_cores) { - if self.cores.iter().any(|&x| x == id) { + if self.cores.ids.iter().any(|&x| x == id.into()) { let stdio = if self.stdout_file.is_some() { Stdio::inherit() } else { diff --git a/libafl/src/bolts/llmp.rs b/libafl/src/bolts/llmp.rs index 095b483d9b..321a4af028 100644 --- a/libafl/src/bolts/llmp.rs +++ b/libafl/src/bolts/llmp.rs @@ -1435,7 +1435,7 @@ where let last_msg = self.last_msg_recvd; if !last_msg.is_null() { assert!( - !((*last_msg).tag == LLMP_TAG_END_OF_PAGE && !llmp_msg_in_page(page, last_msg)), + (*last_msg).tag != LLMP_TAG_END_OF_PAGE || llmp_msg_in_page(page, last_msg), "BUG: full page passed to await_message_blocking or reset failed" ); diff --git a/libafl/src/bolts/os/mod.rs b/libafl/src/bolts/os/mod.rs index a26e0175bd..9cd380f7b9 100644 --- a/libafl/src/bolts/os/mod.rs +++ b/libafl/src/bolts/os/mod.rs @@ -1,4 +1,10 @@ //! Operating System specific abstractions +//! + +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; #[cfg(any(unix, all(windows, feature = "std")))] use crate::Error; @@ -94,37 +100,147 @@ pub fn dup2(fd: i32, device: i32) -> Result<(), Error> { } } +/// Core ID +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CoreId { + pub id: usize, +} + +#[cfg(feature = "std")] +impl From<&CoreId> for core_affinity::CoreId { + fn from(core_id: &CoreId) -> Self { + core_affinity::CoreId { id: core_id.id } + } +} + +#[cfg(feature = "std")] +impl From for core_affinity::CoreId { + fn from(core_id: CoreId) -> Self { + core_affinity::CoreId { id: core_id.id } + } +} + +#[cfg(feature = "std")] +impl CoreId { + /// Set the affinity of the current process to this [`CoreId`] + pub fn set_affinity(&self) { + core_affinity::set_for_current(self.into()); + } +} + +impl From for CoreId { + fn from(id: usize) -> Self { + CoreId { id } + } +} + +#[cfg(feature = "std")] +impl From<&core_affinity::CoreId> for CoreId { + fn from(core_id: &core_affinity::CoreId) -> Self { + CoreId { id: core_id.id } + } +} + +#[cfg(feature = "std")] +impl From for CoreId { + fn from(core_id: core_affinity::CoreId) -> Self { + CoreId { id: core_id.id } + } +} + +/// A list of [`CoreId`] to use for fuzzing +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Cores { + /// The original commandline used during parsing + pub cmdline: String, + + /// Vec of core ids + pub ids: Vec, +} + +#[cfg(feature = "std")] +impl Cores { + /// Pick all cores + pub fn all() -> Result { + Self::from_cmdline("all") + } + + /// Parses core binding args from user input + /// Returns a Vec of CPU IDs. + /// `./fuzzer --cores 1,2-4,6` -> clients run in cores 1,2,3,4,6 + /// ` ./fuzzer --cores all` -> one client runs on each available core + pub fn from_cmdline(args: &str) -> Result { + let mut cores: Vec = vec![]; + + // ./fuzzer --cores all -> one client runs in each available core + if args == "all" { + let num_cores = if let Some(cores) = core_affinity::get_core_ids() { + cores.len() + } else { + return Err(Error::IllegalState( + "Could not read core count from core_affinity".to_string(), + )); + }; + for x in 0..num_cores { + cores.push(x.into()); + } + } else { + let core_args: Vec<&str> = args.split(',').collect(); + + // ./fuzzer --cores 1,2-4,6 -> clients run in cores 1,2,3,4,6 + for csv in core_args { + let core_range: Vec<&str> = csv.split('-').collect(); + if core_range.len() == 1 { + cores.push(core_range[0].parse::()?.into()); + } else if core_range.len() == 2 { + for x in core_range[0].parse::()?..=(core_range[1].parse::()?) { + cores.push(x.into()); + } + } + } + } + + if cores.is_empty() { + return Err(Error::IllegalArgument(format!( + "No cores specified! parsed: {}", + args + ))); + } + + Ok(Self { + cmdline: args.to_string(), + ids: cores, + }) + } +} + +impl From<&[usize]> for Cores { + fn from(cores: &[usize]) -> Self { + let cmdline = cores + .iter() + .map(ToString::to_string) + .collect::>() + .join(","); + let ids = cores.iter().map(|x| (*x).into()).collect(); + Self { cmdline, ids } + } +} + +impl From> for Cores { + fn from(cores: Vec) -> Self { + Self::from(cores.as_slice()) + } +} + /// Parses core binding args from user input /// Returns a Vec of CPU IDs. /// `./fuzzer --cores 1,2-4,6` -> clients run in cores 1,2,3,4,6 /// ` ./fuzzer --cores all` -> one client runs on each available core #[must_use] #[cfg(feature = "std")] +#[deprecated(since = "0.7.1", note = "Use Cores::from_cmdline instead")] pub fn parse_core_bind_arg(args: &str) -> Option> { - let mut cores: Vec = vec![]; - if args == "all" { - let num_cores = core_affinity::get_core_ids().unwrap().len(); - for x in 0..num_cores { - cores.push(x); - } - } else { - let core_args: Vec<&str> = args.split(',').collect(); - - // ./fuzzer --cores 1,2-4,6 -> clients run in cores 1,2,3,4,6 - // ./fuzzer --cores all -> one client runs in each available core - for csv in core_args { - let core_range: Vec<&str> = csv.split('-').collect(); - if core_range.len() == 1 { - cores.push(core_range[0].parse::().unwrap()); - } else if core_range.len() == 2 { - for x in core_range[0].parse::().unwrap() - ..=(core_range[1].parse::().unwrap()) - { - cores.push(x); - } - } - } - } - - Some(cores) + Cores::from_cmdline(args) + .ok() + .map(|cores| cores.ids.iter().map(|x| x.id).collect()) } diff --git a/libafl/src/bolts/os/unix_shmem_server.rs b/libafl/src/bolts/os/unix_shmem_server.rs index 23d3b3dc04..8301809a5d 100644 --- a/libafl/src/bolts/os/unix_shmem_server.rs +++ b/libafl/src/bolts/os/unix_shmem_server.rs @@ -640,7 +640,7 @@ where continue; } }; - let copied_poll_fds: Vec = poll_fds.iter().copied().collect(); + let copied_poll_fds: Vec = poll_fds.clone(); for poll_fd in copied_poll_fds { let revents = poll_fd.revents().expect("revents should not be None"); let raw_polled_fd = diff --git a/libafl/src/corpus/ondisk.rs b/libafl/src/corpus/ondisk.rs index 65e560c061..f4b373e298 100644 --- a/libafl/src/corpus/ondisk.rs +++ b/libafl/src/corpus/ondisk.rs @@ -3,7 +3,10 @@ use alloc::vec::Vec; use core::{cell::RefCell, time::Duration}; use serde::{Deserialize, Serialize}; -use std::{fs::OpenOptions, path::PathBuf}; +use std::{ + fs::OpenOptions, + path::{Path, PathBuf}, +}; #[cfg(feature = "std")] use std::{fs, fs::File, io::Write}; @@ -171,14 +174,20 @@ where { /// Creates the [`OnDiskCorpus`]. /// Will error, if [`std::fs::create_dir_all()`] failed for `dir_path`. - pub fn new(dir_path: PathBuf) -> Result { - fs::create_dir_all(&dir_path)?; - Ok(Self { - entries: vec![], - current: None, - dir_path, - meta_format: None, - }) + pub fn new

(dir_path: P) -> Result + where + P: AsRef, + { + fn new(dir_path: PathBuf) -> Result, Error> { + fs::create_dir_all(&dir_path)?; + Ok(OnDiskCorpus { + entries: vec![], + current: None, + dir_path, + meta_format: None, + }) + } + new(dir_path.as_ref().to_path_buf()) } /// Creates the [`OnDiskCorpus`] specifying the type of `Metadata` to be saved to disk. diff --git a/libafl/src/executors/timeout.rs b/libafl/src/executors/timeout.rs index 66ce1032fa..b8be61b9e1 100644 --- a/libafl/src/executors/timeout.rs +++ b/libafl/src/executors/timeout.rs @@ -61,13 +61,18 @@ const ITIMER_REAL: c_int = 0; /// Reset and remove the timeout #[cfg(unix)] -pub unsafe fn unix_remove_timeout() { - let mut itimerval_zero: Itimerval = zeroed(); - setitimer(ITIMER_REAL, &mut itimerval_zero, null_mut()); +pub(crate) fn unix_remove_timeout() { + unsafe { + let mut itimerval_zero: Itimerval = zeroed(); + setitimer(ITIMER_REAL, &mut itimerval_zero, null_mut()); + } } +/// Deletes this timer queue +/// # Safety +/// Will dereference the given `tp_timer` pointer, unchecked. #[cfg(all(windows, feature = "std"))] -pub unsafe fn windows_delete_timer_queue(tp_timer: *mut TP_TIMER) { +pub(crate) unsafe fn windows_delete_timer_queue(tp_timer: *mut TP_TIMER) { CloseThreadpoolTimer(tp_timer); } diff --git a/libafl_cc/Cargo.toml b/libafl_cc/Cargo.toml index a5bb7cebeb..b2f52737a7 100644 --- a/libafl_cc/Cargo.toml +++ b/libafl_cc/Cargo.toml @@ -14,6 +14,7 @@ edition = "2021" [build-dependencies] cc = { version = "1.0", features = ["parallel"] } +which = "4.2.2" [target.'cfg(target_vendor = "apple")'.build-dependencies] glob = "0.3" diff --git a/libafl_cc/build.rs b/libafl_cc/build.rs index a9844125d2..20ff3e79ef 100644 --- a/libafl_cc/build.rs +++ b/libafl_cc/build.rs @@ -1,11 +1,20 @@ -use std::{env, fs::File, io::Write, path::Path, process::Command, str}; - #[cfg(target_vendor = "apple")] use glob::glob; - #[cfg(target_vendor = "apple")] use std::path::PathBuf; +use std::{env, fs::File, io::Write, path::Path, process::Command, str}; +#[cfg(not(target_vendor = "apple"))] +use which::which; +/// The max version of `LLVM` we're looking for +#[cfg(not(target_vendor = "apple"))] +const LLVM_VERSION_MAX: u32 = 33; + +/// The min version of `LLVM` we're looking for +#[cfg(not(target_vendor = "apple"))] +const LLVM_VERSION_MIN: u32 = 6; + +/// Get the extension for a shared object fn dll_extension<'a>() -> &'a str { match env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() { "windwos" => "dll", @@ -53,6 +62,13 @@ fn find_llvm_config() -> String { } } #[cfg(not(target_vendor = "apple"))] + for version in (LLVM_VERSION_MIN..=LLVM_VERSION_MAX).rev() { + let llvm_config_name = format!("llvm-config-{}", version); + if which(&llvm_config_name).is_ok() { + return llvm_config_name; + } + } + #[cfg(not(target_vendor = "apple"))] "llvm-config".to_string() }) } @@ -62,6 +78,9 @@ fn main() { let out_dir = Path::new(&out_dir); let src_dir = Path::new("src"); + println!("cargo:rerun-if-env-changed=LLVM_CONFIG"); + println!("cargo:rerun-if-env-changed=LIBAFL_EDGES_MAP_SIZE"); + let mut custom_flags = vec![]; let dest_path = Path::new(&out_dir).join("clang_constants.rs"); @@ -70,7 +89,6 @@ fn main() { let edges_map_size: usize = option_env!("LIBAFL_EDGES_MAP_SIZE") .map_or(Ok(65536), str::parse) .expect("Could not parse LIBAFL_EDGES_MAP_SIZE"); - println!("cargo:rerun-if-env-changed=LIBAFL_EDGES_MAP_SIZE"); custom_flags.push(format!("-DLIBAFL_EDGES_MAP_SIZE={}", edges_map_size)); let llvm_config = find_llvm_config(); @@ -154,8 +172,9 @@ pub const CLANGXX_PATH: &str = \"clang++\"; .expect("Could not write file"); println!( - "cargo:warning=Failed to locate the LLVM path using {}, we will not build LLVM passes", - llvm_config + "cargo:warning=Failed to locate the LLVM path using {}, we will not build LLVM passes + (if you need them, set point the LLVM_CONFIG env to a recent llvm-config, or make sure {} is available)", + llvm_config, llvm_config ); } diff --git a/libafl_frida/src/executor.rs b/libafl_frida/src/executor.rs index 0eeb2a62ca..cdf601e80f 100644 --- a/libafl_frida/src/executor.rs +++ b/libafl_frida/src/executor.rs @@ -4,9 +4,12 @@ use std::{ffi::c_void, marker::PhantomData}; use frida_gum::{ stalker::{NoneEventSink, Stalker}, - Gum, MemoryRange, NativePointer, + Gum, NativePointer, }; +#[cfg(all(not(debug_assertions), target_arch = "x86_64"))] +use frida_gum::MemoryRange; + use libafl::{ executors::{Executor, ExitKind, HasObservers, InProcessExecutor}, inputs::{HasTargetBytes, Input}, @@ -108,6 +111,9 @@ where OT: ObserversTuple, { pub fn new(gum: &'a Gum, base: InProcessExecutor<'a, H, I, OT, S>, helper: &'c mut FH) -> Self { + #[cfg(not(all(not(debug_assertions), target_arch = "x86_64")))] + let stalker = Stalker::new(gum); + #[cfg(all(not(debug_assertions), target_arch = "x86_64"))] let mut stalker = Stalker::new(gum); #[cfg(all(not(debug_assertions), target_arch = "x86_64"))] diff --git a/libafl_frida/src/helper.rs b/libafl_frida/src/helper.rs index 8a60d37d8a..fa4805fc19 100644 --- a/libafl_frida/src/helper.rs +++ b/libafl_frida/src/helper.rs @@ -64,6 +64,7 @@ enum CmplogOperandType { Mem(capstone::RegId, capstone::RegId, i32, u32), } +#[cfg(all(feature = "cmplog", target_arch = "aarch64"))] enum SpecialCmpLogCase { Tbz, Tbnz, diff --git a/libafl_frida/src/lib.rs b/libafl_frida/src/lib.rs index f2fdfe3116..6973e7f8c0 100644 --- a/libafl_frida/src/lib.rs +++ b/libafl_frida/src/lib.rs @@ -26,7 +26,8 @@ pub mod helper; pub mod executor; // for parsing asan and cmplog cores -use libafl::bolts::os::parse_core_bind_arg; +use libafl::bolts::os::{CoreId, Cores}; + // for getting current core_id use core_affinity::get_core_ids; @@ -85,7 +86,7 @@ impl FridaOptions { options.asan_max_allocation_panics = value.parse().unwrap(); } "asan-cores" => { - asan_cores = parse_core_bind_arg(value); + asan_cores = Cores::from_cmdline(value).ok(); } "instrument-suppress-locations" => { options.instrument_suppress_locations = Some( @@ -132,7 +133,7 @@ impl FridaOptions { } } "cmplog-cores" => { - cmplog_cores = parse_core_bind_arg(value); + cmplog_cores = Cores::from_cmdline(value).ok(); } _ => { panic!("unknown FRIDA option: '{}'", option); @@ -148,8 +149,8 @@ impl FridaOptions { 1, "Client should only be bound to a single core" ); - let core_id = core_ids[0].id; - options.enable_asan = asan_cores.contains(&core_id); + let core_id: CoreId = core_ids[0].into(); + options.enable_asan = asan_cores.ids.contains(&core_id); } } if options.enable_cmplog { @@ -160,8 +161,8 @@ impl FridaOptions { 1, "Client should only be bound to a single core" ); - let core_id = core_ids[0].id; - options.enable_cmplog = cmplog_cores.contains(&core_id); + let core_id = core_ids[0].into(); + options.enable_cmplog = cmplog_cores.ids.contains(&core_id); } } } diff --git a/libafl_sugar/src/forkserver.rs b/libafl_sugar/src/forkserver.rs index eed5aff118..3abbf1dc8e 100644 --- a/libafl_sugar/src/forkserver.rs +++ b/libafl_sugar/src/forkserver.rs @@ -6,6 +6,7 @@ use libafl::{ bolts::{ current_nanos, launcher::Launcher, + os::Cores, rands::StdRand, shmem::{ShMem, ShMemProvider, StdShMemProvider}, tuples::{tuple_list, Merge}, @@ -54,7 +55,7 @@ pub struct ForkserverBytesCoverageSugar<'a, const MAP_SIZE: usize> { #[builder(default = 1337_u16)] broker_port: u16, /// The list of cores to run on - cores: &'a [usize], + cores: &'a Cores, /// The `ip:port` address of another broker to connect our new broker to for multi-machine /// clusters. #[builder(default = None, setter(strip_option))] @@ -252,6 +253,7 @@ impl<'a, const MAP_SIZE: usize> ForkserverBytesCoverageSugar<'a, MAP_SIZE> { #[cfg(feature = "python")] pub mod pybind { use crate::forkserver; + use libafl::bolts::os::Cores; use pyo3::prelude::*; use std::path::PathBuf; @@ -260,7 +262,7 @@ pub mod pybind { input_dirs: Vec, output_dir: PathBuf, broker_port: u16, - cores: Vec, + cores: Cores, } #[pymethods] @@ -276,7 +278,7 @@ pub mod pybind { input_dirs, output_dir, broker_port, - cores, + cores: cores.into(), } } diff --git a/libafl_sugar/src/inmemory.rs b/libafl_sugar/src/inmemory.rs index 986f5dea83..041129524e 100644 --- a/libafl_sugar/src/inmemory.rs +++ b/libafl_sugar/src/inmemory.rs @@ -6,6 +6,7 @@ use libafl::{ bolts::{ current_nanos, launcher::Launcher, + os::Cores, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::{tuple_list, Merge}, @@ -58,7 +59,7 @@ where #[builder(default = 1337_u16)] broker_port: u16, /// The list of cores to run on - cores: &'a [usize], + cores: &'a Cores, /// The `ip:port` address of another broker to connect our new broker to for multi-machine /// clusters. #[builder(default = None, setter(strip_option))] @@ -272,6 +273,7 @@ where #[cfg(feature = "python")] pub mod pybind { use crate::inmemory; + use libafl::bolts::os::Cores; use pyo3::prelude::*; use pyo3::types::PyBytes; use std::path::PathBuf; @@ -281,7 +283,7 @@ pub mod pybind { input_dirs: Vec, output_dir: PathBuf, broker_port: u16, - cores: Vec, + cores: Cores, } #[pymethods] @@ -297,7 +299,7 @@ pub mod pybind { input_dirs, output_dir, broker_port, - cores, + cores: cores.into(), } } diff --git a/libafl_sugar/src/qemu.rs b/libafl_sugar/src/qemu.rs index fc8fd06fd8..1da4d27ced 100644 --- a/libafl_sugar/src/qemu.rs +++ b/libafl_sugar/src/qemu.rs @@ -6,6 +6,7 @@ use libafl::{ bolts::{ current_nanos, launcher::Launcher, + os::Cores, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::{tuple_list, Merge}, @@ -59,7 +60,7 @@ where #[builder(default = 1337_u16)] broker_port: u16, /// The list of cores to run on - cores: &'a [usize], + cores: &'a Cores, /// The `ip:port` address of another broker to connect our new broker to for multi-machine /// clusters. #[builder(default = None, setter(strip_option))] @@ -330,6 +331,7 @@ where #[cfg(feature = "python")] pub mod pybind { use crate::qemu; + use libafl::bolts::os::Cores; use pyo3::prelude::*; use pyo3::types::PyBytes; use std::path::PathBuf; @@ -339,7 +341,7 @@ pub mod pybind { input_dirs: Vec, output_dir: PathBuf, broker_port: u16, - cores: Vec, + cores: Cores, } #[pymethods] @@ -355,7 +357,7 @@ pub mod pybind { input_dirs, output_dir, broker_port, - cores, + cores: cores.into(), } } diff --git a/utils/deexit/src/lib.rs b/utils/deexit/src/lib.rs index dd1125a3a0..b153bd79e2 100644 --- a/utils/deexit/src/lib.rs +++ b/utils/deexit/src/lib.rs @@ -8,7 +8,7 @@ extern "C" { /// Hooked `exit` function #[no_mangle] -pub fn exit(status: i32) { +pub extern "C" fn exit(status: i32) { println!("DeExit: The target called exit with status code {}", status); unsafe { abort(); diff --git a/utils/gramatron/construct_automata/Cargo.toml b/utils/gramatron/construct_automata/Cargo.toml index 4e551a0ff6..3b54060e72 100644 --- a/utils/gramatron/construct_automata/Cargo.toml +++ b/utils/gramatron/construct_automata/Cargo.toml @@ -11,4 +11,4 @@ regex = "1" postcard = "0.7" lazy_static = "1.4.0" libafl = { path = "../../../libafl" } -clap = { version = "3.0.0-beta.2", features = ["yaml"] } +structopt = "0.3.25" diff --git a/utils/gramatron/construct_automata/src/main.rs b/utils/gramatron/construct_automata/src/main.rs index 5e8c597ea5..985d14fbe2 100644 --- a/utils/gramatron/construct_automata/src/main.rs +++ b/utils/gramatron/construct_automata/src/main.rs @@ -1,17 +1,54 @@ -use clap::{load_yaml, App}; use lazy_static::lazy_static; use regex::Regex; use serde_json::Value; -use std::collections::{HashMap, HashSet, VecDeque}; use std::{ + collections::{HashMap, HashSet, VecDeque}, fs, io::{BufReader, Write}, path::Path, + path::PathBuf, rc::Rc, }; +use structopt::StructOpt; use libafl::generators::gramatron::{Automaton, Trigger}; +#[derive(Debug, StructOpt)] +#[structopt( + name = "construct_automata", + about = "Generate a serialized Automaton using a json GNF grammar", + author = "Andrea Fioraldi " +)] +struct Opt { + #[structopt( + parse(try_from_str), + short, + long = "grammar-file", + name = "GRAMMAR", + help = "The grammar to use during fuzzing" + )] + grammar: PathBuf, + + #[structopt( + parse(try_from_str), + short, + long, + name = "LIMIT", + help = "The max stack size after which a generated input is abandoned", + default_value = "0" + )] + limit: usize, + + #[structopt( + parse(try_from_str), + short, + long, + help = "Set the output file", + name = "OUTPUT" + )] + output: PathBuf, +} + fn read_grammar_from_file>(path: P) -> Value { let file = fs::File::open(path).unwrap(); let reader = BufReader::new(file); @@ -268,12 +305,11 @@ fn postprocess(pda: &[Transition], stack_limit: usize) -> Automaton { } fn main() { - let yaml = load_yaml!("clap-config.yaml"); - let matches = App::from(yaml).get_matches(); + let opt = Opt::from_args(); - let grammar_file = matches.value_of("grammar").unwrap(); - let output_file = matches.value_of("output").unwrap(); - let stack_limit = matches.value_of_t::("limit").unwrap_or(0); + let grammar_file = opt.grammar; + let output_file = opt.output; + let stack_limit = opt.limit; let mut worklist = VecDeque::new(); let mut state_count = 1;