libafl_sugar (#215)

* fuzzer mod

* libafl_sugar skeleton

* build libafl_sugar

* libfuzzer_stb_image_sugar

* Delete log

* qemu in libafl_sugar

* docker

* macos merda

Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
Andrea Fioraldi 2021-07-20 11:47:33 +02:00 committed by GitHub
parent 9591ed995e
commit dfe39e2af7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 8481 additions and 1 deletions

View File

@ -12,6 +12,7 @@ members = [
"libafl_targets",
"libafl_frida",
"libafl_qemu",
"libafl_sugar",
"libafl_tests",
]
default-members = [

View File

@ -41,6 +41,9 @@ COPY libafl_qemu/Cargo.toml libafl_qemu/build.rs libafl_qemu/
COPY scripts/dummy.rs libafl_qemu/src/lib.rs
COPY libafl_qemu/src/weaks.c libafl_qemu/src/weaks.c
COPY libafl_sugar/Cargo.toml libafl_sugar/
COPY scripts/dummy.rs libafl_sugar/src/lib.rs
COPY libafl_cc/Cargo.toml libafl_cc/Cargo.toml
COPY scripts/dummy.rs libafl_cc/src/lib.rs
COPY libafl_cc/build.rs libafl_cc/build.rs

View File

@ -127,7 +127,7 @@ pub fn libafl_main() {
)
});
// Create a PNG dictionary if not existing
// Create a dictionary if not existing
if state.metadata().get::<Tokens>().is_none() {
for tokens_file in &token_files {
state.add_metadata(Tokens::from_tokens_file(tokens_file)?);

View File

@ -0,0 +1 @@
libpng-*

View File

@ -0,0 +1,25 @@
[package]
name = "libfuzzer_stb_image"
version = "0.5.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2018"
build = "build.rs"
[features]
default = ["std"]
std = []
[profile.release]
lto = true
codegen-units = 1
opt-level = 3
debug = true
[dependencies]
libafl = { path = "../../libafl/" }
libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_edges", "sancov_cmplog", "libfuzzer"] }
libafl_sugar = { path = "../../libafl_sugar/" }
[build-dependencies]
cc = { version = "1.0", features = ["parallel"] }
num_cpus = "1.0"

View File

@ -0,0 +1,71 @@
FUZZER_NAME="libfuzzer_stb_image"
PROJECT_DIR=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
PHONY: all
all: fuzzer
target/release/libafl_cxx: build.rs
# Build the libpng libfuzzer library
cargo build --release
libafl_cxx: target/release/libafl_cxx
libafl_cc: target/release/libafl_cxx
fuzzer: libafl_cxx
# Build the libpng libfuzzer library
cargo build --release
cp $(PROJECT_DIR)/target/release/$(FUZZER_NAME) .
clean:
rm ./$(FUZZER_NAME)
run: all
./$(FUZZER_NAME) &
sleep 0.2
./$(FUZZER_NAME) >/dev/null 2>/dev/null &
short_test: all
timeout 11s ./$(FUZZER_NAME) &
sleep 0.2
timeout 10s taskset -c 0 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
timeout 10s taskset -c 1 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
timeout 10s taskset -c 2 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
timeout 10s taskset -c 3 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
test: all
timeout 60s ./$(FUZZER_NAME) &
sleep 0.2
timeout 59s taskset 0x00000001 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
timeout 59s taskset 0x00000002 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
timeout 59s taskset 0x00000004 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
timeout 59s taskset 0x00000008 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000010 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000020 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000040 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000080 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000100 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000200 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000400 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00000800 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00001000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00002000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00004000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00008000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00010000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00020000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00040000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00080000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00100000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00200000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00400000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x00800000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x01000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x02000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x04000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x08000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x10000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x20000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x40000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
# timeout 59s taskset 0x80000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &

View File

@ -0,0 +1,11 @@
# Libfuzzer for stb_image with libafl_sugar
This folder contains an example fuzzer for stb_image, using LLMP for fast multi-process fuzzing and crash detection.
It has been tested on Linux and Windows.
## Build
To build this example, run `cargo build --release`.
This will build the the fuzzer (src/main.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback as a standalone binary.
Unlike the libpng example, in this example the harness (that entirely includes the program under test) is compiled in the `build.rs` file while building the crate, and linked with the fuzzer by cargo when producing the final binary, `target/release/libfuzzer_stb_image`.

View File

@ -0,0 +1,27 @@
// build.rs
use std::env;
fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let out_dir = out_dir.to_string_lossy().to_string();
println!("cargo:rerun-if-changed=harness.c");
// Enforce clang for its -fsanitize-coverage support.
std::env::set_var("CC", "clang");
std::env::set_var("CXX", "clang++");
cc::Build::new()
// Use sanitizer coverage to track the edges in the PUT
.flag("-fsanitize-coverage=trace-pc-guard,trace-cmp")
// Take advantage of LTO (needs lld-link set in your cargo config)
//.flag("-flto=thin")
.flag("-Wno-sign-compare")
.file("./harness.c")
.compile("harness");
println!("cargo:rustc-link-search=native={}", &out_dir);
println!("cargo:rerun-if-changed=build.rs");
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

View File

@ -0,0 +1,28 @@
#include <stdint.h>
#include <assert.h>
#define STBI_ASSERT(x)
#define STBI_NO_SIMD
#define STBI_NO_LINEAR
#define STBI_NO_STDIO
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
int x, y, channels;
if(!stbi_info_from_memory(data, size, &x, &y, &channels)) return 0;
/* exit if the image is larger than ~80MB */
if(y && x > (80000000 / 4) / y) return 0;
unsigned char *img = stbi_load_from_memory(data, size, &x, &y, &channels, 4);
free(img);
// if (x > 10000) free(img); // free crash
return 0;
}

View File

@ -0,0 +1,44 @@
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
//! The example harness is built for `stb_image`.
use std::{env, path::PathBuf};
use libafl_sugar::InMemoryBytesCoverageSugar;
use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input};
pub fn main() {
// Registry the metadata types used in this fuzzer
// Needed only on no_std
//RegistryBuilder::register::<Tokens>();
println!(
"Workdir: {:?}",
env::current_dir().unwrap().to_string_lossy().to_string()
);
fuzz(
&[PathBuf::from("./input")],
PathBuf::from("./output"),
&[1],
1337,
);
}
/// The actual fuzzer
fn fuzz(input_dirs: &[PathBuf], output_dir: PathBuf, cores: &[usize], broker_port: u16) {
// Call LLVMFUzzerInitialize() if present.
let args: Vec<String> = env::args().collect();
if libfuzzer_initialize(&args) == -1 {
println!("Warning: LLVMFuzzerInitialize failed with -1")
}
InMemoryBytesCoverageSugar::builder()
.input_dirs(input_dirs)
.output_dir(output_dir)
.cores(cores)
.broker_port(broker_port)
.harness(|buf| {
libfuzzer_test_one_input(buf);
})
.build()
.run();
}

File diff suppressed because it is too large Load Diff

13
libafl_sugar/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "libafl_sugar"
version = "0.1.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libafl = { path = "../libafl", version = "0.5.0" }
libafl_targets = { path = "../libafl_targets", version = "0.5.0" }
libafl_qemu = { path = "../libafl_qemu", version = "0.5.0" }
typed-builder = "0.9.0" # Implement the builder pattern at compiletime

View File

@ -0,0 +1,238 @@
use typed_builder::TypedBuilder;
use std::{fs, net::SocketAddr, path::PathBuf, time::Duration};
use libafl::{
bolts::{
current_nanos,
launcher::Launcher,
rands::StdRand,
shmem::{ShMemProvider, StdShMemProvider},
tuples::{tuple_list, Merge},
},
corpus::{
CachedOnDiskCorpus, Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
QueueCorpusScheduler,
},
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
feedback_or, feedback_or_fast,
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer},
generators::RandBytesGenerator,
inputs::{BytesInput, HasTargetBytes},
mutators::scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator},
mutators::token_mutations::Tokens,
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
stages::StdMutationalStage,
state::{HasCorpus, HasMetadata, StdState},
stats::MultiStats,
};
use libafl_targets::{EDGES_MAP, MAX_EDGES_NUM};
use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS};
#[derive(TypedBuilder)]
pub struct InMemoryBytesCoverageSugar<'a, H>
where
H: FnMut(&[u8]),
{
/// Laucher configuration (default is random)
#[builder(default = None, setter(strip_option))]
configuration: Option<String>,
/// Timeout of the executor
#[builder(default = None, setter(strip_option))]
timeout: Option<u64>,
/// Input directories
input_dirs: &'a [PathBuf],
/// Output directory
output_dir: PathBuf,
/// Dictionary
#[builder(default = None, setter(strip_option))]
tokens_file: Option<PathBuf>,
/// Flag if use CmpLog
//#[builder(default = false)]
//use_cmplog: bool,
#[builder(default = 1337_u16)]
broker_port: u16,
/// The list of cores to run on
cores: &'a [usize],
/// The `ip:port` address of another broker to connect our new broker to for multi-machine
/// clusters.
#[builder(default = None, setter(strip_option))]
remote_broker_addr: Option<SocketAddr>,
/// Bytes harness
#[builder(setter(strip_option))]
harness: Option<H>,
}
impl<'a, H> InMemoryBytesCoverageSugar<'a, H>
where
H: FnMut(&[u8]),
{
pub fn run(&mut self) {
let conf = self
.configuration
.take()
.unwrap_or_else(|| "default".into());
let timeout = Duration::from_secs(self.timeout.unwrap_or(DEFAULT_TIMEOUT_SECS));
let mut out_dir = self.output_dir.clone();
if fs::create_dir(&out_dir).is_err() {
println!("Out dir at {:?} already exists.", &out_dir);
if !out_dir.is_dir() {
panic!("Out dir at {:?} is not a valid directory!", &out_dir);
}
}
let mut crashes = out_dir.clone();
crashes.push("crashes");
out_dir.push("queue");
let mut harness_bytes = self.harness.take().unwrap();
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
let stats = MultiStats::new(|s| println!("{}", s));
let mut run_client = |state: Option<StdState<_, _, _, _, _>>, mut mgr| {
// 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));
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
// The state of the edges feedback.
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
// 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 objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::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 a part in memory for performance
CachedOnDiskCorpus::new(out_dir.clone(), CORPUS_CACHE_SIZE).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(crashes.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),
)
});
// Create a dictionary if not existing
if let Some(tokens_file) = &self.tokens_file {
if state.metadata().get::<Tokens>().is_none() {
state.add_metadata(Tokens::from_tokens_file(tokens_file)?);
}
}
// A minimization+queue policy to get testcasess from the corpus
let scheduler =
IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
// 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();
(harness_bytes)(buf);
ExitKind::Ok
};
// 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,
);
// In case the corpus is empty (on first run), reset
if state.corpus().count() < 1 {
if self.input_dirs.is_empty() {
// Generator of printable bytearrays of max size 32
let mut generator = RandBytesGenerator::new(32);
// Generate 8 initial inputs
state
.generate_initial_inputs(
&mut fuzzer,
&mut executor,
&mut generator,
&mut mgr,
8,
)
.expect("Failed to generate the initial corpus");
println!(
"We imported {} inputs from the generator.",
state.corpus().count()
);
} else {
println!("Loading from {:?}", &self.input_dirs);
// Load from disk
state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &self.input_dirs)
.unwrap_or_else(|_| {
panic!("Failed to load initial corpus at {:?}", &self.input_dirs)
});
println!("We imported {} inputs from disk.", state.corpus().count());
}
}
if self.tokens_file.is_some() {
// Setup a basic mutator
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
let mutational = StdMutationalStage::new(mutator);
// The order of the stages matter!
let mut stages = tuple_list!(mutational);
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
} else {
// Setup a basic mutator
let mutator = StdScheduledMutator::new(havoc_mutations());
let mutational = StdMutationalStage::new(mutator);
// The order of the stages matter!
let mut stages = tuple_list!(mutational);
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
}
Ok(())
};
let launcher = Launcher::builder()
.shmem_provider(shmem_provider)
.configuration(conf)
.stats(stats)
.run_client(&mut run_client)
.cores(&self.cores)
.broker_port(self.broker_port)
.remote_broker_addr(self.remote_broker_addr);
#[cfg(unix)]
let launcher = launcher.stdout_file(Some("/dev/null"));
launcher.build().launch().expect("Launcher failed");
}
}

