Use Structopt instead of yaml for example fuzzers, introduce Cores API (#420)
* reworked generic_inmemory to structopt * moved core parsing to a struct * added Cores * added structopt to libpng_ctx * improved libafl, added structopt to libpng launcher * fix deexit ub * move more to structopt * improve llvm-config detection * move construct_automata to structopt * clippy, fixes, ... * no_std * clippy * frida core parsing * fixed no-fork cores * updated clap * added missing import * missing borrow * reworked frida to structopt * fixed build * using Cores api for atheris Co-authored-by: Dominik Maier <d.maier@avm.de>
This commit is contained in:
parent
b4c2551544
commit
217a7dee1d
@ -17,4 +17,4 @@ opt-level = 3
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/" }
|
||||
clap = { version = "3.0.0-beta.2", features = ["default"] }
|
||||
clap = { version = "3.0.0-rc.4", features = ["default"] }
|
@ -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"
|
||||
|
@ -47,8 +47,7 @@ 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() {
|
||||
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");
|
||||
@ -60,9 +59,7 @@ fn main() {
|
||||
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() {
|
||||
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");
|
||||
@ -75,7 +72,6 @@ fn main() {
|
||||
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");
|
||||
} else {
|
||||
@ -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",
|
||||
|
@ -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<Duration, Error> {
|
||||
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 <github@shmarya.net>,
|
||||
Dongjia Zhang <toka@aflplus.plus>, Andrea Fioraldi <andreafioraldi@gmail.com>, Dominik Maier <domenukk@gmail.com>"
|
||||
)]
|
||||
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<SocketAddr>,
|
||||
|
||||
#[structopt(
|
||||
parse(try_from_str),
|
||||
short,
|
||||
long,
|
||||
help = "Set an initial corpus directory",
|
||||
name = "INPUT"
|
||||
)]
|
||||
input: Vec<PathBuf>,
|
||||
|
||||
#[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<PathBuf>,
|
||||
|
||||
#[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<String>,
|
||||
|
||||
#[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::<Tokens>();
|
||||
|
||||
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::<Vec<_>>(),
|
||||
&opt.harness,
|
||||
&opt.symbol,
|
||||
&opt.modules_to_instrument.split(':').collect::<Vec<_>>(),
|
||||
//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<SocketAddr>,
|
||||
configuration: String,
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -1,42 +0,0 @@
|
||||
name: generic_inmemory
|
||||
author: "Andrea Fioraldi <andreafioraldi@gmail.com>, Dominik Maier <domenukk@gmail.com>"
|
||||
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
|
@ -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<Duration, Error> {
|
||||
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 <andreafioraldi@gmail.com>, Dominik Maier <domenukk@gmail.com>"
|
||||
)]
|
||||
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<SocketAddr>,
|
||||
|
||||
#[structopt(
|
||||
parse(try_from_str),
|
||||
short,
|
||||
long,
|
||||
help = "Set an initial corpus directory",
|
||||
name = "INPUT"
|
||||
)]
|
||||
input: Vec<PathBuf>,
|
||||
|
||||
#[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<PathBuf>,
|
||||
}
|
||||
|
||||
/// 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<PathBuf> = 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
|
||||
|
@ -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")
|
||||
|
@ -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"
|
||||
|
@ -1,12 +0,0 @@
|
||||
name: libfuzzer libpng
|
||||
version: "0.1.0"
|
||||
author: "Andrea Fioraldi <andreafioraldi@gmail.com>, Dominik Maier <domenukk@gmail.com>"
|
||||
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
|
@ -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<Duration, Error> {
|
||||
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 <andreafioraldi@gmail.com>, Dominik Maier <domenukk@gmail.com>"
|
||||
)]
|
||||
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<SocketAddr>,
|
||||
|
||||
#[structopt(
|
||||
parse(try_from_str),
|
||||
short,
|
||||
long,
|
||||
help = "Set an initial corpus directory",
|
||||
name = "INPUT"
|
||||
)]
|
||||
input: Vec<PathBuf>,
|
||||
|
||||
#[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<PathBuf>,*/
|
||||
}
|
||||
|
||||
/// 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::<Tokens>();
|
||||
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<StdState<_, _, _, _, _>>, 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()
|
||||
|
@ -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"
|
||||
|
@ -1,12 +0,0 @@
|
||||
name: libfuzzer libpng
|
||||
version: "0.1.0"
|
||||
author: "Andrea Fioraldi <andreafioraldi@gmail.com>, Dominik Maier <domenukk@gmail.com>"
|
||||
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
|
@ -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<Duration, Error> {
|
||||
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 <andreafioraldi@gmail.com>, Dominik Maier <domenukk@gmail.com>"
|
||||
)]
|
||||
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<SocketAddr>,
|
||||
|
||||
#[structopt(
|
||||
parse(try_from_str),
|
||||
short,
|
||||
long,
|
||||
help = "Set an initial corpus directory",
|
||||
name = "INPUT"
|
||||
)]
|
||||
input: Vec<PathBuf>,
|
||||
|
||||
#[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<PathBuf>,
|
||||
*/
|
||||
}
|
||||
|
||||
/// 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::<Tokens>();
|
||||
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<StdState<_, _, _, _, _>>, 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()
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
);
|
||||
|
||||
|
@ -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<CoreId> 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<usize> 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<core_affinity::CoreId> 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<CoreId>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Cores {
|
||||
/// Pick all cores
|
||||
pub fn all() -> Result<Self, Error> {
|
||||
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<Self, Error> {
|
||||
let mut cores: Vec<CoreId> = 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::<usize>()?.into());
|
||||
} else if core_range.len() == 2 {
|
||||
for x in core_range[0].parse::<usize>()?..=(core_range[1].parse::<usize>()?) {
|
||||
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::<Vec<String>>()
|
||||
.join(",");
|
||||
let ids = cores.iter().map(|x| (*x).into()).collect();
|
||||
Self { cmdline, ids }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<usize>> for Cores {
|
||||
fn from(cores: Vec<usize>) -> 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<Vec<usize>> {
|
||||
let mut cores: Vec<usize> = 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::<usize>().unwrap());
|
||||
} else if core_range.len() == 2 {
|
||||
for x in core_range[0].parse::<usize>().unwrap()
|
||||
..=(core_range[1].parse::<usize>().unwrap())
|
||||
{
|
||||
cores.push(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(cores)
|
||||
Cores::from_cmdline(args)
|
||||
.ok()
|
||||
.map(|cores| cores.ids.iter().map(|x| x.id).collect())
|
||||
}
|
||||
|
@ -640,7 +640,7 @@ where
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let copied_poll_fds: Vec<PollFd> = poll_fds.iter().copied().collect();
|
||||
let copied_poll_fds: Vec<PollFd> = 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 =
|
||||
|
@ -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,15 +174,21 @@ where
|
||||
{
|
||||
/// Creates the [`OnDiskCorpus`].
|
||||
/// Will error, if [`std::fs::create_dir_all()`] failed for `dir_path`.
|
||||
pub fn new(dir_path: PathBuf) -> Result<Self, Error> {
|
||||
pub fn new<P>(dir_path: P) -> Result<Self, Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fn new<I: Input>(dir_path: PathBuf) -> Result<OnDiskCorpus<I>, Error> {
|
||||
fs::create_dir_all(&dir_path)?;
|
||||
Ok(Self {
|
||||
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.
|
||||
/// Will error, if [`std::fs::create_dir_all()`] failed for `dir_path`.
|
||||
|
@ -61,13 +61,18 @@ const ITIMER_REAL: c_int = 0;
|
||||
|
||||
/// Reset and remove the timeout
|
||||
#[cfg(unix)]
|
||||
pub unsafe fn unix_remove_timeout() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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<I, S>,
|
||||
{
|
||||
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"))]
|
||||
|
@ -64,6 +64,7 @@ enum CmplogOperandType {
|
||||
Mem(capstone::RegId, capstone::RegId, i32, u32),
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
||||
enum SpecialCmpLogCase {
|
||||
Tbz,
|
||||
Tbnz,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<PathBuf>,
|
||||
output_dir: PathBuf,
|
||||
broker_port: u16,
|
||||
cores: Vec<usize>,
|
||||
cores: Cores,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
@ -276,7 +278,7 @@ pub mod pybind {
|
||||
input_dirs,
|
||||
output_dir,
|
||||
broker_port,
|
||||
cores,
|
||||
cores: cores.into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<PathBuf>,
|
||||
output_dir: PathBuf,
|
||||
broker_port: u16,
|
||||
cores: Vec<usize>,
|
||||
cores: Cores,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
@ -297,7 +299,7 @@ pub mod pybind {
|
||||
input_dirs,
|
||||
output_dir,
|
||||
broker_port,
|
||||
cores,
|
||||
cores: cores.into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<PathBuf>,
|
||||
output_dir: PathBuf,
|
||||
broker_port: u16,
|
||||
cores: Vec<usize>,
|
||||
cores: Cores,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
@ -355,7 +357,7 @@ pub mod pybind {
|
||||
input_dirs,
|
||||
output_dir,
|
||||
broker_port,
|
||||
cores,
|
||||
cores: cores.into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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"
|
||||
|
@ -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 <andreafioraldi@gmail.com>"
|
||||
)]
|
||||
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<P: AsRef<Path>>(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::<usize>("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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user