fuzzbench harness (#165)

* starting to build fuzzbench harness

* fuzzbench updated

* fuzzbench example readme

* removed dummy files

* Intial de-luxe dockerfile added

* added to dockerignore

* more fuzzbench

* dockerfile

* final dockerfile fun

* fuzzing fixes, switched rand, build fixes

* fmt

* added dummy fuzzone

* silence wrapper output

* clippy

* logfile fixes

* adopt changes to libafl-cc

* various fixes
This commit is contained in:
Dominik Maier 2021-06-16 18:24:07 +02:00 committed by GitHub
parent 1faadec106
commit dea21da5c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 606 additions and 171 deletions

View File

@ -25,3 +25,7 @@ test.dict
# Ignore all built fuzzers
fuzzer_*
AFLplusplus
# Ignore common dummy and logfiles
*.log
a

4
.gitignore vendored
View File

@ -25,3 +25,7 @@ test.dict
# Ignore all built fuzzers
fuzzer_*
AFLplusplus
# Ignore common dummy and logfiles
*.log
a

View File

@ -1,79 +1,58 @@
#
# This Dockerfile for LibAFL uses rust:bullseye as base.
#
# syntax=docker/dockerfile:1.2
FROM rust:bullseye AS libafl
LABEL "maintainer"="afl++ team <afl@aflplus.plus>"
LABEL "about"="AFLplusplus docker image"
LABEL "about"="LibAFL Docker image"
ARG DEBIAN_FRONTEND=noninteractive
# install sccache to cache subsequent builds of dependencies
RUN cargo install sccache
RUN sh -c 'echo set encoding=utf-8 > /root/.vimrc' \
echo "export PS1='"'[LibAFL \h] \w$(__git_ps1) \$ '"'" >> ~/.bashrc
ENV HOME=/root
ENV SCCACHE_CACHE_SIZE="1G"
ENV SCCACHE_DIR=$HOME/.cache/sccache
ENV RUSTC_WRAPPER="/usr/local/cargo/bin/sccache"
ENV IS_DOCKER="1"
RUN sh -c 'echo set encoding=utf-8 > /root/.vimrc' \
echo "export PS1='"'[LibAFL \h] \w$(__git_ps1) \$ '"'" >> ~/.bashrc && \
mkdir ~/.cargo && \
echo "[build]\nrustc-wrapper = \"${RUSTC_WRAPPER}\"" >> ~/.cargo/config
RUN rustup component add rustfmt clippy
# Install clang 11, common build tools
RUN apt update && apt install -y build-essential gdb git wget clang clang-tools libc++-11-dev libc++abi-11-dev
# Copy a dummy.rs and Cargo.toml first, so that dependencies are cached
WORKDIR /libafl
COPY Cargo.toml Cargo.toml
COPY README.md README.md
COPY Cargo.toml README.md ./
COPY libafl_derive/Cargo.toml libafl_derive/Cargo.toml
COPY scripts/dummy.rs libafl_derive/src/lib.rs
COPY libafl/Cargo.toml libafl/Cargo.toml
COPY libafl/build.rs libafl/build.rs
COPY libafl/Cargo.toml libafl/build.rs libafl/
COPY libafl/benches libafl/benches
COPY libafl/examples libafl/examples
COPY scripts/dummy.rs libafl/src/lib.rs
COPY libafl_frida/Cargo.toml libafl_frida/Cargo.toml
COPY libafl_frida/build.rs libafl_frida/build.rs
COPY libafl_frida/Cargo.toml libafl_frida/build.rs libafl_frida/
COPY scripts/dummy.rs libafl_frida/src/lib.rs
COPY libafl_frida/src/gettls.c libafl_frida/src/gettls.c
COPY libafl_cc/Cargo.toml libafl_cc/Cargo.toml
COPY scripts/dummy.rs libafl_cc/src/lib.rs
COPY libafl_targets/Cargo.toml libafl_targets/Cargo.toml
COPY libafl_targets/build.rs libafl_targets/build.rs
COPY libafl_targets/Cargo.toml libafl_targets/build.rs libafl_targets/
COPY libafl_targets/src libafl_targets/src
COPY scripts/dummy.rs libafl_targets/src/lib.rs
COPY libafl_tests/Cargo.toml libafl_tests/Cargo.toml
COPY libafl_tests/build.rs libafl_tests/build.rs
COPY libafl_tests/Cargo.toml libafl_tests/build.rs libafl_tests/
COPY scripts/dummy.rs libafl_tests/src/lib.rs
RUN cargo build && cargo build --release
COPY scripts scripts
COPY docs docs
RUN cargo build && cargo build --release
# Pre-build dependencies for a few common fuzzers
COPY fuzzers/baby_fuzzer/Cargo.toml fuzzers/baby_fuzzer/Cargo.toml
COPY fuzzers/baby_fuzzer/README.md fuzzers/baby_fuzzer/README.md
COPY scripts/dummy.rs fuzzers/baby_fuzzer/src/main.rs
WORKDIR /libafl/fuzzers/baby_fuzzer
RUN cargo build && cargo build --release
WORKDIR /libafl
COPY fuzzers/forkserver_simple/Cargo.toml fuzzers/forkserver_simple/Cargo.toml
COPY fuzzers/forkserver_simple/README.md fuzzers/forkserver_simple/README.md
COPY scripts/dummy.rs fuzzers/forkserver_simple/src/main.rs
WORKDIR /libafl/fuzzers/forkserver_simple
RUN cargo build && cargo build --release
WORKDIR /libafl
COPY fuzzers/frida_libpng/Cargo.toml fuzzers/frida_libpng/Cargo.toml
COPY fuzzers/frida_libpng/README.md fuzzers/frida_libpng/README.md
COPY fuzzers/frida_libpng/build.rs fuzzers/frida_libpng/build.rs
COPY scripts/dummy.rs fuzzers/frida_libpng/src/main.rs
WORKDIR /libafl/fuzzers/frida_libpng
RUN cargo build && cargo build --release
WORKDIR /libafl
COPY fuzzers/generic_inmemory/Cargo.toml fuzzers/generic_inmemory/Cargo.toml
COPY fuzzers/generic_inmemory/README.md fuzzers/generic_inmemory/README.md
COPY scripts/dummy.rs fuzzers/generic_inmemory/src/main.rs
WORKDIR /libafl/fuzzers/generic_inmemory
RUN cargo build && cargo build --release
WORKDIR /libafl
# Dep chain:
# libafl_cc (independent)
@ -84,42 +63,22 @@ WORKDIR /libafl
# Build once without source
COPY libafl_cc/src libafl_cc/src
RUN touch libafl_cc/src/lib.rs && cargo build && cargo build --release
RUN touch libafl_cc/src/lib.rs
COPY libafl_derive/src libafl_derive/src
RUN touch libafl_derive/src/lib.rs && cargo build && cargo build --release
RUN touch libafl_derive/src/lib.rs
COPY libafl_tests/src libafl_tests/src
RUN touch libafl_tests/src/lib.rs && cargo build && cargo build --release
RUN touch libafl_tests/src/lib.rs
COPY libafl/src libafl/src
RUN touch libafl/src/lib.rs && cargo build && cargo build --release
RUN touch libafl/src/lib.rs
COPY libafl_targets/src libafl_targets/src
RUN touch libafl_targets/src/lib.rs && cargo build && cargo build --release
RUN touch libafl_targets/src/lib.rs
COPY libafl_frida/src libafl_frida/src
RUN touch libafl_frida/src/lib.rs && cargo build && cargo build --release
RUN touch libafl_frida/src/lib.rs
RUN cargo build && cargo build --release
# Copy fuzzers over
COPY fuzzers/baby_fuzzer/src fuzzers/baby_fuzzer/src/src
RUN touch fuzzers/baby_fuzzer/src/main.rs
COPY fuzzers/forkserver_simple/corpus fuzzers/forkserver_simple/corpus
COPY fuzzers/forkserver_simple/src fuzzers/forkserver_simple/src
RUN touch fuzzers/forkserver_simple/src/main.rs
COPY fuzzers/frida_libpng/src fuzzers/frida_libpng/src
COPY fuzzers/frida_libpng/harness.cc fuzzers/frida_libpng/harness.cc
RUN touch fuzzers/frida_libpng/src/main.rs
COPY fuzzers/generic_inmemory/src fuzzers/generic_inmemory/src
COPY fuzzers/generic_inmemory/fuzz.c fuzzers/generic_inmemory/fuzz.c
RUN touch fuzzers/frida_libpng/src/main.rs
COPY fuzzers/libfuzzer_libmozjpeg fuzzers/libfuzzer_libmozjpg
COPY fuzzers/libfuzzer_libpng fuzzers/libfuzzer_libpng
COPY fuzzers/libfuzzer_libpng_launcher fuzzers/libfuzzer_libpng_launcher
COPY fuzzers/libfuzzer_reachability fuzzers/libfuzzer_reachability
COPY fuzzers/libfuzzer_stb_image fuzzers/libfuzzer_stb_image
#RUN ./scripts/build_all_fuzzers.sh
COPY fuzzers fuzzers
# RUN ./scripts/build_all_fuzzers.sh --no-fmt
ENTRYPOINT [ "/bin/bash" ]

1
fuzzers/fuzzbench/.gitignore vendored Normal file
View File

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

View File

@ -0,0 +1,44 @@
[package]
name = "fuzzbench"
version = "0.3.2"
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
[build-dependencies]
cc = { version = "1.0", features = ["parallel"] }
which = { version = "4.0.2" }
num_cpus = "1.0"
[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.0.0-beta.2", features = ["default"] }
nix = "0.20.0"
[lib]
name = "fuzzbench"
crate-type = ["staticlib"]
[[bin]]
# For c binaries
name = "libafl_cc"
path = "src/bin/libafl_cc.rs"
[[bin]]
# For cpp binaries
name = "libafl_cxx"
path = "src/bin/libafl_cc.rs"

View File

@ -0,0 +1,17 @@
# 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).

View File

@ -0,0 +1,9 @@
// build.rs
fn main() {
cc::Build::new()
.file("src/libafl_wrapper.c")
.compile("libafl_sys.a");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src/libafl_wrapper.c");
}

15
fuzzers/fuzzbench/fuzz.c Normal file
View File

@ -0,0 +1,15 @@
#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);
}*/

View File

@ -0,0 +1,34 @@
use libafl_cc::{ClangWrapper, CompilerWrapper};
use std::env;
fn main() {
let 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();
let mut cc = ClangWrapper::new("clang", if is_cpp { "clang++" } else { "clang" });
cc.is_cpp(is_cpp)
.from_args(&args)
.unwrap()
.link_staticlib(&dir, "fuzzbench".into())
.unwrap()
.add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp".into())
.unwrap()
// silence the compiler wrapper output, needed for some configure scripts.
.silence()
.run()
.unwrap();
} else {
panic!("LibAFL CC: No Arguments given");
}
}

