diff --git a/.gitignore b/.gitignore index 854e3f6b54..7f58fd3d22 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ vendor *.bin *.dll *.exe +*.dylib *.dSYM *.obj diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml b/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml index ae86baa7d7..19ec9783e8 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml @@ -33,7 +33,7 @@ crate-type = ["staticlib", "rlib"] [dependencies] libafl = { path = "../../libafl", default-features = false, features = ["std", "derive", "llmp_compression", "rand_trait", "regex", "errors_backtrace", "serdeany_autoreg", "tui_monitor", "unicode"] } libafl_bolts = { path = "../../libafl_bolts", default-features = false, features = ["std", "derive", "llmp_compression", "rand_trait", "serdeany_autoreg", "errors_backtrace"] } -libafl_targets = { path = "../../libafl_targets", features = ["sancov_8bit", "sancov_cmplog", "sancov_pcguard", "libfuzzer", "libfuzzer_oom", "libfuzzer_define_run_driver", "libfuzzer_interceptors", "sanitizers_flags", "whole_archive", "sanitizer_interfaces"] } +libafl_targets = { path = "../../libafl_targets", features = ["sancov_8bit", "sancov_cmplog", "sancov_value_profile", "sancov_pcguard", "libfuzzer", "libfuzzer_oom", "libfuzzer_define_run_driver", "libfuzzer_interceptors", "sanitizers_flags", "whole_archive", "sanitizer_interfaces"] } ahash = { version = "0.8.3", default-features = false } libc = "0.2.1" diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/build.sh b/libafl_libfuzzer/libafl_libfuzzer_runtime/build.sh index 5114009450..a7a41ec663 100755 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/build.sh +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/build.sh @@ -17,28 +17,37 @@ if ! cargo +nightly --version >& /dev/null; then exit 1 fi -RUSTC_BIN="$(cargo +nightly rustc -Zunstable-options --print target-libdir)/../bin" -RUST_LLD="${RUSTC_BIN}/rust-lld" -RUST_AR="${RUSTC_BIN}/llvm-ar" - -if ! [ -f "${RUST_LLD}" ] && [ -f "${RUST_AR}" ]; then - echo -e "You must install the llvm-tools component: \`rustup component add llvm-tools'" - exit 1 -fi - cargo +nightly build --profile "$profile" -tmpdir="" +if [[ "$OSTYPE" == "darwin"* ]]; then + # MacOS and iOS + "${CXX:-clang++}" -dynamiclib -Wl,-force_load target/release/libafl_libfuzzer_runtime.a \ + -Wl,-U,_LLVMFuzzerInitialize -Wl,-U,_LLVMFuzzerCustomMutator -Wl,-U,_LLVMFuzzerCustomCrossOver -Wl,-U,_libafl_main \ + -o libafl_libfuzzer_runtime.dylib +else + # Linux and *BSD + RUSTC_BIN="$(cargo +nightly rustc -Zunstable-options --print target-libdir)/../bin" + RUST_LLD="${RUSTC_BIN}/rust-lld" + RUST_AR="${RUSTC_BIN}/llvm-ar" -cleanup() { - rm -rf "${tmpdir}" - exit -} -trap cleanup INT TERM + if ! [ -f "${RUST_LLD}" ] && [ -f "${RUST_AR}" ]; then + echo -e "You must install the llvm-tools component: \`rustup component add llvm-tools'" + exit 1 + fi -tmpdir="$(mktemp -d)" -"${RUST_LLD}" -flavor gnu -r --whole-archive target/release/libafl_libfuzzer_runtime.a -o "${tmpdir}/libFuzzer.o" -"${RUST_AR}" cr libFuzzer.a "${tmpdir}/libFuzzer.o" + tmpdir="" + + cleanup() { + rm -rf "${tmpdir}" + exit + } + trap cleanup INT TERM + + tmpdir="$(mktemp -d)" + "${RUST_LLD}" -flavor gnu -r --whole-archive target/release/libafl_libfuzzer_runtime.a -o "${tmpdir}/libFuzzer.o" + "${RUST_AR}" cr libFuzzer.a "${tmpdir}/libFuzzer.o" + + echo "Done! Wrote the runtime to \`${SCRIPT_DIR}/libFuzzer.a'" + cleanup +fi -echo "Done! Wrote the runtime to \`${SCRIPT_DIR}/libFuzzer.a'" -cleanup diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs index 54aca0134d..355e34b953 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/lib.rs @@ -139,7 +139,7 @@ impl CustomMutationStatus { } macro_rules! fuzz_with { - ($options:ident, $harness:ident, $operation:expr, $and_then:expr, $edge_maker:expr) => {{ + ($options:ident, $harness:ident, $operation:expr, $and_then:expr, $edge_maker:expr, $extra_feedback:expr, $extra_obsv:expr) => {{ use libafl_bolts::{ rands::StdRand, tuples::{Merge, tuple_list}, @@ -169,7 +169,7 @@ macro_rules! fuzz_with { state::{HasCorpus, StdState}, StdFuzzer, }; - use libafl_targets::{CmpLogObserver, LLVMCustomMutator, OomFeedback, OomObserver}; + use libafl_targets::{CmpLogObserver, LLVMCustomMutator, OomFeedback, OomObserver, CMP_MAP}; use rand::{thread_rng, RngCore}; use std::{env::temp_dir, fs::create_dir, path::PathBuf}; @@ -203,6 +203,9 @@ macro_rules! fuzz_with { // Create the Cmp observer let cmplog_observer = CmpLogObserver::new("cmplog", true); + // Create an observer using the cmp map for value profile + let value_profile_observer = unsafe { StdMapObserver::from_mut_ptr("cmps", CMP_MAP.as_mut_ptr(), CMP_MAP.len()) }; + // Create a stacktrace observer let backtrace_observer = BacktraceObserver::owned( "BacktraceObserver", @@ -213,14 +216,27 @@ macro_rules! fuzz_with { let map_feedback = MaxMapFeedback::new(&edges_observer); let shrinking_map_feedback = ShrinkMapFeedback::new(&size_edges_observer); + // Value profile maximization feedback + let value_profile_feedback = MaxMapFeedback::new(&value_profile_observer); + // Set up a generalization stage for grimoire let generalization = GeneralizationStage::new(&edges_observer); let generalization = IfStage::new(|_, _, _, _| Ok(grimoire.into()), tuple_list!(generalization)); let calibration = CalibrationStage::new(&map_feedback); + let add_extra_feedback = $extra_feedback; + let coverage_feedback = add_extra_feedback( + feedback_or!( + map_feedback, + feedback_and_fast!(ConstFeedback::new($options.shrink()), shrinking_map_feedback), + // Time feedback, this one does not need a feedback state + TimeFeedback::new(&time_observer) + ), + value_profile_feedback + ); + // Feedback to rate the interestingness of an input - // This one is composed by two Feedbacks in OR let mut feedback = feedback_and_fast!( feedback_not!( feedback_or_fast!( @@ -230,12 +246,7 @@ macro_rules! fuzz_with { ) ), keep_observer, - feedback_or!( - map_feedback, - feedback_and_fast!(ConstFeedback::new($options.shrink()), shrinking_map_feedback), - // Time feedback, this one does not need a feedback state - TimeFeedback::new(&time_observer) - ) + coverage_feedback ); // A feedback to choose if an input is a solution or not @@ -424,10 +435,16 @@ macro_rules! fuzz_with { let mut tracing_harness = harness; + let add_extra_observer = $extra_obsv; + let observers = add_extra_observer( + tuple_list!(edges_observer, size_edges_observer, time_observer, backtrace_observer, oom_observer), + value_profile_observer + ); + // Create the executor for an in-process function with one observer for edge coverage and one for the execution time let mut executor = InProcessExecutor::with_timeout( &mut harness, - tuple_list!(edges_observer, size_edges_observer, time_observer, backtrace_observer, oom_observer), + observers, &mut fuzzer, &mut state, &mut mgr, @@ -466,7 +483,6 @@ macro_rules! fuzz_with { } } - // Setup a tracing stage in which we log comparisons let tracing = IfStage::new(|_, _, _, _| Ok(!$options.skip_tracing()), (TracingStage::new(InProcessExecutor::new( &mut tracing_harness, @@ -500,6 +516,21 @@ macro_rules! fuzz_with { $and_then(closure) }}; + ($options:ident, $harness:ident, $operation:expr, $and_then:expr, $edge_maker:expr) => {{ + if $options.use_value_profile() { + fuzz_with!($options, $harness, $operation, $and_then, $edge_maker, + |feedback, value_profile_feedback| { + feedback_or!(feedback, value_profile_feedback) + }, + |observers, value_profile_observer| { + (value_profile_observer, observers) // Prepend the value profile observer in the tuple list + } + ) + } else { + fuzz_with!($options, $harness, $operation, $and_then, $edge_maker, |feedback, _| feedback, |observers, _| observers) + } + }}; + ($options:ident, $harness:ident, $operation:expr, $and_then:expr) => {{ use libafl::observers::{ HitcountsIterableMapObserver, HitcountsMapObserver, MultiMapObserver, StdMapObserver, diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/options.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/options.rs index 98d9172234..82ff3741d8 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/options.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/options.rs @@ -107,6 +107,7 @@ pub struct LibfuzzerOptions { artifact_prefix: ArtifactPrefix, timeout: Duration, grimoire: Option, + use_value_profile: bool, unicode: bool, forks: Option, dict: Option, @@ -163,6 +164,10 @@ impl LibfuzzerOptions { self.grimoire } + pub fn use_value_profile(&self) -> bool { + self.use_value_profile + } + pub fn unicode(&self) -> bool { self.unicode } @@ -235,6 +240,7 @@ struct LibfuzzerOptionsBuilder<'a> { artifact_prefix: Option<&'a str>, timeout: Option, grimoire: Option, + use_value_profile: Option, unicode: Option, forks: Option, dict: Option<&'a str>, @@ -298,6 +304,9 @@ impl<'a> LibfuzzerOptionsBuilder<'a> { } } "grimoire" => self.grimoire = Some(parse_or_bail!(name, value, u64) > 0), + "use_value_profile" => { + self.use_value_profile = Some(parse_or_bail!(name, value, u64) > 0); + } "unicode" => self.unicode = Some(parse_or_bail!(name, value, u64) > 0), "artifact_prefix" => { self.artifact_prefix = Some(value); @@ -371,6 +380,7 @@ impl<'a> LibfuzzerOptionsBuilder<'a> { .unwrap_or_default(), timeout: self.timeout.unwrap_or(Duration::from_secs(1200)), grimoire: self.grimoire, + use_value_profile: self.use_value_profile.unwrap_or(false), unicode: self.unicode.unwrap_or(true), forks: self.forks, dict: self.dict.map(|path| {