Remove fuzzbench_weighted and update fuzzbench (#865)
This commit is contained in:
parent
3054a69cf6
commit
5da5997b20
@ -38,7 +38,7 @@ use libafl::{
|
|||||||
},
|
},
|
||||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||||
schedulers::{
|
schedulers::{
|
||||||
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler,
|
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
|
||||||
},
|
},
|
||||||
stages::{
|
stages::{
|
||||||
calibrate::CalibrationStage, power::StdPowerMutationalStage, StdMutationalStage,
|
calibrate::CalibrationStage, power::StdPowerMutationalStage, StdMutationalStage,
|
||||||
@ -71,19 +71,22 @@ pub fn libafl_main() {
|
|||||||
Arg::new("out")
|
Arg::new("out")
|
||||||
.short('o')
|
.short('o')
|
||||||
.long("output")
|
.long("output")
|
||||||
.help("The directory to place finds in ('corpus')"),
|
.help("The directory to place finds in ('corpus')")
|
||||||
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("in")
|
Arg::new("in")
|
||||||
.short('i')
|
.short('i')
|
||||||
.long("input")
|
.long("input")
|
||||||
.help("The directory to read initial inputs from ('seeds')"),
|
.help("The directory to read initial inputs from ('seeds')")
|
||||||
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("tokens")
|
Arg::new("tokens")
|
||||||
.short('x')
|
.short('x')
|
||||||
.long("tokens")
|
.long("tokens")
|
||||||
.help("A file to read tokens from, to be used during fuzzing"),
|
.help("A file to read tokens from, to be used during fuzzing")
|
||||||
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new("logfile")
|
Arg::new("logfile")
|
||||||
@ -99,7 +102,7 @@ pub fn libafl_main() {
|
|||||||
.help("Timeout for each individual execution, in milliseconds")
|
.help("Timeout for each individual execution, in milliseconds")
|
||||||
.default_value("1200"),
|
.default_value("1200"),
|
||||||
)
|
)
|
||||||
.arg(Arg::new("remaining"))
|
.arg(Arg::new("remaining").multiple_values(true))
|
||||||
.try_get_matches()
|
.try_get_matches()
|
||||||
{
|
{
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
@ -120,8 +123,8 @@ pub fn libafl_main() {
|
|||||||
env::current_dir().unwrap().to_string_lossy().to_string()
|
env::current_dir().unwrap().to_string_lossy().to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(filenames) = res.get_many::<String>("remaining") {
|
if let Some(filenames) = res.values_of("remaining") {
|
||||||
let filenames: Vec<&str> = filenames.map(|v| v.as_str()).collect();
|
let filenames: Vec<&str> = filenames.collect();
|
||||||
if !filenames.is_empty() {
|
if !filenames.is_empty() {
|
||||||
run_testcases(&filenames);
|
run_testcases(&filenames);
|
||||||
return;
|
return;
|
||||||
@ -157,11 +160,12 @@ pub fn libafl_main() {
|
|||||||
|
|
||||||
let tokens = res.get_one::<String>("tokens").map(PathBuf::from);
|
let tokens = res.get_one::<String>("tokens").map(PathBuf::from);
|
||||||
|
|
||||||
let logfile = PathBuf::from(res.get_one::<String>("logfile").unwrap());
|
let logfile = PathBuf::from(res.get_one::<String>("logfile").unwrap().to_string());
|
||||||
|
|
||||||
let timeout = Duration::from_millis(
|
let timeout = Duration::from_millis(
|
||||||
res.get_one::<String>("timeout")
|
res.get_one::<String>("timeout")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
.parse()
|
.parse()
|
||||||
.expect("Could not parse timeout in milliseconds"),
|
.expect("Could not parse timeout in milliseconds"),
|
||||||
);
|
);
|
||||||
@ -313,8 +317,9 @@ fn fuzz(
|
|||||||
let power = StdPowerMutationalStage::new(mutator, &edges_observer);
|
let power = StdPowerMutationalStage::new(mutator, &edges_observer);
|
||||||
|
|
||||||
// A minimization+queue policy to get testcasess from the corpus
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
let scheduler =
|
let scheduler = IndexesLenTimeMinimizerScheduler::new(StdWeightedScheduler::with_schedule(
|
||||||
IndexesLenTimeMinimizerScheduler::new(PowerQueueScheduler::new(PowerSchedule::FAST));
|
PowerSchedule::EXPLORE,
|
||||||
|
));
|
||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
@ -44,7 +44,7 @@ use libafl::{
|
|||||||
},
|
},
|
||||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||||
schedulers::{
|
schedulers::{
|
||||||
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, PowerQueueScheduler,
|
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
|
||||||
},
|
},
|
||||||
stages::{
|
stages::{
|
||||||
calibrate::CalibrationStage, power::StdPowerMutationalStage, GeneralizationStage,
|
calibrate::CalibrationStage, power::StdPowerMutationalStage, GeneralizationStage,
|
||||||
@ -377,8 +377,9 @@ fn fuzz_binary(
|
|||||||
let power = StdPowerMutationalStage::new(mutator, &edges_observer);
|
let power = StdPowerMutationalStage::new(mutator, &edges_observer);
|
||||||
|
|
||||||
// A minimization+queue policy to get testcasess from the corpus
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
let scheduler =
|
let scheduler = IndexesLenTimeMinimizerScheduler::new(StdWeightedScheduler::with_schedule(
|
||||||
IndexesLenTimeMinimizerScheduler::new(PowerQueueScheduler::new(PowerSchedule::FAST));
|
PowerSchedule::EXPLORE,
|
||||||
|
));
|
||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
@ -532,7 +533,7 @@ fn fuzz_text(
|
|||||||
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true);
|
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true);
|
||||||
|
|
||||||
// New maximization map feedback linked to the edges observer and the feedback state
|
// New maximization map feedback linked to the edges observer and the feedback state
|
||||||
let mut map_feedback = MaxMapFeedback::new_tracking(&edges_observer, true, true);
|
let map_feedback = MaxMapFeedback::new_tracking(&edges_observer, true, true);
|
||||||
|
|
||||||
let calibration = CalibrationStage::new(&map_feedback);
|
let calibration = CalibrationStage::new(&map_feedback);
|
||||||
|
|
||||||
@ -602,8 +603,9 @@ fn fuzz_text(
|
|||||||
let grimoire = StdMutationalStage::new(grimoire_mutator);
|
let grimoire = StdMutationalStage::new(grimoire_mutator);
|
||||||
|
|
||||||
// A minimization+queue policy to get testcasess from the corpus
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
let scheduler =
|
let scheduler = IndexesLenTimeMinimizerScheduler::new(StdWeightedScheduler::with_schedule(
|
||||||
IndexesLenTimeMinimizerScheduler::new(PowerQueueScheduler::new(PowerSchedule::FAST));
|
PowerSchedule::EXPLORE,
|
||||||
|
));
|
||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
2
fuzzers/fuzzbench_weighted/.gitignore
vendored
2
fuzzers/fuzzbench_weighted/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
libpng-*
|
|
||||||
fuzzer
|
|
@ -1,32 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "fuzzbench_weighted"
|
|
||||||
version = "0.8.2"
|
|
||||||
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std"]
|
|
||||||
std = []
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
lto = true
|
|
||||||
codegen-units = 1
|
|
||||||
opt-level = 3
|
|
||||||
debug = true
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
cc = { version = "1.0", features = ["parallel"] }
|
|
||||||
which = { version = "4.0.2" }
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
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.2", features = ["default"] }
|
|
||||||
nix = "0.25"
|
|
||||||
mimalloc = { version = "*", default-features = false }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "fuzzbench"
|
|
||||||
crate-type = ["staticlib"]
|
|
@ -1,100 +0,0 @@
|
|||||||
[env]
|
|
||||||
CARGO_TARGET_DIR = { value = "target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } }
|
|
||||||
FUZZER_NAME="fuzzer"
|
|
||||||
PROJECT_DIR = { script = ["pwd"] }
|
|
||||||
|
|
||||||
[tasks.unsupported]
|
|
||||||
script_runner="@shell"
|
|
||||||
script='''
|
|
||||||
echo "Cargo-make not integrated yet on this"
|
|
||||||
'''
|
|
||||||
|
|
||||||
# Compilers
|
|
||||||
[tasks.cxx]
|
|
||||||
linux_alias = "cxx_unix"
|
|
||||||
mac_alias = "cxx_unix"
|
|
||||||
windows_alias = "unsupported"
|
|
||||||
|
|
||||||
[tasks.cxx_unix]
|
|
||||||
command = "cargo"
|
|
||||||
args = ["build" , "--release"]
|
|
||||||
|
|
||||||
[tasks.cc]
|
|
||||||
linux_alias = "cc_unix"
|
|
||||||
mac_alias = "cc_unix"
|
|
||||||
windows_alias = "unsupported"
|
|
||||||
|
|
||||||
[tasks.cc_unix]
|
|
||||||
command = "cargo"
|
|
||||||
args = ["build" , "--release"]
|
|
||||||
|
|
||||||
# fuzz.o File
|
|
||||||
[tasks.fuzz_o]
|
|
||||||
linux_alias = "fuzz_o_unix"
|
|
||||||
mac_alias = "fuzz_o_unix"
|
|
||||||
windows_alias = "unsupported"
|
|
||||||
|
|
||||||
[tasks.fuzz_o_unix]
|
|
||||||
command = "${CARGO_TARGET_DIR}/release/libafl_cc"
|
|
||||||
args = ["--libafl-no-link", "-O3", "-c", "fuzz.c", "-o", "fuzz.o"]
|
|
||||||
dependencies = ["cc", "cxx"]
|
|
||||||
|
|
||||||
# Fuzzer
|
|
||||||
[tasks.fuzzer]
|
|
||||||
linux_alias = "fuzzer_unix"
|
|
||||||
mac_alias = "fuzzer_unix"
|
|
||||||
windows_alias = "unsupported"
|
|
||||||
|
|
||||||
[tasks.fuzzer_unix]
|
|
||||||
command = "${CARGO_TARGET_DIR}/release/libafl_cxx"
|
|
||||||
args = ["--libafl", "fuzz.o", "-o", "${FUZZER_NAME}", "-lm", "-lz"]
|
|
||||||
dependencies = ["cc", "cxx", "fuzz_o"]
|
|
||||||
|
|
||||||
# Run
|
|
||||||
[tasks.run]
|
|
||||||
linux_alias = "run_unix"
|
|
||||||
mac_alias = "run_unix"
|
|
||||||
windows_alias = "unsupported"
|
|
||||||
|
|
||||||
[tasks.run_unix]
|
|
||||||
script_runner="@shell"
|
|
||||||
script='''
|
|
||||||
rm -rf libafl_unix_shmem_server || true
|
|
||||||
mkdir in || true
|
|
||||||
echo a > in/a
|
|
||||||
./${FUZZER_NAME} -o out -i in
|
|
||||||
'''
|
|
||||||
dependencies = ["fuzzer"]
|
|
||||||
|
|
||||||
|
|
||||||
# Test
|
|
||||||
[tasks.test]
|
|
||||||
linux_alias = "test_unix"
|
|
||||||
mac_alias = "test_unix"
|
|
||||||
windows_alias = "unsupported"
|
|
||||||
|
|
||||||
[tasks.test_unix]
|
|
||||||
script_runner="@shell"
|
|
||||||
script='''
|
|
||||||
rm -rf libafl_unix_shmem_server || true
|
|
||||||
mkdir in || true
|
|
||||||
echo a > in/a
|
|
||||||
# Allow sigterm as exit code
|
|
||||||
timeout 11s ./${FUZZER_NAME} -o out -i in || [ $? -eq 124 ]
|
|
||||||
rm -rf out || true
|
|
||||||
rm -rf in || true
|
|
||||||
'''
|
|
||||||
dependencies = ["fuzzer"]
|
|
||||||
|
|
||||||
# Clean
|
|
||||||
[tasks.clean]
|
|
||||||
linux_alias = "clean_unix"
|
|
||||||
mac_alias = "clean_unix"
|
|
||||||
windows_alias = "unsupported"
|
|
||||||
|
|
||||||
[tasks.clean_unix]
|
|
||||||
script_runner="@shell"
|
|
||||||
script='''
|
|
||||||
rm ./${FUZZER_NAME} || true
|
|
||||||
rm fuzz.o || true
|
|
||||||
'''
|
|
@ -1,17 +0,0 @@
|
|||||||
# Fuzzbench Harness
|
|
||||||
|
|
||||||
This folder contains an example fuzzer tailored for fuzzbench.
|
|
||||||
It uses the best possible setting, with the exception of a SimpleRestartingEventManager instead of an LlmpEventManager - since fuzzbench is single threaded.
|
|
||||||
Real fuzz campaigns should consider using multithreaded LlmpEventManager, see the other examples.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
To build this example, run `cargo build --release`.
|
|
||||||
This will build the fuzzer compilers (`libafl_cc` and `libafl_cpp`) with `src/lib.rs` as fuzzer.
|
|
||||||
The fuzzer uses the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback.
|
|
||||||
|
|
||||||
These can then be used to build libfuzzer harnesses in the software project of your choice.
|
|
||||||
Finally, just run the resulting binary with `out_dir`, `in_dir`.
|
|
||||||
|
|
||||||
In any real-world scenario, you should use `taskset` to pin each client to an empty CPU core, the lib does not pick an empty core automatically (yet).
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
|||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
|
||||||
if (Size >= 8 && *(uint32_t *)Data == 0xaabbccdd) { abort(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
int main() {
|
|
||||||
|
|
||||||
char buf [10] = {0};
|
|
||||||
LLVMFuzzerTestOneInput(buf, 10);
|
|
||||||
|
|
||||||
}*/
|
|
@ -1,45 +0,0 @@
|
|||||||
use std::env;
|
|
||||||
|
|
||||||
use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses};
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
let mut args: Vec<String> = env::args().collect();
|
|
||||||
if args.len() > 1 {
|
|
||||||
let mut dir = env::current_exe().unwrap();
|
|
||||||
let wrapper_name = dir.file_name().unwrap().to_str().unwrap();
|
|
||||||
|
|
||||||
let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() {
|
|
||||||
"cc" => false,
|
|
||||||
"++" | "pp" | "xx" => true,
|
|
||||||
_ => panic!("Could not figure out if c or c++ warpper was called. Expected {:?} to end with c or cxx", dir),
|
|
||||||
};
|
|
||||||
|
|
||||||
dir.pop();
|
|
||||||
|
|
||||||
// Must be always present, even without --libafl
|
|
||||||
args.push("-fsanitize-coverage=trace-pc-guard,trace-cmp".into());
|
|
||||||
|
|
||||||
let mut cc = ClangWrapper::new();
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
|
||||||
cc.add_pass(LLVMPasses::AutoTokens);
|
|
||||||
|
|
||||||
if let Some(code) = cc
|
|
||||||
.cpp(is_cpp)
|
|
||||||
// silence the compiler wrapper output, needed for some configure scripts.
|
|
||||||
.silence(true)
|
|
||||||
// add arguments only if --libafl or --libafl-no-link are present
|
|
||||||
.need_libafl_arg(true)
|
|
||||||
.parse_args(&args)
|
|
||||||
.expect("Failed to parse the command line")
|
|
||||||
.link_staticlib(&dir, "fuzzbench")
|
|
||||||
.add_pass(LLVMPasses::CmpLogRtn)
|
|
||||||
.run()
|
|
||||||
.expect("Failed to run the wrapped compiler")
|
|
||||||
{
|
|
||||||
std::process::exit(code);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic!("LibAFL CC: No Arguments given");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
pub mod libafl_cc;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
libafl_cc::main()
|
|
||||||
}
|
|
@ -1,411 +0,0 @@
|
|||||||
//! A singlethreaded libfuzzer-like fuzzer that can auto-restart.
|
|
||||||
use mimalloc::MiMalloc;
|
|
||||||
#[global_allocator]
|
|
||||||
static GLOBAL: MiMalloc = MiMalloc;
|
|
||||||
|
|
||||||
use core::{cell::RefCell, time::Duration};
|
|
||||||
#[cfg(unix)]
|
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
|
||||||
use std::{
|
|
||||||
env,
|
|
||||||
fs::{self, File, OpenOptions},
|
|
||||||
io::{self, Read, Write},
|
|
||||||
path::PathBuf,
|
|
||||||
process,
|
|
||||||
};
|
|
||||||
|
|
||||||
use clap::{Arg, Command};
|
|
||||||
use libafl::{
|
|
||||||
bolts::{
|
|
||||||
current_nanos, current_time,
|
|
||||||
os::dup2,
|
|
||||||
rands::StdRand,
|
|
||||||
shmem::{ShMemProvider, StdShMemProvider},
|
|
||||||
tuples::{tuple_list, Merge},
|
|
||||||
AsSlice,
|
|
||||||
},
|
|
||||||
corpus::{Corpus, OnDiskCorpus},
|
|
||||||
events::SimpleRestartingEventManager,
|
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
|
|
||||||
feedback_or,
|
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
|
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
|
||||||
inputs::{BytesInput, HasTargetBytes},
|
|
||||||
monitors::SimpleMonitor,
|
|
||||||
mutators::{
|
|
||||||
scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations,
|
|
||||||
StdMOptMutator, StdScheduledMutator, Tokens,
|
|
||||||
},
|
|
||||||
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
|
||||||
schedulers::{
|
|
||||||
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
|
|
||||||
},
|
|
||||||
stages::{
|
|
||||||
calibrate::CalibrationStage, power::StdPowerMutationalStage, StdMutationalStage,
|
|
||||||
TracingStage,
|
|
||||||
},
|
|
||||||
state::{HasCorpus, HasMetadata, StdState},
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
|
||||||
use libafl_targets::autotokens;
|
|
||||||
use libafl_targets::{
|
|
||||||
libfuzzer_initialize, libfuzzer_test_one_input, CmpLogObserver, CMPLOG_MAP, EDGES_MAP,
|
|
||||||
MAX_EDGES_NUM,
|
|
||||||
};
|
|
||||||
#[cfg(unix)]
|
|
||||||
use nix::{self, unistd::dup};
|
|
||||||
|
|
||||||
/// The fuzzer main (as `no_mangle` C function)
|
|
||||||
#[no_mangle]
|
|
||||||
pub fn libafl_main() {
|
|
||||||
// Registry the metadata types used in this fuzzer
|
|
||||||
// Needed only on no_std
|
|
||||||
//RegistryBuilder::register::<Tokens>();
|
|
||||||
|
|
||||||
let res = match Command::new(env!("CARGO_PKG_NAME"))
|
|
||||||
.version(env!("CARGO_PKG_VERSION"))
|
|
||||||
.author("AFLplusplus team")
|
|
||||||
.about("LibAFL-based fuzzer for Fuzzbench")
|
|
||||||
.arg(
|
|
||||||
Arg::new("out")
|
|
||||||
.short('o')
|
|
||||||
.long("output")
|
|
||||||
.help("The directory to place finds in ('corpus')")
|
|
||||||
.takes_value(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("in")
|
|
||||||
.short('i')
|
|
||||||
.long("input")
|
|
||||||
.help("The directory to read initial inputs from ('seeds')")
|
|
||||||
.takes_value(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("tokens")
|
|
||||||
.short('x')
|
|
||||||
.long("tokens")
|
|
||||||
.help("A file to read tokens from, to be used during fuzzing")
|
|
||||||
.takes_value(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("logfile")
|
|
||||||
.short('l')
|
|
||||||
.long("logfile")
|
|
||||||
.help("Duplicates all output to this file")
|
|
||||||
.default_value("libafl.log"),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new("timeout")
|
|
||||||
.short('t')
|
|
||||||
.long("timeout")
|
|
||||||
.help("Timeout for each individual execution, in milliseconds")
|
|
||||||
.default_value("1200"),
|
|
||||||
)
|
|
||||||
.arg(Arg::new("remaining").multiple_values(true))
|
|
||||||
.try_get_matches()
|
|
||||||
{
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(err) => {
|
|
||||||
println!(
|
|
||||||
"Syntax: {}, [-x dictionary] -o corpus_dir -i seed_dir\n{:?}",
|
|
||||||
env::current_exe()
|
|
||||||
.unwrap_or_else(|_| "fuzzer".into())
|
|
||||||
.to_string_lossy(),
|
|
||||||
err,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Workdir: {:?}",
|
|
||||||
env::current_dir().unwrap().to_string_lossy().to_string()
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(filenames) = res.values_of("remaining") {
|
|
||||||
let filenames: Vec<&str> = filenames.collect();
|
|
||||||
if !filenames.is_empty() {
|
|
||||||
run_testcases(&filenames);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir.
|
|
||||||
let mut out_dir = PathBuf::from(
|
|
||||||
res.get_one::<String>("out")
|
|
||||||
.expect("The --output parameter is missing")
|
|
||||||
.to_string(),
|
|
||||||
);
|
|
||||||
if fs::create_dir(&out_dir).is_err() {
|
|
||||||
println!("Out dir at {:?} already exists.", &out_dir);
|
|
||||||
if !out_dir.is_dir() {
|
|
||||||
println!("Out dir at {:?} is not a valid directory!", &out_dir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut crashes = out_dir.clone();
|
|
||||||
crashes.push("crashes");
|
|
||||||
out_dir.push("queue");
|
|
||||||
|
|
||||||
let in_dir = PathBuf::from(
|
|
||||||
res.get_one::<String>("in")
|
|
||||||
.expect("The --input parameter is missing")
|
|
||||||
.to_string(),
|
|
||||||
);
|
|
||||||
if !in_dir.is_dir() {
|
|
||||||
println!("In dir at {:?} is not a valid directory!", &in_dir);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tokens = res.get_one::<String>("tokens").map(PathBuf::from);
|
|
||||||
|
|
||||||
let logfile = PathBuf::from(res.get_one::<String>("logfile").unwrap().to_string());
|
|
||||||
|
|
||||||
let timeout = Duration::from_millis(
|
|
||||||
res.get_one::<String>("timeout")
|
|
||||||
.unwrap()
|
|
||||||
.to_string()
|
|
||||||
.parse()
|
|
||||||
.expect("Could not parse timeout in milliseconds"),
|
|
||||||
);
|
|
||||||
|
|
||||||
fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout)
|
|
||||||
.expect("An error occurred while fuzzing");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_testcases(filenames: &[&str]) {
|
|
||||||
// The actual target run starts here.
|
|
||||||
// Call LLVMFUzzerInitialize() if present.
|
|
||||||
let args: Vec<String> = env::args().collect();
|
|
||||||
if libfuzzer_initialize(&args) == -1 {
|
|
||||||
println!("Warning: LLVMFuzzerInitialize failed with -1")
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"You are not fuzzing, just executing {} testcases",
|
|
||||||
filenames.len()
|
|
||||||
);
|
|
||||||
for fname in filenames {
|
|
||||||
println!("Executing {}", fname);
|
|
||||||
|
|
||||||
let mut file = File::open(fname).expect("No file found");
|
|
||||||
let mut buffer = vec![];
|
|
||||||
file.read_to_end(&mut buffer).expect("Buffer overflow");
|
|
||||||
|
|
||||||
libfuzzer_test_one_input(&buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The actual fuzzer
|
|
||||||
fn fuzz(
|
|
||||||
corpus_dir: PathBuf,
|
|
||||||
objective_dir: PathBuf,
|
|
||||||
seed_dir: PathBuf,
|
|
||||||
tokenfile: Option<PathBuf>,
|
|
||||||
logfile: PathBuf,
|
|
||||||
timeout: Duration,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let log = RefCell::new(
|
|
||||||
OpenOptions::new()
|
|
||||||
.append(true)
|
|
||||||
.create(true)
|
|
||||||
.open(&logfile)?,
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
let mut stdout_cpy = unsafe {
|
|
||||||
let new_fd = dup(io::stdout().as_raw_fd())?;
|
|
||||||
File::from_raw_fd(new_fd)
|
|
||||||
};
|
|
||||||
#[cfg(unix)]
|
|
||||||
let file_null = File::open("/dev/null")?;
|
|
||||||
|
|
||||||
// 'While the monitor are state, they are usually used in the broker - which is likely never restarted
|
|
||||||
let monitor = SimpleMonitor::new(|s| {
|
|
||||||
#[cfg(unix)]
|
|
||||||
writeln!(&mut stdout_cpy, "{}", s).unwrap();
|
|
||||||
#[cfg(windows)]
|
|
||||||
println!("{}", s);
|
|
||||||
writeln!(log.borrow_mut(), "{:?} {}", current_time(), s).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
// We need a shared map to store our state before a crash.
|
|
||||||
// This way, we are able to continue fuzzing afterwards.
|
|
||||||
let mut shmem_provider = StdShMemProvider::new()?;
|
|
||||||
|
|
||||||
let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider)
|
|
||||||
{
|
|
||||||
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(err) => match err {
|
|
||||||
Error::ShuttingDown => {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!("Failed to setup the restarter: {}", err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
|
||||||
// We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges)
|
|
||||||
let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] };
|
|
||||||
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("edges", edges));
|
|
||||||
|
|
||||||
// Create an observation channel to keep track of the execution time
|
|
||||||
let time_observer = TimeObserver::new("time");
|
|
||||||
|
|
||||||
let cmplog = unsafe { &mut CMPLOG_MAP };
|
|
||||||
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true);
|
|
||||||
|
|
||||||
let map_feedback = MaxMapFeedback::new_tracking(&edges_observer, true, false);
|
|
||||||
|
|
||||||
let calibration = CalibrationStage::new(&map_feedback);
|
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input
|
|
||||||
// This one is composed by two Feedbacks in OR
|
|
||||||
let mut feedback = feedback_or!(
|
|
||||||
// New maximization map feedback linked to the edges observer and the feedback state
|
|
||||||
map_feedback,
|
|
||||||
// Time feedback, this one does not need a feedback state
|
|
||||||
TimeFeedback::new_with_observer(&time_observer)
|
|
||||||
);
|
|
||||||
|
|
||||||
// A feedback to choose if an input is a solution or not
|
|
||||||
let mut objective = CrashFeedback::new();
|
|
||||||
|
|
||||||
// If not restarting, create a State from scratch
|
|
||||||
let mut state = state.unwrap_or_else(|| {
|
|
||||||
StdState::new(
|
|
||||||
// RNG
|
|
||||||
StdRand::with_seed(current_nanos()),
|
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
|
||||||
OnDiskCorpus::new(corpus_dir).unwrap(),
|
|
||||||
// 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(),
|
|
||||||
// States of the feedbacks.
|
|
||||||
// The feedbacks can report the data that should persist in the State.
|
|
||||||
&mut feedback,
|
|
||||||
// Same for objective feedbacks
|
|
||||||
&mut objective,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
|
|
||||||
println!("Let's fuzz :)");
|
|
||||||
|
|
||||||
// The actual target run starts here.
|
|
||||||
// Call LLVMFUzzerInitialize() if present.
|
|
||||||
let args: Vec<String> = env::args().collect();
|
|
||||||
if libfuzzer_initialize(&args) == -1 {
|
|
||||||
println!("Warning: LLVMFuzzerInitialize failed with -1")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup a randomic Input2State stage
|
|
||||||
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
|
|
||||||
|
|
||||||
// Setup a MOPT mutator
|
|
||||||
let mutator = StdMOptMutator::new(
|
|
||||||
&mut state,
|
|
||||||
havoc_mutations().merge(tokens_mutations()),
|
|
||||||
7,
|
|
||||||
5,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let power = StdPowerMutationalStage::new(mutator, &edges_observer);
|
|
||||||
|
|
||||||
// A minimization+queue policy to get testcasess from the corpus
|
|
||||||
let scheduler = IndexesLenTimeMinimizerScheduler::new(StdWeightedScheduler::with_schedule(
|
|
||||||
PowerSchedule::EXPLORE,
|
|
||||||
));
|
|
||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
|
||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
|
||||||
let mut harness = |input: &BytesInput| {
|
|
||||||
let target = input.target_bytes();
|
|
||||||
let buf = target.as_slice();
|
|
||||||
libfuzzer_test_one_input(buf);
|
|
||||||
ExitKind::Ok
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut tracing_harness = harness;
|
|
||||||
|
|
||||||
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time
|
|
||||||
let mut executor = TimeoutExecutor::new(
|
|
||||||
InProcessExecutor::new(
|
|
||||||
&mut harness,
|
|
||||||
tuple_list!(edges_observer, time_observer),
|
|
||||||
&mut fuzzer,
|
|
||||||
&mut state,
|
|
||||||
&mut mgr,
|
|
||||||
)?,
|
|
||||||
timeout,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Setup a tracing stage in which we log comparisons
|
|
||||||
let tracing = TracingStage::new(TimeoutExecutor::new(
|
|
||||||
InProcessExecutor::new(
|
|
||||||
&mut tracing_harness,
|
|
||||||
tuple_list!(cmplog_observer),
|
|
||||||
&mut fuzzer,
|
|
||||||
&mut state,
|
|
||||||
&mut mgr,
|
|
||||||
)?,
|
|
||||||
// Give it more time!
|
|
||||||
timeout * 10,
|
|
||||||
));
|
|
||||||
|
|
||||||
// The order of the stages matter!
|
|
||||||
let mut stages = tuple_list!(calibration, tracing, i2s, power);
|
|
||||||
|
|
||||||
// Read tokens
|
|
||||||
if state.metadata().get::<Tokens>().is_none() {
|
|
||||||
let mut toks = Tokens::default();
|
|
||||||
if let Some(tokenfile) = tokenfile {
|
|
||||||
toks.add_from_file(tokenfile)?;
|
|
||||||
}
|
|
||||||
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
|
|
||||||
{
|
|
||||||
toks += autotokens()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !toks.is_empty() {
|
|
||||||
state.add_metadata(toks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case the corpus is empty (on first run), reset
|
|
||||||
if state.corpus().count() < 1 {
|
|
||||||
state
|
|
||||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()])
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
println!("Failed to load initial corpus at {:?}", &seed_dir);
|
|
||||||
process::exit(0);
|
|
||||||
});
|
|
||||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove target ouput (logs still survive)
|
|
||||||
#[cfg(unix)]
|
|
||||||
{
|
|
||||||
let null_fd = file_null.as_raw_fd();
|
|
||||||
dup2(null_fd, io::stdout().as_raw_fd())?;
|
|
||||||
dup2(null_fd, io::stderr().as_raw_fd())?;
|
|
||||||
}
|
|
||||||
// reopen file to make sure we're at the end
|
|
||||||
log.replace(
|
|
||||||
OpenOptions::new()
|
|
||||||
.append(true)
|
|
||||||
.create(true)
|
|
||||||
.open(&logfile)?,
|
|
||||||
);
|
|
||||||
|
|
||||||
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
|
|
||||||
|
|
||||||
// Never reached
|
|
||||||
Ok(())
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user