View File

@ -0,0 +1,314 @@
//! A singlethreade libfuzzer-like fuzzer that can auto-restart.
use clap::{App, Arg};
use core::{cell::RefCell, time::Duration};
#[cfg(unix)]
use nix::{self, unistd::dup};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::{
env,
fs::{File, OpenOptions},
io,
io::Write,
path::PathBuf,
};
use libafl::{
bolts::{
current_nanos, current_time,
os::dup2,
rands::StdRand,
shmem::{ShMemProvider, StdShMemProvider},
tuples::{tuple_list, Merge},
},
corpus::{Corpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus, QueueCorpusScheduler},
events::SimpleRestartingEventManager,
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
feedback_or,
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes},
mutators::{
scheduled::{havoc_mutations, StdScheduledMutator},
token_mutations::I2SRandReplace,
tokens_mutations, Tokens,
},
observers::{StdMapObserver, TimeObserver},
stages::{StdMutationalStage, TracingStage},
state::{HasCorpus, HasMetadata, StdState},
stats::SimpleStats,
Error,
};
use libafl_targets::{
libfuzzer_initialize, libfuzzer_test_one_input, CmpLogObserver, CMPLOG_MAP, EDGES_MAP,
MAX_EDGES_NUM,
};
/// The fuzzer main (as `no_mangle` c function)
#[no_mangle]
pub extern "C" fn fuzzer_main() {
// Registry the metadata types used in this fuzzer
// Needed only on no_std
//RegistryBuilder::register::<Tokens>();
let res = App::new("libafl_fuzzbench")
.version("0.4.0")
.author("AFLplusplus team")
.about("LibAFL-based fuzzer for Fuzzbench")
.arg(
Arg::new("out")
.about("The directory to place finds in ('corpus')")
.required(true)
.index(1)
.takes_value(true),
)
.arg(
Arg::new("in")
.about("The directory to read initial inputs from ('seeds')")
.required(true)
.index(2)
.takes_value(true),
)
.arg(
Arg::new("tokens")
.short('x')
.long("tokens")
.about("A file to read tokens from, to be used during fuzzing")
.takes_value(true),
)
.arg(
Arg::new("logfile")
.short('l')
.long("logfile")
.about("Duplicates all output to this file")
.default_value("libafl.log"),
)
.arg(
Arg::new("timeout")
.short('t')
.long("timeout")
.about("Timeout for each individual execution, in milliseconds")
.default_value("1000"),
)
.get_matches();
println!(
"Workdir: {:?}",
env::current_dir().unwrap().to_string_lossy().to_string()
);
// For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir.
let mut corpus = PathBuf::from(res.value_of("corpus").unwrap().to_string());
let mut crashes = corpus.clone();
crashes.push("crashes");
corpus.push("queue");
let seeds = PathBuf::from(res.value_of("seeds").unwrap().to_string());
let tokens = res.value_of("tokens").map(PathBuf::from);
let logfile = PathBuf::from(res.value_of("logfile").unwrap().to_string());
let timeout = Duration::from_millis(
res.value_of("timeout")
.unwrap()
.to_string()
.parse()
.expect("Could not parse timeout in milliseconds"),
);
fuzz(corpus, crashes, seeds, tokens, logfile, timeout)
.expect("An error occurred while fuzzing");
}
/// 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 stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::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.
#[cfg(target_os = "android")]
AshmemService::start().expect("Failed to start Ashmem service");
let mut shmem_provider = StdShMemProvider::new()?;
let (state, mut mgr) = match SimpleRestartingEventManager::launch(stats, &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 = 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);
// 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!(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 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.
// They are the data related to the feedbacks that you want to persist in the State.
tuple_list!(feedback_state),
)
});
println!("Let's fuzz :)");
// The actual target run starts here.
// Call LLVMFUzzerInitialize() if present.
// let args: Vec<String> = env::args().collect();
let args = [];
if libfuzzer_initialize(&args) == -1 {
println!("Warning: LLVMFuzzerInitialize failed with -1")
}
// 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();
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,
));
// Setup a randomic Input2State stage
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
// 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!(tracing, i2s, mutational);
// Read tokens
if let Some(tokenfile) = tokenfile {
if state.metadata().get::<Tokens>().is_none() {
state.add_metadata(Tokens::from_tokens_file(tokenfile)?);
}
}
// 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(|_| panic!("Failed to load initial corpus at {:?}", &seed_dir));
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(())
}

