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:
Dominik Maier 2021-12-15 03:58:35 +01:00 committed by GitHub
parent b4c2551544
commit 217a7dee1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 716 additions and 324 deletions

View File

@ -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"] }

View File

@ -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"

View File

@ -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",

View File

@ -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,

View File

@ -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]

View File

@ -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]

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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"

View File

@ -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

View File

@ -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()

View File

@ -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"

View File

@ -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

View File

@ -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()

View File

@ -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 {

View File

@ -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"
);

View File

@ -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())
}

View File

@ -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 =

View File

@ -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`.

View File

@ -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);
}

View File

@ -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"

View File

@ -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
);
}

View File

@ -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"))]

View File

@ -64,6 +64,7 @@ enum CmplogOperandType {
Mem(capstone::RegId, capstone::RegId, i32, u32),
}
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
enum SpecialCmpLogCase {
Tbz,
Tbnz,

View File

@ -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);
}
}
}

View File

@ -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(),
}
}

View File

@ -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(),
}
}

View File

@ -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(),
}
}

View File

@ -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();

View File

@ -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"

View File

@ -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;