12
libafl_sugar/src/lib.rs Normal file
View File

@ -0,0 +1,12 @@
//! Sugar API to simplify the life of the naibe user of `LibAFL`
pub mod inmemory;
pub use inmemory::InMemoryBytesCoverageSugar;
#[cfg(target_os = "linux")]
pub mod qemu;
#[cfg(target_os = "linux")]
pub use qemu::QemuBytesCoverageSugar;
pub const DEFAULT_TIMEOUT_SECS: u64 = 1200;
pub const CORPUS_CACHE_SIZE: usize = 4096;

244
libafl_sugar/src/qemu.rs Normal file
View File

@ -0,0 +1,244 @@
use typed_builder::TypedBuilder;
use std::{fs, net::SocketAddr, path::PathBuf, time::Duration};
use libafl::{
bolts::{
current_nanos,
launcher::Launcher,
rands::StdRand,
shmem::{ShMemProvider, StdShMemProvider},
tuples::{tuple_list, Merge},
},
corpus::{
CachedOnDiskCorpus, Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
QueueCorpusScheduler,
},
executors::{ExitKind, TimeoutExecutor},
feedback_or, feedback_or_fast,
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer},
generators::RandBytesGenerator,
inputs::{BytesInput, HasTargetBytes},
mutators::scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator},
mutators::token_mutations::Tokens,
observers::{HitcountsMapObserver, TimeObserver, VariableMapObserver},
stages::StdMutationalStage,
state::{HasCorpus, HasMetadata, StdState},
stats::MultiStats,
};
pub use libafl_qemu::emu;
use libafl_qemu::{hooks, QemuExecutor};
use crate::{CORPUS_CACHE_SIZE, DEFAULT_TIMEOUT_SECS};
#[derive(TypedBuilder)]
pub struct QemuBytesCoverageSugar<'a, H>
where
H: FnMut(&[u8]),
{
/// Laucher configuration (default is random)
#[builder(default = None, setter(strip_option))]
configuration: Option<String>,
/// Timeout of the executor
#[builder(default = None, setter(strip_option))]
timeout: Option<u64>,
/// Input directories
input_dirs: &'a [PathBuf],
/// Output directory
output_dir: PathBuf,
/// Dictionary
#[builder(default = None, setter(strip_option))]
tokens_file: Option<PathBuf>,
/// Flag if use CmpLog
//#[builder(default = false)]
//use_cmplog: bool,
#[builder(default = 1337_u16)]
broker_port: u16,
/// The list of cores to run on
cores: &'a [usize],
/// The `ip:port` address of another broker to connect our new broker to for multi-machine
/// clusters.
#[builder(default = None, setter(strip_option))]
remote_broker_addr: Option<SocketAddr>,
/// Bytes harness
#[builder(setter(strip_option))]
harness: Option<H>,
}
impl<'a, H> QemuBytesCoverageSugar<'a, H>
where
H: FnMut(&[u8]),
{
pub fn run(&mut self) {
let conf = self
.configuration
.take()
.unwrap_or_else(|| "default".into());
let timeout = Duration::from_secs(self.timeout.unwrap_or(DEFAULT_TIMEOUT_SECS));
let mut out_dir = self.output_dir.clone();
if fs::create_dir(&out_dir).is_err() {
println!("Out dir at {:?} already exists.", &out_dir);
if !out_dir.is_dir() {
panic!("Out dir at {:?} is not a valid directory!", &out_dir);
}
}
let mut crashes = out_dir.clone();
crashes.push("crashes");
out_dir.push("queue");
let mut harness_bytes = self.harness.take().unwrap();
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
let stats = MultiStats::new(|s| println!("{}", s));
let mut run_client = |state: Option<StdState<_, _, _, _, _>>, mut mgr| {
// Create an observation channel using the coverage map
let edges = unsafe { &mut hooks::EDGES_MAP };
let edges_counter = unsafe { &mut hooks::MAX_EDGES_NUM };
let edges_observer =
HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter));
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
// The state of the edges feedback.
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false),
// 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 objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::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 a part in memory for performance
CachedOnDiskCorpus::new(out_dir.clone(), CORPUS_CACHE_SIZE).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(crashes.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),
)
});
// Create a dictionary if not existing
if let Some(tokens_file) = &self.tokens_file {
if state.metadata().get::<Tokens>().is_none() {
state.add_metadata(Tokens::from_tokens_file(tokens_file)?);
}
}
// A minimization+queue policy to get testcasess from the corpus
let scheduler =
IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new());
// 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();
(harness_bytes)(buf);
ExitKind::Ok
};
let executor = QemuExecutor::new(
&mut harness,
tuple_list!(edges_observer, time_observer),
&mut fuzzer,
&mut state,
&mut mgr,
)?;
// Track edge coverage
executor.hook_edge_generation(hooks::gen_unique_edge_ids);
executor.hook_edge_execution(hooks::trace_edge_hitcount);
// 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(executor, timeout);
// In case the corpus is empty (on first run), reset
if state.corpus().count() < 1 {
if self.input_dirs.is_empty() {
// Generator of printable bytearrays of max size 32
let mut generator = RandBytesGenerator::new(32);
// Generate 8 initial inputs
state
.generate_initial_inputs(
&mut fuzzer,
&mut executor,
&mut generator,
&mut mgr,
8,
)
.expect("Failed to generate the initial corpus");
println!(
"We imported {} inputs from the generator.",
state.corpus().count()
);
} else {
println!("Loading from {:?}", &self.input_dirs);
// Load from disk
state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &self.input_dirs)
.unwrap_or_else(|_| {
panic!("Failed to load initial corpus at {:?}", &self.input_dirs)
});
println!("We imported {} inputs from disk.", state.corpus().count());
}
}
if self.tokens_file.is_some() {
// Setup a basic mutator
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
let mutational = StdMutationalStage::new(mutator);
// The order of the stages matter!
let mut stages = tuple_list!(mutational);
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
} else {
// Setup a basic mutator
let mutator = StdScheduledMutator::new(havoc_mutations());
let mutational = StdMutationalStage::new(mutator);
// The order of the stages matter!
let mut stages = tuple_list!(mutational);
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;
}
Ok(())
};
let launcher = Launcher::builder()
.shmem_provider(shmem_provider)
.configuration(conf)
.stats(stats)
.run_client(&mut run_client)
.cores(&self.cores)
.broker_port(self.broker_port)
.remote_broker_addr(self.remote_broker_addr);
#[cfg(unix)]
let launcher = launcher.stdout_file(Some("/dev/null"));
launcher.build().launch().expect("Launcher failed");
}
}