View File

@ -0,0 +1,24 @@
// We only want to link our fuzzer main, if the target doesn't specify its own main - hence we define `main` as `weak` in this file.
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
// jump to rust
void fuzzer_main();
// Link in a dummy llvm test to non-fuzzing builds, for configure et al.
int __attribute__((weak)) LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) {
(void) buf;
(void) len;
fprintf(stderr, "LibAFL - No LLVMFuzzerTestOneInput function found! Linker error?\n");
fflush(stderr);
abort();
}
int __attribute__((weak)) main(int argc, char *argv[]) {
(void) argc;
(void) argv;
fuzzer_main();
return 0;
}

View File

@ -33,9 +33,10 @@ args:
about: "Set the execution timeout in milliseconds, default 10000"
value_name: TIMEOUT
takes_value: true
- dict:
long: dict
about: "Feed the fuzzer with an user-specified dictionary of tokens"
- 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

@ -66,8 +66,8 @@ pub fn main() {
.value_of("output")
.map(|s| PathBuf::from(s))
.unwrap_or(workdir.clone());
let dicts: Vec<&str> = matches
.values_of("dict")
let token_files: Vec<&str> = matches
.values_of("tokens")
.map(|v| v.collect())
.unwrap_or(vec![]);
let timeout_ms = matches
@ -129,8 +129,8 @@ pub fn main() {
// Create a PNG dictionary if not existing
if state.metadata().get::<Tokens>().is_none() {
for dict in &dicts {
state.add_metadata(Tokens::from_tokens_file(dict)?);
for tokens_file in &token_files {
state.add_metadata(Tokens::from_tokens_file(tokens_file)?);
}
}

View File

@ -13,7 +13,7 @@ const HASH_CONST: u64 = 0xa5b35705;
/// The standard rand implementation for `LibAFL`.
/// It is usually the right choice, with very good speed and a reasonable randomness.
/// Not cryptographically secure (which is not what you want during fuzzing ;) )
pub type StdRand = RomuTrioRand;
pub type StdRand = RomuDuoJrRand;
/// Ways to get random around here
/// Please note that these are not cryptographically secure

View File

@ -8,8 +8,6 @@ use core::{
};
#[cfg(feature = "std")]
use serde::{de::DeserializeOwned, Serialize};
#[cfg(feature = "std")]
use typed_builder::TypedBuilder;
#[cfg(all(feature = "std", windows))]
use crate::bolts::os::startable_self;
@ -203,7 +201,6 @@ where
/// `restarter` will start a new process each time the child crashes or times out.
#[cfg(feature = "std")]
#[allow(clippy::default_trait_access)]
#[derive(TypedBuilder, Debug)]
pub struct SimpleRestartingEventManager<I, S, SP, ST>
where
I: Input,
@ -213,16 +210,9 @@ where
{
/// The actual simple event mgr
simple_event_mgr: SimpleEventManager<I, ST>,
/// The shared memory provider to use for the broker or client spawned by the restarting
/// manager.
shmem_provider: SP,
/// The stats to use
#[builder(setter(strip_option))]
stats: Option<ST>,
/// [`LlmpSender`] for restarts
sender: LlmpSender<SP>,
/// Phantom data
#[builder(setter(skip), default = PhantomData {})]
_phantom: PhantomData<(I, S)>,
}
@ -281,26 +271,6 @@ where
{
}
#[cfg(feature = "std")]
impl<I, S, SP, ST> SimpleRestartingEventManager<I, S, SP, ST>
where
I: Input,
S: Serialize,
SP: ShMemProvider,
ST: Stats, //TODO CE: CustomEvent,
{
/// Creates a new [`SimpleEventManager`].
pub fn new(stats: ST, sender: LlmpSender<SP>, shmem_provider: SP) -> Self {
Self {
stats: None,
sender,
simple_event_mgr: SimpleEventManager::new(stats),
shmem_provider,
_phantom: PhantomData {},
}
}
}
#[cfg(feature = "std")]
#[allow(clippy::type_complexity, clippy::too_many_lines)]
impl<I, S, SP, ST> SimpleRestartingEventManager<I, S, SP, ST>
@ -308,24 +278,32 @@ where
I: Input,
S: DeserializeOwned + Serialize,
SP: ShMemProvider,
ST: Stats + Clone,
ST: Stats, //TODO CE: CustomEvent,
{
/// Launch the restarting manager
/// Creates a new [`SimpleEventManager`].
fn new_launched(stats: ST, sender: LlmpSender<SP>) -> Self {
Self {
sender,
simple_event_mgr: SimpleEventManager::new(stats),
_phantom: PhantomData {},
}
}
/// Launch the simple restarting manager.
/// This [`EventManager`] is simple and single threaded,
/// but can still used shared maps to recover from crashes and timeouts.
#[allow(clippy::similar_names)]
pub fn launch(
&mut self,
stats: ST,
shmem_provider: &mut SP,
) -> Result<(Option<S>, SimpleRestartingEventManager<I, S, SP, ST>), Error> {
// We start ourself as child process to actually fuzz
let (mut sender, mut receiver, new_shmem_provider) = if std::env::var(_ENV_FUZZER_SENDER)
.is_err()
{
let (mut sender, mut receiver) = if std::env::var(_ENV_FUZZER_SENDER).is_err() {
// First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts.
let sender = { LlmpSender::new(self.shmem_provider.clone(), 0, false)? };
let sender = { LlmpSender::new(shmem_provider.clone(), 0, false)? };
let map = {
self.shmem_provider
.clone_ref(&sender.out_maps.last().unwrap().shmem)?
};
let receiver = LlmpReceiver::on_existing_map(self.shmem_provider.clone(), map, None)?;
let map = { shmem_provider.clone_ref(&sender.out_maps.last().unwrap().shmem)? };
let receiver = LlmpReceiver::on_existing_map(shmem_provider.clone(), map, None)?;
// Store the information to a map.
sender.to_env(_ENV_FUZZER_SENDER)?;
receiver.to_env(_ENV_FUZZER_RECEIVER)?;
@ -338,15 +316,15 @@ where
// On Unix, we fork
#[cfg(unix)]
let child_status = {
self.shmem_provider.pre_fork()?;
shmem_provider.pre_fork()?;
match unsafe { fork() }? {
ForkResult::Parent(handle) => {
self.shmem_provider.post_fork(false)?;
shmem_provider.post_fork(false)?;
handle.status()
}
ForkResult::Child => {
self.shmem_provider.post_fork(true)?;
break (sender, receiver, self.shmem_provider.clone());
shmem_provider.post_fork(true)?;
break (sender, receiver);
}
}
};
@ -376,12 +354,8 @@ where
// We get here *only on Windows*, if we were started by a restarting fuzzer.
// A sender and a receiver for single communication
(
LlmpSender::on_existing_from_env(self.shmem_provider.clone(), _ENV_FUZZER_SENDER)?,
LlmpReceiver::on_existing_from_env(
self.shmem_provider.clone(),
_ENV_FUZZER_RECEIVER,
)?,
self.shmem_provider.clone(),
LlmpSender::on_existing_from_env(shmem_provider.clone(), _ENV_FUZZER_SENDER)?,
LlmpReceiver::on_existing_from_env(shmem_provider.clone(), _ENV_FUZZER_RECEIVER)?,
)
};
@ -394,11 +368,7 @@ where
// Mgr to send and receive msgs from/to all other fuzzer instances
(
None,
SimpleRestartingEventManager::new(
self.stats.take().unwrap(),
sender,
new_shmem_provider,
),
SimpleRestartingEventManager::new_launched(stats, sender),
)
}
// Restoring from a previous run, deserialize state and corpus.
@ -412,11 +382,7 @@ where
(
Some(state),
SimpleRestartingEventManager::new(
self.stats.take().unwrap(),
sender,
new_shmem_provider,
),
SimpleRestartingEventManager::new_launched(stats, sender),
)
}
};

View File

@ -206,7 +206,7 @@ impl Forkserver {
Ok(_) => {}
Err(err) => {
return Err(Error::Forkserver(format!(
"Could not spawn forkserver: {:#?}",
"Could not spawn the forkserver: {:#?}",
err
)));
}

View File

@ -460,7 +460,8 @@ where
stats_timeout: Duration,
) -> Result<Duration, Error> {
let cur = current_time();
if cur - last > stats_timeout {
// default to 0 here to avoid crashes on clock skew
if cur.checked_sub(last).unwrap_or_default() > stats_timeout {
// Default no introspection implmentation
#[cfg(not(feature = "introspection"))]
manager.fire(

View File

@ -19,6 +19,12 @@ use crate::bolts::current_time;
const CLIENT_STATS_TIME_WINDOW_SECS: u64 = 5; // 5 seconds
/// Number of stages in the fuzzer
pub(crate) const NUM_STAGES: usize = 8;
/// Number of feedback mechanisms to measure for performance
pub(crate) const NUM_FEEDBACKS: usize = 16;
/// User-defined stats types
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum UserStats {
@ -224,6 +230,7 @@ impl Stats for NopStats {
impl NopStats {
/// Create new [`NopStats`]
#[must_use]
pub fn new() -> Self {
Self {
start_time: current_time(),
@ -350,12 +357,6 @@ macro_rules! mark_feedback_time {
}};
}
/// Number of stages in the fuzzer
pub(crate) const NUM_STAGES: usize = 8;
/// Number of feedback mechanisms to measure for performance
pub(crate) const NUM_FEEDBACKS: usize = 16;
/// Client performance statistics
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub struct ClientPerfStats {
@ -770,6 +771,7 @@ impl core::fmt::Display for ClientPerfStats {
#[cfg(feature = "introspection")]
impl Default for ClientPerfStats {
#[must_use]
fn default() -> Self {
Self::new()
}

View File

@ -51,10 +51,19 @@ pub trait CompilerWrapper {
/// Get if in linking mode
fn is_linking(&self) -> bool;
/// Silences `libafl_cc` output
fn silence(&mut self) -> &'_ mut Self;
/// Returns `true` if `silence` was called
fn is_silent(&self) -> bool;
/// Run the compiler
fn run(&mut self) -> Result<(), Error> {
let args = self.command()?;
if !self.is_silent() {
dbg!(&args);
}
if args.is_empty() {
return Err(Error::InvalidArguments(
"The number of arguments cannot be 0".into(),
@ -64,7 +73,9 @@ pub trait CompilerWrapper {
Ok(s) => s,
Err(e) => return Err(Error::Io(e)),
};
if !self.is_silent() {
dbg!(status);
}
Ok(())
}
}
@ -72,6 +83,7 @@ pub trait CompilerWrapper {
/// Wrap Clang
#[allow(clippy::struct_excessive_bools)]
pub struct ClangWrapper {
is_silent: bool,
optimize: bool,
wrapped_cc: String,
wrapped_cxx: String,
@ -167,13 +179,21 @@ impl CompilerWrapper for ClangWrapper {
}
fn link_staticlib(&mut self, dir: &Path, name: String) -> Result<&'_ mut Self, Error> {
self.add_link_arg("-Wl,--whole-archive".into())?
.add_link_arg(
if cfg!(any(target_os = "macos", target_os = "ios")) {
//self.add_link_arg("-force_load".into())?;
} else {
self.add_link_arg("-Wl,--whole-archive".into())?;
}
self.add_link_arg(
dir.join(format!("{}{}.{}", LIB_PREFIX, name, LIB_EXT))
.display()
.to_string(),
)?
.add_link_arg("-Wl,-no-whole-archive".into())
)?;
if cfg!(any(target_os = "macos", target_os = "ios")) {
Ok(self)
} else {
self.add_link_arg("-Wl,-no-whole-archive".into())
}
}
fn command(&mut self) -> Result<Vec<String>, Error> {
@ -201,6 +221,15 @@ impl CompilerWrapper for ClangWrapper {
fn is_linking(&self) -> bool {
self.linking
}
fn silence(&mut self) -> &'_ mut Self {
self.is_silent = true;
self
}
fn is_silent(&self) -> bool {
self.is_silent
}
}
impl ClangWrapper {
@ -219,6 +248,7 @@ impl ClangWrapper {
base_args: vec![],
cc_args: vec![],
link_args: vec![],
is_silent: false,
}
}

View File

@ -1,4 +1,5 @@
#!/bin/bash
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
cd "$SCRIPT_DIR/.."
@ -8,11 +9,20 @@ cd fuzzers
for fuzzer in *;
do
echo "[+] Checking fmt and building $fuzzer"
cd $fuzzer \
&& cargo fmt --all -- --check \
&& cargo clippy \
&& cargo build \
&& cd .. \
|| exit 1
cd $fuzzer
# Clippy checks
if [ "$1" != "--no-fmt" ]; then
echo "[*] Checking fmt for $fuzzer"
cargo fmt --all -- --check || exit 1
echo "[*] Running clippy for $fuzzer"
cargo clippy || exit 1
else
echo "[+] Skipping fmt and clippy for $fuzzer (--no-fmt specified)"
fi
echo "[*] Building $fuzzer"
cargo build || exit 1
cd ..
echo "[+] Done building $fuzzer"
echo ""
done