diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index be3f9b9aca..929eb2160e 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -168,13 +168,26 @@ jobs: ubuntu-check: runs-on: ubuntu-22.04 steps: - - name: Remove Dotnet & Haskell - run: rm -rf /usr/share/dotnet && rm -rf /opt/ghc - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable - components: llvm-tools + components: llvm-tools + - name: Free Disk Space (Ubuntu) + uses: jlumbroso/free-disk-space@main + with: + # this might remove tools that are actually needed, + # if set to "true" but frees about 6 GB + tool-cache: false + + # all of these default to true, but feel free to set to + # "false" if necessary for your workflow + android: true + dotnet: true + haskell: true + large-packages: false + docker-images: true + swap-storage: true - name: Remove existing clang and LLVM run: sudo apt purge llvm* clang* - name: Install and cache deps diff --git a/fuzzers/baby_fuzzer_swap_differential/Cargo.toml b/fuzzers/baby_fuzzer_swap_differential/Cargo.toml index 08491cce07..13256d5e97 100644 --- a/fuzzers/baby_fuzzer_swap_differential/Cargo.toml +++ b/fuzzers/baby_fuzzer_swap_differential/Cargo.toml @@ -21,7 +21,7 @@ debug = true [build-dependencies] anyhow = "1" -bindgen = "0.63" +bindgen = "0.69.4" cc = "1.0" [dependencies] diff --git a/fuzzers/forkserver_libafl_cc/Cargo.toml b/fuzzers/forkserver_libafl_cc/Cargo.toml index 2d6efbfc7b..aed0c975ef 100644 --- a/fuzzers/forkserver_libafl_cc/Cargo.toml +++ b/fuzzers/forkserver_libafl_cc/Cargo.toml @@ -26,7 +26,7 @@ nix = "0.27" libafl = { path = "../../libafl/" } libafl_bolts = { path = "../../libafl_bolts/" } libafl_cc = { path = "../../libafl_cc/" } -libafl_targets = { path = "../../libafl_targets/" } +libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "libfuzzer"] } [lib] name = "libforkserver_libafl_cc" diff --git a/fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs b/fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs index 1687f30265..011d073a84 100644 --- a/fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs +++ b/fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs @@ -24,9 +24,6 @@ pub fn main() { .parse_args(&args) .expect("Failed to parse the command line") // Enable libafl's coverage instrumentation - .add_pass(LLVMPasses::AFLCoverage) - .add_arg("-mllvm") - .add_arg("-ctx") // Context sensitive coverage // Imitate afl-cc's compile definitions .add_arg("-D__AFL_FUZZ_INIT()=int __afl_sharedmem_fuzzing = 1;extern unsigned int *__afl_fuzz_len;extern unsigned char *__afl_fuzz_ptr;unsigned char __afl_fuzz_alt[1048576];unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;void libafl_start_forkserver(void)") .add_arg("-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr)") diff --git a/fuzzers/libfuzzer_libpng_ctx/Cargo.toml b/fuzzers/fuzzbench_ctx/Cargo.toml similarity index 62% rename from fuzzers/libfuzzer_libpng_ctx/Cargo.toml rename to fuzzers/fuzzbench_ctx/Cargo.toml index 53de79a405..067e942340 100644 --- a/fuzzers/libfuzzer_libpng_ctx/Cargo.toml +++ b/fuzzers/fuzzbench_ctx/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "libfuzzer_libpng_ctx" +name = "fuzzbench" version = "0.11.2" authors = ["Andrea Fioraldi ", "Dominik Maier "] edition = "2021" @@ -7,6 +7,7 @@ edition = "2021" [features] default = ["std"] std = [] +no_link_main = ["libafl_targets/libfuzzer_no_link_main"] [profile.release] lto = true @@ -14,19 +15,25 @@ codegen-units = 1 opt-level = 3 debug = true +[profile.release-fuzzbench] +inherits = "release" +debug = false +strip = true + [build-dependencies] cc = { version = "1.0", features = ["parallel"] } which = "4.4" [dependencies] -libafl = { path = "../../libafl/", features = ["std", "derive", "llmp_compression", "introspection"] } +libafl = { path = "../../libafl/" } libafl_bolts = { path = "../../libafl_bolts/" } -libafl_targets = { path = "../../libafl_targets/", features = ["libfuzzer"] } +libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_hitcounts", "sancov_cmplog", "libfuzzer", "sancov_ctx"] } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } -clap = { version = "4.0", features = ["derive"] } +clap = { version = "4.0", features = ["default"] } +nix = { version = "0.27", features = ["fs"] } mimalloc = { version = "*", default-features = false } [lib] -name = "libfuzzer_libpng" +name = "fuzzbench" crate-type = ["staticlib"] diff --git a/fuzzers/fuzzbench_ctx/Makefile.toml b/fuzzers/fuzzbench_ctx/Makefile.toml new file mode 100644 index 0000000000..3cc7164e4a --- /dev/null +++ b/fuzzers/fuzzbench_ctx/Makefile.toml @@ -0,0 +1,108 @@ +[env] +PROJECT_DIR = { script = ["pwd"] } +CARGO_TARGET_DIR = { value = "${PROJECT_DIR}/target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } } +FUZZER_NAME="fuzzer" +PROFILE = { value = "release", condition = {env_not_set = ["PROFILE"]} } +PROFILE_DIR = {value = "release", condition = {env_not_set = ["PROFILE_DIR"] }} + +[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", "--profile", "${PROFILE}"] + +[tasks.cc] +linux_alias = "cc_unix" +mac_alias = "cc_unix" +windows_alias = "unsupported" + +[tasks.cc_unix] +command = "cargo" +args = ["build", "--profile", "${PROFILE}"] + +# 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}/${PROFILE_DIR}/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}/${PROFILE_DIR}/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 31s ./${FUZZER_NAME} -o out -i in >fuzz_stdout.log || true +if grep -qa "objectives: 1" fuzz_stdout.log; then + echo "Fuzzer is working" +else + echo "Fuzzer does not generate any testcases or any crashes" + exit 1 +fi +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 +''' diff --git a/fuzzers/fuzzbench_ctx/fuzz.c b/fuzzers/fuzzbench_ctx/fuzz.c new file mode 100644 index 0000000000..0460dd63d2 --- /dev/null +++ b/fuzzers/fuzzbench_ctx/fuzz.c @@ -0,0 +1,19 @@ +#include +#include +#include + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size >= 8 && *(uint32_t *)Data == 0xaabbccdd) { abort(); } + char buf[8] = {'a', 'b', 'c', 'd'}; + + if (memcmp(Data, buf, 4) == 0) { abort(); } + return 0; +} + +/* +int main() { + + char buf [10] = {0}; + LLVMFuzzerTestOneInput(buf, 10); + +}*/ diff --git a/fuzzers/libfuzzer_libpng_ctx/src/bin/libafl_cc.rs b/fuzzers/fuzzbench_ctx/src/bin/libafl_cc.rs similarity index 66% rename from fuzzers/libfuzzer_libpng_ctx/src/bin/libafl_cc.rs rename to fuzzers/fuzzbench_ctx/src/bin/libafl_cc.rs index fc8c6c2800..9ddfe1a5e7 100644 --- a/fuzzers/libfuzzer_libpng_ctx/src/bin/libafl_cc.rs +++ b/fuzzers/fuzzbench_ctx/src/bin/libafl_cc.rs @@ -3,7 +3,7 @@ use std::env; use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper}; pub fn main() { - let args: Vec = env::args().collect(); + let mut args: Vec = env::args().collect(); if args.len() > 1 { let mut dir = env::current_exe().unwrap(); let wrapper_name = dir.file_name().unwrap().to_str().unwrap(); @@ -16,16 +16,25 @@ pub fn main() { 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") - .add_pass(LLVMPasses::AFLCoverage) - .add_passes_arg("-ctx") // Context sensitive coverage - .link_staticlib(&dir, "libfuzzer_libpng") + .link_staticlib(&dir, "fuzzbench") + .add_pass(LLVMPasses::CmpLogRtn) + .add_pass(LLVMPasses::Ctx) .run() .expect("Failed to run the wrapped compiler") { diff --git a/fuzzers/libfuzzer_libpng_ctx/src/bin/libafl_cxx.rs b/fuzzers/fuzzbench_ctx/src/bin/libafl_cxx.rs similarity index 100% rename from fuzzers/libfuzzer_libpng_ctx/src/bin/libafl_cxx.rs rename to fuzzers/fuzzbench_ctx/src/bin/libafl_cxx.rs diff --git a/fuzzers/fuzzbench_ctx/src/lib.rs b/fuzzers/fuzzbench_ctx/src/lib.rs new file mode 100644 index 0000000000..f7ad1ef1ba --- /dev/null +++ b/fuzzers/fuzzbench_ctx/src/lib.rs @@ -0,0 +1,407 @@ +//! 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::{ + corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, + events::SimpleRestartingEventManager, + executors::{inprocess::HookableInProcessExecutor, ExitKind}, + 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, +}; +use libafl_bolts::{ + current_nanos, current_time, + os::dup2, + ownedref::OwnedMutSlice, + rands::StdRand, + shmem::{ShMemProvider, StdShMemProvider}, + tuples::{tuple_list, Merge}, + AsSlice, +}; +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +use libafl_targets::autotokens; +use libafl_targets::{ + edges_map_mut_ptr, edges_max_num, libfuzzer_initialize, libfuzzer_test_one_input, + std_edges_map_observer, CmpLogObserver, CtxHook, EDGES_MAP_SIZE, +}; +#[cfg(unix)] +use nix::{self, unistd::dup}; + +/// The fuzzer main (as `no_mangle` C function) +#[no_mangle] +pub extern "C" fn libafl_main() { + // Registry the metadata types used in this fuzzer + // Needed only on no_std + // unsafe { RegistryBuilder::register::(); } + + 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')"), + ) + .arg( + Arg::new("in") + .short('i') + .long("input") + .help("The directory to read initial inputs from ('seeds')"), + ) + .arg( + Arg::new("tokens") + .short('x') + .long("tokens") + .help("A file to read tokens from, to be used during fuzzing"), + ) + .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")) + .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.get_many::("remaining") { + let filenames: Vec<&str> = filenames.map(String::as_str).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::("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::("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::("tokens").map(PathBuf::from); + + let logfile = PathBuf::from(res.get_one::("logfile").unwrap().to_string()); + + let timeout = Duration::from_millis( + res.get_one::("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 = 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 +#[allow(clippy::too_many_lines)] +fn fuzz( + corpus_dir: PathBuf, + objective_dir: PathBuf, + seed_dir: &PathBuf, + tokenfile: Option, + 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(), "{:?} {s}", current_time()).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_observer = HitcountsMapObserver::new(unsafe { + StdMapObserver::from_mut_slice( + "edges", + OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_SIZE), + ) + }); + + // Create an observation channel to keep track of the execution time + let time_observer = TimeObserver::new("time"); + + let cmplog_observer = CmpLogObserver::new("cmplog", true); + + let map_feedback = MaxMapFeedback::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::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 + InMemoryOnDiskCorpus::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 = 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); + + // A minimization+queue policy to get testcasess from the corpus + let scheduler = IndexesLenTimeMinimizerScheduler::new(StdWeightedScheduler::with_schedule( + &mut state, + &edges_observer, + Some(PowerSchedule::FAST), + )); + + // 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; + let ctx_hook = CtxHook::default(); + // Create the executor for an in-process function with one observer for edge coverage and one for the execution time + let mut executor = HookableInProcessExecutor::with_timeout_generic( + tuple_list!(ctx_hook), + &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( + HookableInProcessExecutor::with_timeout_generic( + tuple_list!(ctx_hook), + &mut tracing_harness, + tuple_list!(cmplog_observer), + &mut fuzzer, + &mut state, + &mut mgr, + timeout * 10, + )?, + // Give it more time! + ); + + // The order of the stages matter! + let mut stages = tuple_list!(calibration, tracing, i2s, power); + + // Read tokens + if state.metadata_map().get::().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.must_load_initial_inputs() { + 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 output (logs still survive) + #[cfg(unix)] + { + let null_fd = file_null.as_raw_fd(); + // dup2(null_fd, io::stdout().as_raw_fd())?; + if std::env::var("LIBAFL_FUZZBENCH_DEBUG").is_err() { + 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(()) +} diff --git a/fuzzers/fuzzbench_ctx/stub_rt.c b/fuzzers/fuzzbench_ctx/stub_rt.c new file mode 100644 index 0000000000..825d6780af --- /dev/null +++ b/fuzzers/fuzzbench_ctx/stub_rt.c @@ -0,0 +1,34 @@ +#include + +__attribute__((weak)) void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, + uint32_t *stop) { +} + +__attribute__((weak)) void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { +} + +__attribute__((weak)) void __cmplog_rtn_hook(uint8_t *ptr1, uint8_t *ptr2) { +} + +__attribute__((weak)) void __cmplog_rtn_gcc_stdstring_cstring( + uint8_t *stdstring, uint8_t *cstring) { +} + +__attribute__((weak)) void __cmplog_rtn_gcc_stdstring_stdstring( + uint8_t *stdstring1, uint8_t *stdstring2) { +} + +__attribute__((weak)) void __cmplog_rtn_llvm_stdstring_cstring( + uint8_t *stdstring, uint8_t *cstring) { +} + +__attribute__((weak)) void __cmplog_rtn_llvm_stdstring_stdstring( + uint8_t *stdstring1, uint8_t *stdstring2) { +} + +extern void libafl_main(void); + +int main(int argc, char **argv) { + libafl_main(); + return 0; +} diff --git a/fuzzers/libfuzzer_libpng_ctx/.gitignore b/fuzzers/libfuzzer_libpng_ctx/.gitignore deleted file mode 100644 index a977a2ca5b..0000000000 --- a/fuzzers/libfuzzer_libpng_ctx/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libpng-* \ No newline at end of file diff --git a/fuzzers/libfuzzer_libpng_ctx/Makefile.toml b/fuzzers/libfuzzer_libpng_ctx/Makefile.toml deleted file mode 100644 index 8d66dbc8e3..0000000000 --- a/fuzzers/libfuzzer_libpng_ctx/Makefile.toml +++ /dev/null @@ -1,134 +0,0 @@ -# Variables -[env] -FUZZER_NAME='fuzzer_libpng_ctx' -PROJECT_DIR = { script = ["pwd"] } -CARGO_TARGET_DIR = { value = "${PROJECT_DIR}/target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } } -PROFILE = { value = "release", condition = {env_not_set = ["PROFILE"]} } -PROFILE_DIR = {value = "release", condition = {env_not_set = ["PROFILE_DIR"] }} -LIBAFL_CC = '${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cc' -LIBAFL_CXX = '${CARGO_TARGET_DIR}/${PROFILE}/libafl_cxx' -FUZZER = '${CARGO_TARGET_DIR}/${PROFILE_DIR}/${FUZZER_NAME}' - -[tasks.unsupported] -script_runner="@shell" -script=''' -echo "Cargo-make not integrated yet on this platform" -''' - -# libpng -[tasks.libpng] -linux_alias = "libpng_unix" -mac_alias = "libpng_unix" -windows_alias = "unsupported" - -[tasks.libpng_unix] -condition = { files_not_exist = ["./libpng-1.6.37"]} -script_runner="@shell" -script=''' -wget https://github.com/glennrp/libpng/archive/refs/tags/v1.6.37.tar.gz -tar -xvf v1.6.37.tar.gz -''' - -# Compilers -[tasks.cxx] -linux_alias = "cxx_unix" -mac_alias = "cxx_unix" -windows_alias = "unsupported" - -[tasks.cxx_unix] -command = "cargo" -args = ["build" , "--profile", "${PROFILE}"] - -[tasks.cc] -linux_alias = "cc_unix" -mac_alias = "cc_unix" -windows_alias = "unsupported" - -[tasks.cc_unix] -command = "cargo" -args = ["build" , "--profile", "${PROFILE}"] - -# Library -[tasks.lib] -linux_alias = "lib_unix" -mac_alias = "lib_unix" -windows_alias = "unsupported" - -[tasks.lib_unix] -script_runner="@shell" -script=''' -cd libpng-1.6.37 && ./configure --enable-shared=no --with-pic=yes --enable-hardware-optimizations=yes -cd "${PROJECT_DIR}" -make -C libpng-1.6.37 CC="${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cc" CXX="${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cxx" -''' -dependencies = [ "libpng", "cxx", "cc" ] - - -# Harness -[tasks.fuzzer] -linux_alias = "fuzzer_unix" -mac_alias = "fuzzer_unix" -windows_alias = "unsupported" - -[tasks.fuzzer_unix] -command = "${CARGO_TARGET_DIR}/${PROFILE_DIR}/libafl_cxx" -args = ["${PROJECT_DIR}/harness.cc", "${PROJECT_DIR}/libpng-1.6.37/.libs/libpng16.a", "-I", "${PROJECT_DIR}/libpng-1.6.37/", "-o", "${FUZZER_NAME}", "-lm", "-lz"] -dependencies = [ "lib", "cxx", "cc" ] - -# Run the fuzzer -[tasks.run] -linux_alias = "run_unix" -mac_alias = "run_unix" -windows_alias = "unsupported" - -[tasks.run_unix] -script_runner = "@shell" -script=''' -./${FUZZER_NAME} --cores 0 --input ./corpus -''' -dependencies = [ "fuzzer" ] - -# Test -[tasks.test] -linux_alias = "test_unix" -mac_alias = "test_mac" -windows_alias = "unsupported" - -[tasks.test_unix] -script_runner = "@shell" -script=''' -rm -rf libafl_unix_shmem_server || true -timeout 31s ./${FUZZER_NAME} --cores 0 --input ./corpus >fuzz_stdout.log 2>/dev/null || true -if grep -qa "corpus: 30" fuzz_stdout.log; then - echo "Fuzzer is working" -elif grep -qa "objectives: 1" fuzz_stdout.log; then - echo "Fuzzer finds timeout or crash" -else - echo "Fuzzer does not generate any testcases or any crashes" - exit 1 -fi -''' -dependencies = [ "fuzzer" ] - -[tasks.test_mac] -script_runner = "@shell" -script=''' -rm -rf libafl_unix_shmem_server || true -timeout 31s ./${FUZZER_NAME} --cores 0 --input ./corpus >fuzz_stdout.log 2>/dev/null || true -''' - -# Clean up -[tasks.clean] -linux_alias = "clean_unix" -mac_alias = "clean_unix" -windows_alias = "unsupported" - -[tasks.clean_unix] -# Disable default `clean` definition -clear = true -script_runner="@shell" -script=''' -rm -f ./${FUZZER_NAME} -make -C libpng-1.6.37 clean -cargo clean -''' diff --git a/fuzzers/libfuzzer_libpng_ctx/README.md b/fuzzers/libfuzzer_libpng_ctx/README.md deleted file mode 100644 index b9ffdecb35..0000000000 --- a/fuzzers/libfuzzer_libpng_ctx/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Libfuzzer for libpng, with launcher - -This folder contains an example fuzzer for libpng, using LLMP for fast multi-process fuzzing and crash detection. -To show off crash detection, we added a `ud2` instruction to the harness, edit harness.cc if you want a non-crashing example. -It has been tested on Linux. - -In contrast to the normal libfuzzer libpng example, this uses the `launcher` feature, that automatically spawns `n` child processes, and binds them to a free core. - -## Build - -To build this example, run - -```bash -cargo build --release -``` - -This will build the library with the fuzzer (src/lib.rs) with the libfuzzer compatibility layer and the SanitizerCoverage runtime functions for coverage feedback. -In addition, it will also build two C and C++ compiler wrappers (bin/libafl_c(libafl_c/xx).rs) that you must use to compile the target. - -Then download libpng, and unpack the archive: -```bash -wget https://github.com/glennrp/libpng/archive/refs/tags/v1.6.37.tar.gz -tar -xvf v1.6.37.tar.gz -``` - -Now compile libpng, using the libafl_cc compiler wrapper: - -```bash -cd libpng-1.6.37 -./configure -make CC=../target/release/libafl_cc CXX=../target/release/libafl_cxx -j `nproc` -``` - -You can find the static lib at `libpng-1.6.37/.libs/libpng16.a`. - -Now, we have to build the libfuzzer harness and link all together to create our fuzzer binary. - -``` -cd .. -./target/release/libafl_cxx ./harness.cc libpng-1.6.37/.libs/libpng16.a -I libpng-1.6.37/ -o fuzzer_libpng -lz -lm -``` - -Afterwards, the fuzzer will be ready to run. - -## Run - -Just run once, the launcher feature should do the rest. \ No newline at end of file diff --git a/fuzzers/libfuzzer_libpng_ctx/corpus/not_kitty.png b/fuzzers/libfuzzer_libpng_ctx/corpus/not_kitty.png deleted file mode 100644 index eff7c1707b..0000000000 Binary files a/fuzzers/libfuzzer_libpng_ctx/corpus/not_kitty.png and /dev/null differ diff --git a/fuzzers/libfuzzer_libpng_ctx/corpus/not_kitty_alpha.png b/fuzzers/libfuzzer_libpng_ctx/corpus/not_kitty_alpha.png deleted file mode 100644 index 2fb8da2c8f..0000000000 Binary files a/fuzzers/libfuzzer_libpng_ctx/corpus/not_kitty_alpha.png and /dev/null differ diff --git a/fuzzers/libfuzzer_libpng_ctx/corpus/not_kitty_gamma.png b/fuzzers/libfuzzer_libpng_ctx/corpus/not_kitty_gamma.png deleted file mode 100644 index 939d9d29a9..0000000000 Binary files a/fuzzers/libfuzzer_libpng_ctx/corpus/not_kitty_gamma.png and /dev/null differ diff --git a/fuzzers/libfuzzer_libpng_ctx/corpus/not_kitty_icc.png b/fuzzers/libfuzzer_libpng_ctx/corpus/not_kitty_icc.png deleted file mode 100644 index f0c7804d99..0000000000 Binary files a/fuzzers/libfuzzer_libpng_ctx/corpus/not_kitty_icc.png and /dev/null differ diff --git a/fuzzers/libfuzzer_libpng_ctx/harness.cc b/fuzzers/libfuzzer_libpng_ctx/harness.cc deleted file mode 100644 index e26e707e17..0000000000 --- a/fuzzers/libfuzzer_libpng_ctx/harness.cc +++ /dev/null @@ -1,188 +0,0 @@ -// libpng_read_fuzzer.cc -// Copyright 2017-2018 Glenn Randers-Pehrson -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that may -// be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE - -// Last changed in libpng 1.6.35 [July 15, 2018] - -// The modifications in 2017 by Glenn Randers-Pehrson include -// 1. addition of a PNG_CLEANUP macro, -// 2. setting the option to ignore ADLER32 checksums, -// 3. adding "#include " which is needed on some platforms -// to provide memcpy(). -// 4. adding read_end_info() and creating an end_info structure. -// 5. adding calls to png_set_*() transforms commonly used by browsers. - -#include -#include -#include - -#include - -#define PNG_INTERNAL -#include "png.h" - -#define PNG_CLEANUP \ - if (png_handler.png_ptr) { \ - if (png_handler.row_ptr) \ - png_free(png_handler.png_ptr, png_handler.row_ptr); \ - if (png_handler.end_info_ptr) \ - png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr, \ - &png_handler.end_info_ptr); \ - else if (png_handler.info_ptr) \ - png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr, \ - nullptr); \ - else \ - png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \ - png_handler.png_ptr = nullptr; \ - png_handler.row_ptr = nullptr; \ - png_handler.info_ptr = nullptr; \ - png_handler.end_info_ptr = nullptr; \ - } - -struct BufState { - const uint8_t *data; - size_t bytes_left; -}; - -struct PngObjectHandler { - png_infop info_ptr = nullptr; - png_structp png_ptr = nullptr; - png_infop end_info_ptr = nullptr; - png_voidp row_ptr = nullptr; - BufState *buf_state = nullptr; - - ~PngObjectHandler() { - if (row_ptr) { png_free(png_ptr, row_ptr); } - if (end_info_ptr) - png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr); - else if (info_ptr) - png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); - else - png_destroy_read_struct(&png_ptr, nullptr, nullptr); - delete buf_state; - } -}; - -void user_read_data(png_structp png_ptr, png_bytep data, size_t length) { - BufState *buf_state = static_cast(png_get_io_ptr(png_ptr)); - if (length > buf_state->bytes_left) { png_error(png_ptr, "read error"); } - memcpy(data, buf_state->data, length); - buf_state->bytes_left -= length; - buf_state->data += length; -} - -static const int kPngHeaderSize = 8; - -// Entry point for LibFuzzer. -// Roughly follows the libpng book example: -// http://www.libpng.org/pub/png/book/chapter13.html -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - if (size < kPngHeaderSize) { return 0; } - - std::vector v(data, data + size); - if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) { - // not a PNG. - return 0; - } - - PngObjectHandler png_handler; - png_handler.png_ptr = nullptr; - png_handler.row_ptr = nullptr; - png_handler.info_ptr = nullptr; - png_handler.end_info_ptr = nullptr; - - png_handler.png_ptr = - png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - if (!png_handler.png_ptr) { return 0; } - - png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr); - if (!png_handler.info_ptr) { - PNG_CLEANUP - return 0; - } - - png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr); - if (!png_handler.end_info_ptr) { - PNG_CLEANUP - return 0; - } - - png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); -#ifdef PNG_IGNORE_ADLER32 - png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON); -#endif - - // Setting up reading from buffer. - png_handler.buf_state = new BufState(); - png_handler.buf_state->data = data + kPngHeaderSize; - png_handler.buf_state->bytes_left = size - kPngHeaderSize; - png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data); - png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize); - - if (setjmp(png_jmpbuf(png_handler.png_ptr))) { - PNG_CLEANUP - return 0; - } - - // Reading. - png_read_info(png_handler.png_ptr, png_handler.info_ptr); - - // reset error handler to put png_deleter into scope. - if (setjmp(png_jmpbuf(png_handler.png_ptr))) { - PNG_CLEANUP - return 0; - } - - png_uint_32 width, height; - int bit_depth, color_type, interlace_type, compression_type; - int filter_type; - - if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, &height, - &bit_depth, &color_type, &interlace_type, &compression_type, - &filter_type)) { - PNG_CLEANUP - return 0; - } - - // This is going to be too slow. - if (width && height > 100000000 / width) { - PNG_CLEANUP -#ifdef HAS_DUMMY_CRASH - #ifdef __aarch64__ - asm volatile(".word 0xf7f0a000\n"); - #else - asm("ud2"); - #endif -#endif - return 0; - } - - // Set several transforms that browsers typically use: - png_set_gray_to_rgb(png_handler.png_ptr); - png_set_expand(png_handler.png_ptr); - png_set_packing(png_handler.png_ptr); - png_set_scale_16(png_handler.png_ptr); - png_set_tRNS_to_alpha(png_handler.png_ptr); - - int passes = png_set_interlace_handling(png_handler.png_ptr); - - png_read_update_info(png_handler.png_ptr, png_handler.info_ptr); - - png_handler.row_ptr = - png_malloc(png_handler.png_ptr, - png_get_rowbytes(png_handler.png_ptr, png_handler.info_ptr)); - - for (int pass = 0; pass < passes; ++pass) { - for (png_uint_32 y = 0; y < height; ++y) { - png_read_row(png_handler.png_ptr, - static_cast(png_handler.row_ptr), nullptr); - } - } - - png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); - - PNG_CLEANUP - return 0; -} diff --git a/fuzzers/libfuzzer_libpng_ctx/src/lib.rs b/fuzzers/libfuzzer_libpng_ctx/src/lib.rs deleted file mode 100644 index 2e422fb71c..0000000000 --- a/fuzzers/libfuzzer_libpng_ctx/src/lib.rs +++ /dev/null @@ -1,243 +0,0 @@ -//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts -//! The example harness is built for libpng. -//! In this example, you will see the use of the `launcher` feature. -//! The `launcher` will spawn new processes for each cpu core. -use mimalloc::MiMalloc; -#[global_allocator] -static GLOBAL: MiMalloc = MiMalloc; - -use core::time::Duration; -use std::{env, net::SocketAddr, path::PathBuf}; - -use clap::{self, Parser}; -use libafl::{ - corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, - events::{launcher::Launcher, EventConfig}, - executors::{inprocess::InProcessExecutor, ExitKind}, - feedback_or, feedback_or_fast, - feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, - fuzzer::{Fuzzer, StdFuzzer}, - inputs::{BytesInput, HasTargetBytes}, - monitors::MultiMonitor, - mutators::{ - scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, - token_mutations::Tokens, - }, - observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, - schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, - stages::mutational::StdMutationalStage, - state::{HasCorpus, HasMetadata, StdState}, - Error, -}; -use libafl_bolts::{ - core_affinity::Cores, - current_nanos, - rands::StdRand, - shmem::{ShMemProvider, StdShMemProvider}, - tuples::{tuple_list, Merge}, - AsSlice, -}; -use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer}; - -fn timeout_from_millis_str(time: &str) -> Result { - Ok(Duration::from_millis(time.parse()?)) -} - -#[derive(Debug, Parser)] -#[command( - name = "libfuzzer_libpng_ctx", - about = "A clone of libfuzzer using LibAFL for a libpng harness", - author = "Andrea Fioraldi , Dominik Maier " -)] -struct Opt { - #[arg( - short, - long, - value_parser = Cores::from_cmdline, - help = "Spawn a client in each of the provided cores. Broker runs in the 0th core. 'all' to select all available cores. 'none' to run a client without binding to any core. eg: '1,2-4,6' selects the cores 1,2,3,4,6.", - name = "CORES" - )] - cores: Cores, - - #[arg( - short = 'p', - long, - help = "Choose the broker TCP port, default is 1337", - name = "PORT", - default_value = "1337" - )] - broker_port: u16, - - #[arg(short = 'a', long, help = "Specify a remote broker", name = "REMOTE")] - remote_broker_addr: Option, - - #[arg(short, long, help = "Set an initial corpus directory", name = "INPUT")] - input: Vec, - - #[arg( - short, - long, - help = "Set the output directory, default is ./out", - name = "OUTPUT", - default_value = "./out" - )] - output: PathBuf, - - #[arg( - short, - long, - value_parser = timeout_from_millis_str, - help = "Set the exeucution timeout in milliseconds, default is 10000", - name = "TIMEOUT", - default_value = "10000", - )] - timeout: Duration, - /* - // The tokens are hardcoded in this example. - #[arg( - - short = "x", - long, - help = "Feed the fuzzer with an user-specified list of tokens (often called \"dictionary\"", - name = "TOKENS", - multiple = true - )] - tokens: Vec,*/ -} - -/// The main fn, `no_mangle` as it is a C symbol -#[no_mangle] -pub extern "C" fn libafl_main() { - // Registry the metadata types used in this fuzzer - // Needed only on no_std - // unsafe { RegistryBuilder::register::(); } - let opt = Opt::parse(); - - let broker_port = opt.broker_port; - - let cores = opt.cores; - - println!( - "Workdir: {:?}", - env::current_dir().unwrap().to_string_lossy().to_string() - ); - - let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); - - let monitor = MultiMonitor::new(|s| println!("{s}")); - - let mut run_client = |state: Option<_>, mut restarting_mgr, _core_id| { - // Create an observation channel using the coverage map - let edges_observer = unsafe { HitcountsMapObserver::new(std_edges_map_observer("edges")) }; - - // Create an observation channel to keep track of the execution time - let time_observer = TimeObserver::new("time"); - - // 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 - MaxMapFeedback::tracking(&edges_observer, true, false), - // Time feedback, this one does not need a feedback state - TimeFeedback::with_observer(&time_observer) - ); - - // A feedback to choose if an input is a solution or not - let mut 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 it in memory for performance - InMemoryCorpus::new(), - // Corpus in which we store solutions (crashes in this example), - // on disk so the user can get them after stopping the fuzzer - OnDiskCorpus::new(opt.output.clone()).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!("We're a client, let's fuzz :)"); - - // Create a PNG dictionary if not existing - if state.metadata_map().get::().is_none() { - state.add_metadata(Tokens::from([ - vec![137, 80, 78, 71, 13, 10, 26, 10], // PNG header - "IHDR".as_bytes().to_vec(), - "IDAT".as_bytes().to_vec(), - "PLTE".as_bytes().to_vec(), - "IEND".as_bytes().to_vec(), - ])); - } - - // Setup a basic mutator with a mutational stage - let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations())); - let mut stages = tuple_list!(StdMutationalStage::new(mutator)); - - // A minimization+queue policy to get testcasess from the corpus - let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::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 - }; - - // 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, time_observer), - &mut fuzzer, - &mut state, - &mut restarting_mgr, - opt.timeout, - )?; - - // The actual target run starts here. - // Call LLVMFUzzerInitialize() if present. - let args: Vec = env::args().collect(); - if libfuzzer_initialize(&args) == -1 { - println!("Warning: LLVMFuzzerInitialize failed with -1"); - } - - // In case the corpus is empty (on first run), reset - if state.must_load_initial_inputs() { - state - .load_initial_inputs(&mut fuzzer, &mut executor, &mut restarting_mgr, &opt.input) - .unwrap_or_else(|_| panic!("Failed to load initial corpus at {:?}", &opt.input)); - println!("We imported {} inputs from disk.", state.corpus().count()); - } - - fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?; - Ok(()) - }; - - match Launcher::builder() - .shmem_provider(shmem_provider) - .configuration(EventConfig::from_name("default")) - .monitor(monitor) - .run_client(&mut run_client) - .cores(&cores) - .broker_port(broker_port) - .remote_broker_addr(opt.remote_broker_addr) - .stdout_file(Some("/dev/null")) - .build() - .launch() - { - Ok(()) => (), - Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."), - Err(err) => panic!("Failed to run launcher: {err:?}"), - } -} diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index 883a405a8f..fcee2eba17 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -44,6 +44,9 @@ use crate::{ /// The process executor simply calls a target function, as mutable reference to a closure pub type InProcessExecutor<'a, H, OT, S> = GenericInProcessExecutor; +/// The inprocess executor that allows hooks +pub type HookableInProcessExecutor<'a, H, HT, OT, S> = + GenericInProcessExecutor; /// The process executor simply calls a target function, as boxed `FnMut` trait object pub type OwnedInProcessExecutor = GenericInProcessExecutor< dyn FnMut(&::Input) -> ExitKind, diff --git a/libafl/src/stages/calibrate.rs b/libafl/src/stages/calibrate.rs index 7e2224cbf0..3b9e45e70b 100644 --- a/libafl/src/stages/calibrate.rs +++ b/libafl/src/stages/calibrate.rs @@ -254,6 +254,7 @@ where .ok_or_else(|| Error::key_not_found("MapObserver not found".to_string()))?; let mut bitmap_size = map.count_bytes(); + assert!(bitmap_size != 0); bitmap_size = bitmap_size.max(1); // just don't make it 0 because we take log2 of it later. let psmeta = state .metadata_map_mut() diff --git a/libafl_cc/build.rs b/libafl_cc/build.rs index d0bb165dcc..f88ac511cc 100644 --- a/libafl_cc/build.rs +++ b/libafl_cc/build.rs @@ -404,10 +404,10 @@ pub const LIBAFL_CC_LLVM_VERSION: Option = None; for pass in &[ "cmplog-routines-pass.cc", - "afl-coverage-pass.cc", "autotokens-pass.cc", "coverage-accounting-pass.cc", "cmplog-instructions-pass.cc", + "ctx-pass.cc", ] { build_pass( bindir_path, diff --git a/libafl_cc/src/afl-coverage-pass.cc b/libafl_cc/src/afl-coverage-pass.cc deleted file mode 100644 index 921013bb52..0000000000 --- a/libafl_cc/src/afl-coverage-pass.cc +++ /dev/null @@ -1,872 +0,0 @@ -/* - american fuzzy lop++ - LLVM-mode instrumentation pass - --------------------------------------------------- - - Written by Laszlo Szekeres , - Adrian Herrera , - Michal Zalewski - - LLVM integration design comes from Laszlo Szekeres. C bits copied-and-pasted - from afl-as.c are Michal's fault. - - NGRAM previous location coverage comes from Adrian Herrera. - - Copyright 2015, 2016 Google Inc. All rights reserved. - Copyright 2019-2020 AFLplusplus Project. All rights reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - This library is plugged into LLVM when invoking clang through afl-clang-fast. - It tells the compiler to add code roughly equivalent to the bits discussed - in ../afl-as.h. - - */ - -#include "common-llvm.h" - -#include -#include -#include -#ifndef _WIN32 - #include - #include -#else - #include -#endif -#include -#include -#include -#include - -#include -#include -#include - -#include "llvm/Support/CommandLine.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/Support/FormatVariadic.h" - -// Without this, Can't build with llvm-14 & old PM -#if LLVM_VERSION_MAJOR >= 14 && !defined(USE_NEW_PM) - #include "llvm/Pass.h" -#endif - -#if LLVM_VERSION_MAJOR > 3 || \ - (LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR > 4) - #include "llvm/IR/DebugInfo.h" - #include "llvm/IR/CFG.h" -#else - #include "llvm/DebugInfo.h" - #include "llvm/Support/CFG.h" -#endif - -typedef uint32_t prev_loc_t; - -/* Maximum ngram size */ -#define NGRAM_SIZE_MAX 16U - -/* Maximum K for top-K context sensitivity */ -#define CTX_MAX_K 32U - -#define MAP_SIZE LIBAFL_EDGES_MAP_SIZE - -using namespace llvm; - -static cl::opt Debug("debug_afl_coverage", cl::desc("Debug prints"), - cl::init(false), cl::NotHidden); -static cl::opt InstRatio( - "inst_ratio_afl_coverage", cl::desc("Instrumentation ratio in percentage"), - cl::init(100), cl::NotHidden); -static cl::opt NotZero("not_zero", - cl::desc("Never hit 0 again in the hitcount"), - cl::init(true), cl::NotHidden); -static cl::opt Ngram( - "ngram", cl::desc("Size of the Ngram instrumentation (0 to disable)"), - cl::init(0), cl::NotHidden); -static cl::opt CtxK( - "ctx_k", - cl::desc( - "Size of the context for K-Ctx context sensitivity (0 to disable)"), - cl::init(0), cl::NotHidden); -static cl::opt Ctx("ctx", - cl::desc("Enable full context sensitive coverage"), - cl::init(false), cl::NotHidden); -static cl::opt ThreadSafe("thread_safe_afl_coverage", - cl::desc("Use the thread safe instrumentation"), - cl::init(false), cl::NotHidden); -static cl::opt DumpCFG( - "dump_afl_cfg", cl::desc("Dump CFG containing AFL-style edge index"), - cl::init(false), cl::NotHidden); -static cl::opt DumpCFGPath( - "dump_afl_cfg_path", - cl::desc("Path to dump CFG containing AFL-style edge index"), - cl::init(".cfg"), cl::NotHidden); - -namespace { - -#ifdef USE_NEW_PM -class AFLCoverage : public PassInfoMixin { - public: - AFLCoverage() { -#else -class AFLCoverage : public ModulePass { - public: - static char ID; - AFLCoverage() : ModulePass(ID) { -#endif - - // initInstrumentList(); - } - -#ifdef USE_NEW_PM - PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); -#else - bool runOnModule(Module &M) override; -#endif - - protected: - uint32_t map_size = MAP_SIZE; - uint32_t function_minimum_size = 1; - DenseMap bb_to_cur_loc; - DenseMap entry_bb; -}; - -} // namespace - -#ifdef USE_NEW_PM -extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK -llvmGetPassPluginInfo() { - return {LLVM_PLUGIN_API_VERSION, "AFLCoverage", "v0.1", - /* lambda to insert our pass into the pass pipeline. */ - [](PassBuilder &PB) { - #if 1 - #if LLVM_VERSION_MAJOR <= 13 - using OptimizationLevel = typename PassBuilder::OptimizationLevel; - #endif - PB.registerOptimizerLastEPCallback( - [](ModulePassManager &MPM, OptimizationLevel OL) { - MPM.addPass(AFLCoverage()); - }); - /* TODO LTO registration */ - #else - using PipelineElement = typename PassBuilder::PipelineElement; - PB.registerPipelineParsingCallback([](StringRef Name, - ModulePassManager &MPM, - ArrayRef) { - if (Name == "AFLCoverage") { - MPM.addPass(AFLCoverage()); - return true; - } else { - return false; - } - }); - #endif - }}; -} -#else - -char AFLCoverage::ID = 0; -#endif - -#ifdef USE_NEW_PM -PreservedAnalyses AFLCoverage::run(Module &M, ModuleAnalysisManager &MAM) { -#else -bool AFLCoverage::runOnModule(Module &M) { -#endif - if (Ctx && DumpCFG) { - FATAL( - "Does not support dumping CFG with full context sensitive coverage " - "enabled."); - } - LLVMContext &C = M.getContext(); - - IntegerType *Int8Ty = IntegerType::getInt8Ty(C); - IntegerType *Int32Ty = IntegerType::getInt32Ty(C); -#ifdef HAVE_VECTOR_INTRINSICS - IntegerType *IntLocTy = - IntegerType::getIntNTy(C, sizeof(prev_loc_t) * CHAR_BIT); -#endif - uint32_t rand_seed; - unsigned int cur_loc = 0; - -#ifdef USE_NEW_PM - auto PA = PreservedAnalyses::all(); -#endif - - /* Setup random() so we get Actually Random(TM) */ - rand_seed = time(NULL); - srand(rand_seed); - - /* - char *ptr; - if ((ptr = getenv("AFL_MAP_SIZE")) || (ptr = getenv("AFL_MAPSIZE"))) { - - map_size = atoi(ptr); - if (map_size < 8 || map_size > (1 << 29)) { - FATAL("illegal AFL_MAP_SIZE %u, must be between 2^3 and 2^30", - } - map_size); if (map_size % 8) {map_size = (((map_size >> 3) + 1) << 3);} - - } - - */ - - /* Decide instrumentation ratio */ - - if (!InstRatio || InstRatio > 100) { - FATAL("Bad value of the instrumentation ratio (must be between 1 and 100)"); - } - - unsigned PrevLocSize = 0; - unsigned PrevCallerSize = 0; - - bool instrument_ctx = Ctx || CtxK > 0; - bool instrument_caller = false; - -#ifdef HAVE_VECTOR_INTRINSICS - /* Decide previous location vector size (must be a power of two) */ - VectorType *PrevLocTy = NULL; - - if (Ngram && (Ngram < 2 || Ngram > NGRAM_SIZE_MAX)) { - FATAL( - "Bad value of the Ngram size (must be between 2 and NGRAM_SIZE_MAX " - "(%u))", - NGRAM_SIZE_MAX); - } - - if (Ngram) { - PrevLocSize = Ngram - 1; - } else { - PrevLocSize = 1; - } - - /* Decide K-Ctx vector size (must be a power of two) */ - VectorType *PrevCallerTy = NULL; - - if (CtxK > CTX_MAX_K) { - FATAL( - "Bad value of K for K-context sensitivity (must be between 1 and " - "CTX_MAX_K (%u))", - CTX_MAX_K); - } - - if (CtxK == 1) { - CtxK = 0; - instrument_ctx = true; - instrument_caller = true; // Enable CALLER instead - } - - if (CtxK) { - PrevCallerSize = CtxK; - instrument_ctx = true; - } - -#else - if (Ngram) - #ifndef LLVM_VERSION_PATCH - FATAL( - "Sorry, NGRAM branch coverage is not supported with llvm version " - "%d.%d.%d!", - LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0); - #else - FATAL( - "Sorry, NGRAM branch coverage is not supported with llvm version " - "%d.%d.%d!", - LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH); - #endif - if (CtxK) - #ifndef LLVM_VERSION_PATCH - FATAL( - "Sorry, K-CTX branch coverage is not supported with llvm version " - "%d.%d.%d!", - LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, 0); - #else - FATAL( - "Sorry, K-CTX branch coverage is not supported with llvm version " - "%d.%d.%d!", - LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH); - #endif - PrevLocSize = 1; -#endif - -#ifdef HAVE_VECTOR_INTRINSICS - int PrevLocVecSize = PowerOf2Ceil(PrevLocSize); - if (Ngram) - PrevLocTy = VectorType::get(IntLocTy, PrevLocVecSize - #if LLVM_VERSION_MAJOR >= 12 - , - false - #endif - ); -#endif - -#ifdef HAVE_VECTOR_INTRINSICS - int PrevCallerVecSize = PowerOf2Ceil(PrevCallerSize); - if (CtxK) - PrevCallerTy = VectorType::get(IntLocTy, PrevCallerVecSize - #if LLVM_VERSION_MAJOR >= 12 - , - false - #endif - ); -#endif - - /* Get globals for the SHM region and the previous location. Note that - __afl_prev_loc is thread-local. */ - - GlobalVariable *AFLMapPtr = - new GlobalVariable(M, PointerType::get(Int8Ty, 0), false, - GlobalValue::ExternalLinkage, 0, "__afl_area_ptr"); - GlobalVariable *AFLPrevLoc; - GlobalVariable *AFLPrevCaller; - GlobalVariable *AFLContext = NULL; - - if (Ctx || instrument_caller) -#if defined(__ANDROID__) || defined(__HAIKU__) - AFLContext = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); -#else - AFLContext = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx", 0, - GlobalVariable::GeneralDynamicTLSModel, 0, false); -#endif - -#ifdef HAVE_VECTOR_INTRINSICS - if (Ngram) - #if defined(__ANDROID__) || defined(__HAIKU__) - AFLPrevLoc = new GlobalVariable( - M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage, - /* Initializer */ nullptr, "__afl_prev_loc"); - #else - AFLPrevLoc = new GlobalVariable( - M, PrevLocTy, /* isConstant */ false, GlobalValue::ExternalLinkage, - /* Initializer */ nullptr, "__afl_prev_loc", - /* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel, - /* AddressSpace */ 0, /* IsExternallyInitialized */ false); - #endif - else -#endif -#if defined(__ANDROID__) || defined(__HAIKU__) - AFLPrevLoc = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc"); -#else - AFLPrevLoc = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_loc", 0, - GlobalVariable::GeneralDynamicTLSModel, 0, false); -#endif - -#ifdef HAVE_VECTOR_INTRINSICS - if (CtxK) - #if defined(__ANDROID__) || defined(__HAIKU__) - AFLPrevCaller = new GlobalVariable( - M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage, - /* Initializer */ nullptr, "__afl_prev_caller"); - #else - AFLPrevCaller = new GlobalVariable( - M, PrevCallerTy, /* isConstant */ false, GlobalValue::ExternalLinkage, - /* Initializer */ nullptr, "__afl_prev_caller", - /* InsertBefore */ nullptr, GlobalVariable::GeneralDynamicTLSModel, - /* AddressSpace */ 0, /* IsExternallyInitialized */ false); - #endif - else -#endif -#if defined(__ANDROID__) || defined(__HAIKU__) - AFLPrevCaller = - new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, - "__afl_prev_caller"); -#else - AFLPrevCaller = new GlobalVariable( - M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_caller", - 0, GlobalVariable::GeneralDynamicTLSModel, 0, false); -#endif - -#ifdef HAVE_VECTOR_INTRINSICS - /* Create the vector shuffle mask for updating the previous block history. - Note that the first element of the vector will store cur_loc, so just set - it to undef to allow the optimizer to do its thing. */ - - SmallVector PrevLocShuffle = {UndefValue::get(Int32Ty)}; - - for (unsigned I = 0; I < PrevLocSize - 1; ++I) { - PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, I)); - } - - for (int I = PrevLocSize; I < PrevLocVecSize; ++I) { - PrevLocShuffle.push_back(ConstantInt::get(Int32Ty, PrevLocSize)); - } - - Constant *PrevLocShuffleMask = ConstantVector::get(PrevLocShuffle); - - Constant *PrevCallerShuffleMask = NULL; - SmallVector PrevCallerShuffle = {UndefValue::get(Int32Ty)}; - - if (CtxK) { - for (unsigned I = 0; I < PrevCallerSize - 1; ++I) { - PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, I)); - } - - for (int I = PrevCallerSize; I < PrevCallerVecSize; ++I) { - PrevCallerShuffle.push_back(ConstantInt::get(Int32Ty, PrevCallerSize)); - } - - PrevCallerShuffleMask = ConstantVector::get(PrevCallerShuffle); - } - -#endif - - // other constants we need - ConstantInt *One = ConstantInt::get(Int8Ty, 1); - - Value *PrevCtx = NULL; // CTX sensitive coverage - LoadInst *PrevCaller = NULL; // K-CTX coverage - - /* Instrument all the things! */ - - int inst_blocks = 0; - // scanForDangerousFunctions(&M); - - for (auto &F : M) { - int has_calls = 0; - if (Debug) - fprintf(stderr, "FUNCTION: %s (%zu)\n", F.getName().str().c_str(), - F.size()); - - if (isIgnoreFunction(&F)) { continue; } - - if (F.size() < function_minimum_size) { continue; } - if (DumpCFG) { entry_bb[F.getName()] = &F.getEntryBlock(); } - - std::list todo; - for (auto &BB : F) { - BasicBlock::iterator IP = BB.getFirstInsertionPt(); - IRBuilder<> IRB(&(*IP)); - - // Context sensitive coverage - if (instrument_ctx && &BB == &F.getEntryBlock()) { -#ifdef HAVE_VECTOR_INTRINSICS - if (CtxK) { - PrevCaller = IRB.CreateLoad( - #if LLVM_VERSION_MAJOR >= 14 - PrevCallerTy, - #endif - AFLPrevCaller); - PrevCaller->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - PrevCtx = - IRB.CreateZExt(IRB.CreateXorReduce(PrevCaller), IRB.getInt32Ty()); - - } else -#endif - { - - // load the context ID of the previous function and write to to a - // local variable on the stack - LoadInst *PrevCtxLoad = IRB.CreateLoad( -#if LLVM_VERSION_MAJOR >= 14 - IRB.getInt32Ty(), -#endif - AFLContext); - PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - PrevCtx = PrevCtxLoad; - } - - // does the function have calls? and is any of the calls larger than one - // basic block? - for (auto &BB_2 : F) { - if (has_calls) { break; } - for (auto &IN : BB_2) { - CallInst *callInst = nullptr; - if ((callInst = dyn_cast(&IN))) { - Function *Callee = callInst->getCalledFunction(); - if (!Callee || Callee->size() < function_minimum_size) - continue; - else { - has_calls = 1; - break; - } - } - } - } - - // if yes we store a context ID for this function in the global var - if (has_calls) { - Value *NewCtx = ConstantInt::get(Int32Ty, RandBelow(map_size)); -#ifdef HAVE_VECTOR_INTRINSICS - if (CtxK) { - Value *ShuffledPrevCaller = IRB.CreateShuffleVector( - PrevCaller, UndefValue::get(PrevCallerTy), - PrevCallerShuffleMask); - Value *UpdatedPrevCaller = IRB.CreateInsertElement( - ShuffledPrevCaller, NewCtx, (uint64_t)0); - - StoreInst *Store = - IRB.CreateStore(UpdatedPrevCaller, AFLPrevCaller); - Store->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - - } else -#endif - { - - if (Ctx) { NewCtx = IRB.CreateXor(PrevCtx, NewCtx); } - StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext); - StoreCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - } - } - } - - if (RandBelow(100) >= InstRatio) { continue; } - - /* Make up cur_loc */ - - // cur_loc++; - cur_loc = RandBelow(map_size); - if (DumpCFG) { bb_to_cur_loc[&BB] = cur_loc; } -/* There is a problem with Ubuntu 18.04 and llvm 6.0 (see issue #63). - The inline function successors() is not inlined and also not found at runtime - :-( As I am unable to detect Ubuntu18.04 heree, the next best thing is to - disable this optional optimization for LLVM 6.0.0 and Linux */ -#if !(LLVM_VERSION_MAJOR == 6 && LLVM_VERSION_MINOR == 0) || !defined __linux__ - // only instrument if this basic block is the destination of a previous - // basic block that has multiple successors - // this gets rid of ~5-10% of instrumentations that are unnecessary - // result: a little more speed and less map pollution - int more_than_one = -1; - // fprintf(stderr, "BB %u: ", cur_loc); - for (pred_iterator PI = pred_begin(&BB), E = pred_end(&BB); PI != E; - ++PI) { - BasicBlock *Pred = *PI; - - int count = 0; - if (more_than_one == -1) { more_than_one = 0; } - // fprintf(stderr, " %p=>", Pred); - - for (succ_iterator SI = succ_begin(Pred), E = succ_end(Pred); SI != E; - ++SI) { - BasicBlock *Succ = *SI; - - // if (count > 0) - // fprintf(stderr, "|"); - if (Succ != NULL) { count++; } - // fprintf(stderr, "%p", Succ); - } - - if (count > 1) { more_than_one = 1; } - } - - // fprintf(stderr, " == %d\n", more_than_one); - if (F.size() > 1 && more_than_one != 1) { - // in CTX mode we have to restore the original context for the caller - - // she might be calling other functions which need the correct CTX - if (instrument_ctx && has_calls) { - Instruction *Inst = BB.getTerminator(); - if (isa(Inst) || isa(Inst)) { - IRBuilder<> Post_IRB(Inst); - - StoreInst *RestoreCtx; - #ifdef HAVE_VECTOR_INTRINSICS - if (CtxK) - RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller); - else - #endif - RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); - RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - } - } - - continue; - } - -#endif - - ConstantInt *CurLoc; - -#ifdef HAVE_VECTOR_INTRINSICS - if (Ngram) - CurLoc = ConstantInt::get(IntLocTy, cur_loc); - else -#endif - CurLoc = ConstantInt::get(Int32Ty, cur_loc); - - /* Load prev_loc */ - - LoadInst *PrevLoc; - - if (Ngram) { - PrevLoc = IRB.CreateLoad( -#if LLVM_VERSION_MAJOR >= 14 - PrevLocTy, -#endif - AFLPrevLoc); - } else { - PrevLoc = IRB.CreateLoad( -#if LLVM_VERSION_MAJOR >= 14 - IRB.getInt32Ty(), -#endif - AFLPrevLoc); - } - PrevLoc->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - Value *PrevLocTrans; - -#ifdef HAVE_VECTOR_INTRINSICS - /* "For efficiency, we propose to hash the tuple as a key into the - hit_count map as (prev_block_trans << 1) ^ curr_block_trans, where - prev_block_trans = (block_trans_1 ^ ... ^ block_trans_(n-1)" */ - - if (Ngram) - PrevLocTrans = - IRB.CreateZExt(IRB.CreateXorReduce(PrevLoc), IRB.getInt32Ty()); - else -#endif - PrevLocTrans = PrevLoc; - - if (instrument_ctx) - PrevLocTrans = - IRB.CreateZExt(IRB.CreateXor(PrevLocTrans, PrevCtx), Int32Ty); - else - PrevLocTrans = IRB.CreateZExt(PrevLocTrans, IRB.getInt32Ty()); - - /* Load SHM pointer */ - - LoadInst *MapPtr = IRB.CreateLoad( -#if LLVM_VERSION_MAJOR >= 14 - PointerType::get(Int8Ty, 0), -#endif - AFLMapPtr); - MapPtr->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - Value *MapPtrIdx; -#ifdef HAVE_VECTOR_INTRINSICS - if (Ngram) - MapPtrIdx = IRB.CreateGEP( - #if LLVM_VERSION_MAJOR >= 14 - Int8Ty, - #endif - MapPtr, - IRB.CreateZExt( - IRB.CreateXor(PrevLocTrans, IRB.CreateZExt(CurLoc, Int32Ty)), - Int32Ty)); - else -#endif - MapPtrIdx = IRB.CreateGEP( -#if LLVM_VERSION_MAJOR >= 14 - Int8Ty, -#endif - MapPtr, IRB.CreateXor(PrevLocTrans, CurLoc)); - - /* Update bitmap */ - - if (ThreadSafe) { /* Atomic */ - /* - #if LLVM_VERSION_MAJOR < 9 - if (neverZero_counters_str != - NULL) { // with llvm 9 we make this the default as the bug - in llvm - // is then fixed - #else - if (NotZero) { - - #endif - // register MapPtrIdx in a todo list - todo.push_back(MapPtrIdx); - - } else { - - */ - IRB.CreateAtomicRMW(llvm::AtomicRMWInst::BinOp::Add, MapPtrIdx, One, -#if LLVM_VERSION_MAJOR >= 13 - llvm::MaybeAlign(1), -#endif - llvm::AtomicOrdering::Monotonic); - /* - - } - - */ - - } else { - LoadInst *Counter = IRB.CreateLoad( -#if LLVM_VERSION_MAJOR >= 14 - IRB.getInt8Ty(), -#endif - MapPtrIdx); - Counter->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - Value *Incr = IRB.CreateAdd(Counter, One); - -#if LLVM_VERSION_MAJOR < 9 - if (neverZero_counters_str != - NULL) { // with llvm 9 we make this the default as the bug in llvm - // is then fixed -#else - if (NotZero) { - -#endif - /* hexcoder: Realize a counter that skips zero during overflow. - * Once this counter reaches its maximum value, it next increments to - * 1 - * - * Instead of - * Counter + 1 -> Counter - * we inject now this - * Counter + 1 -> {Counter, OverflowFlag} - * Counter + OverflowFlag -> Counter - */ - - ConstantInt *Zero = ConstantInt::get(Int8Ty, 0); - auto cf = IRB.CreateICmpEQ(Incr, Zero); - auto carry = IRB.CreateZExt(cf, Int8Ty); - Incr = IRB.CreateAdd(Incr, carry); - } - - IRB.CreateStore(Incr, MapPtrIdx) - ->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - } /* non atomic case */ - - /* Update prev_loc history vector (by placing cur_loc at the head of the - vector and shuffle the other elements back by one) */ - - StoreInst *Store; - -#ifdef HAVE_VECTOR_INTRINSICS - if (Ngram) { - Value *ShuffledPrevLoc = IRB.CreateShuffleVector( - PrevLoc, UndefValue::get(PrevLocTy), PrevLocShuffleMask); - Value *UpdatedPrevLoc = IRB.CreateInsertElement( - ShuffledPrevLoc, IRB.CreateLShr(CurLoc, (uint64_t)1), (uint64_t)0); - - Store = IRB.CreateStore(UpdatedPrevLoc, AFLPrevLoc); - Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - - } else -#endif - { - - Store = IRB.CreateStore(ConstantInt::get(Int32Ty, cur_loc >> 1), - AFLPrevLoc); - Store->setMetadata(M.getMDKindID("nosanitize"), MDNode::get(C, None)); - } - - // in CTX mode we have to restore the original context for the caller - - // she might be calling other functions which need the correct CTX. - // Currently this is only needed for the Ubuntu clang-6.0 bug - if (instrument_ctx && has_calls) { - Instruction *Inst = BB.getTerminator(); - if (isa(Inst) || isa(Inst)) { - IRBuilder<> Post_IRB(Inst); - - StoreInst *RestoreCtx; -#ifdef HAVE_VECTOR_INTRINSICS - if (CtxK) - RestoreCtx = IRB.CreateStore(PrevCaller, AFLPrevCaller); - else -#endif - RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); - RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), - MDNode::get(C, None)); - } - } - - inst_blocks++; - } - } - if (DumpCFG) { - int fd; -#ifndef _WIN32 - if ((fd = open(DumpCFGPath.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644)) < - 0) -#else - if ((fd = _open(DumpCFGPath.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644)) < - 0) -#endif - FATAL("Could not open/create CFG dump file."); - std::string cfg = ""; - for (auto record = entry_bb.begin(); record != entry_bb.end(); record++) { - // Dump function BB entry points - cfg += formatv("$${0}+{1}\n", record->getFirst(), - bb_to_cur_loc[record->getSecond()]); - } - for (auto record = bb_to_cur_loc.begin(); record != bb_to_cur_loc.end(); - record++) { - // Dump CFG information - auto current_bb = record->getFirst(); - Function *calling_func = current_bb->getParent(); - if (calling_func) { - auto function_name = calling_func->getName().str(); - cfg += formatv("%%{0}", function_name); - } else - cfg += "%%__"; - auto current_cur_loc = record->getSecond(); - cfg += formatv("+{0}\n", current_cur_loc); - for (auto bb_successor = succ_begin(current_bb); - bb_successor != succ_end(current_bb); bb_successor++) { - cfg += formatv("->{0}\n", bb_to_cur_loc[*bb_successor]).str(); - } - } - if (Debug) { errs() << "CFG: \n" << cfg; } - if (cfg.size() > 0 && write(fd, cfg.c_str(), cfg.length()) <= 0) - FATAL("Failed to dump CFG.\n"); - } - - /* Say something nice. */ - - /*if (!be_quiet) { - - if (!inst_blocks) - WARNF("No instrumentation targets found."); - else { - - char modeline[100]; - snprintf(modeline, sizeof(modeline), "%s%s%s%s%s", - getenv("AFL_HARDEN") ? "hardened" : "non-hardened", - getenv("AFL_USE_ASAN") ? ", ASAN" : "", - getenv("AFL_USE_MSAN") ? ", MSAN" : "", - getenv("AFL_USE_CFISAN") ? ", CFISAN" : "", - getenv("AFL_USE_UBSAN") ? ", UBSAN" : ""); - OKF("Instrumented %d locations (%s mode, ratio %u%%).", inst_blocks, - modeline, InstRatio); - - } - - }*/ - - if (Debug) { - if (!inst_blocks) - fprintf(stderr, "No instrumentation targets found.\n"); - else - fprintf(stderr, "Instrumented %d locations (ratio %u%%).\n", inst_blocks, - (unsigned)InstRatio); - } - -#ifdef USE_NEW_PM - return PA; -#else - return true; -#endif -} - -#ifndef USE_NEW_PM -static void registerAFLPass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) { - PM.add(new AFLCoverage()); -} - -static RegisterStandardPasses RegisterAFLPass( - PassManagerBuilder::EP_OptimizerLast, registerAFLPass); - -static RegisterStandardPasses RegisterAFLPass0( - PassManagerBuilder::EP_EnabledOnOptLevel0, registerAFLPass); -#endif diff --git a/libafl_cc/src/clang.rs b/libafl_cc/src/clang.rs index d8d4efba55..22959b69e1 100644 --- a/libafl_cc/src/clang.rs +++ b/libafl_cc/src/clang.rs @@ -30,8 +30,6 @@ pub enum LLVMPasses { //CmpLogIns, /// The `CmpLog` pass CmpLogRtn, - /// The AFL coverage pass - AFLCoverage, /// The Autotoken pass AutoTokens, /// The Coverage Accouting (BB metric) pass @@ -41,6 +39,8 @@ pub enum LLVMPasses { #[cfg(unix)] /// The `CmpLog` Instruction pass CmpLogInstructions, + /// Instrument caller for sancov coverage + Ctx, } impl LLVMPasses { @@ -50,8 +50,6 @@ impl LLVMPasses { match self { LLVMPasses::CmpLogRtn => PathBuf::from(env!("OUT_DIR")) .join(format!("cmplog-routines-pass.{}", dll_extension())), - LLVMPasses::AFLCoverage => PathBuf::from(env!("OUT_DIR")) - .join(format!("afl-coverage-pass.{}", dll_extension())), LLVMPasses::AutoTokens => { PathBuf::from(env!("OUT_DIR")).join(format!("autotokens-pass.{}", dll_extension())) } @@ -63,6 +61,9 @@ impl LLVMPasses { #[cfg(unix)] LLVMPasses::CmpLogInstructions => PathBuf::from(env!("OUT_DIR")) .join(format!("cmplog-instructions-pass.{}", dll_extension())), + LLVMPasses::Ctx => { + PathBuf::from(env!("OUT_DIR")).join(format!("ctx-pass.{}", dll_extension())) + } } } } diff --git a/libafl_cc/src/cmplog-routines-pass.cc b/libafl_cc/src/cmplog-routines-pass.cc index 25fe1d6e76..0a68ab544f 100644 --- a/libafl_cc/src/cmplog-routines-pass.cc +++ b/libafl_cc/src/cmplog-routines-pass.cc @@ -71,7 +71,7 @@ class CmpLogRoutines : public ModulePass { #if USE_NEW_PM PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); #else - bool runOnModule(Module &M) override; + bool runOnModule(Module &M) override; #if LLVM_VERSION_MAJOR < 4 const char *getPassName() const override { diff --git a/libafl_cc/src/ctx-pass.cc b/libafl_cc/src/ctx-pass.cc new file mode 100644 index 0000000000..1a92788c59 --- /dev/null +++ b/libafl_cc/src/ctx-pass.cc @@ -0,0 +1,224 @@ +/* + LibAFL - Ctx LLVM pass + -------------------------------------------------- + + Written by Dongjia Zhang + + Copyright 2022-2023 AFLplusplus Project. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +*/ + +#include +#include +#include "common-llvm.h" +#ifndef _WIN32 + #include + #include +#else + #include +#endif +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "llvm/Config/llvm-config.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/IR/IRBuilder.h" + +#if USE_NEW_PM + #include "llvm/Passes/PassPlugin.h" + #include "llvm/Passes/PassBuilder.h" + #include "llvm/IR/PassManager.h" +#else + #include "llvm/IR/LegacyPassManager.h" + #include "llvm/Transforms/IPO/PassManagerBuilder.h" +#endif + +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/Pass.h" +#include "llvm/IR/Constants.h" + +#include + +using namespace llvm; + +#define MAP_SIZE LIBAFL_EDGES_MAP_SIZE + +namespace { + +#if USE_NEW_PM +class CtxPass : public PassInfoMixin { + public: + CtxPass() { +#else +class CtxPass : public ModulePass { + public: + static char ID; + + CtxPass() : ModulePass(ID) { +#endif + } + +#if USE_NEW_PM + PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); +#else + bool runOnModule(Module &M) override; +#endif + + protected: + uint32_t map_size = MAP_SIZE; + + private: + bool isLLVMIntrinsicFn(StringRef &n) { + // Not interested in these LLVM's functions + if (n.startswith("llvm.")) { + return true; + } else { + return false; + } + } +}; + +} // namespace + +#if USE_NEW_PM +extern "C" ::llvm::PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK +llvmGetPassPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "CtxPass", "v0.1", + /* lambda to insert our pass into the pass pipeline. */ + [](PassBuilder &PB) { + + #if LLVM_VERSION_MAJOR <= 13 + using OptimizationLevel = typename PassBuilder::OptimizationLevel; + #endif + PB.registerOptimizerLastEPCallback( + [](ModulePassManager &MPM, OptimizationLevel OL) { + MPM.addPass(CtxPass()); + }); + }}; +} +#else +char CtxPass::ID = 0; +#endif + +#if USE_NEW_PM +PreservedAnalyses CtxPass::run(Module &M, ModuleAnalysisManager &MAM) { +#else +bool CtxPass::runOnModule(Module &M) { + +#endif + LLVMContext &C = M.getContext(); + auto moduleName = M.getName(); + IntegerType *Int8Ty = IntegerType::getInt8Ty(C); + IntegerType *Int32Ty = IntegerType::getInt32Ty(C); + + uint32_t rand_seed; + + rand_seed = time(NULL); + srand(rand_seed); + + GlobalVariable *AFLContext = new GlobalVariable( + M, Int32Ty, false, GlobalValue::ExternalLinkage, 0, "__afl_prev_ctx"); + Value *PrevCtx = + NULL; // the ctx value up until now that we save on the stack + + for (auto &F : M) { + int has_calls = 0; + + if (isIgnoreFunction(&F)) { continue; } + if (F.size() < 1) { continue; } + for (auto &BB : F) { + BasicBlock::iterator IP = BB.getFirstInsertionPt(); + IRBuilder<> IRB(&(*IP)); + if (&BB == &F.getEntryBlock()) { + // if this is the first block.. + LoadInst *PrevCtxLoad = IRB.CreateLoad(IRB.getInt32Ty(), AFLContext); + PrevCtxLoad->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + PrevCtx = PrevCtxLoad; + + // now check for if calls exists + for (auto &BB_2 : F) { + if (has_calls) { break; } + for (auto &IN : BB_2) { + CallInst *callInst = nullptr; + if ((callInst = dyn_cast(&IN))) { + Function *Callee = callInst->getCalledFunction(); + if (!Callee || Callee->size() < 1) { + continue; + } else { + has_calls = 1; + break; + } + } + } + } + + if (has_calls) { + Value *NewCtx = ConstantInt::get(Int32Ty, RandBelow(map_size)); + NewCtx = IRB.CreateXor(PrevCtx, NewCtx); + StoreInst *StoreCtx = IRB.CreateStore(NewCtx, AFLContext); + StoreCtx->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + } + } + // Restore the ctx at the end of BB + Instruction *Inst = BB.getTerminator(); + if (has_calls) { + if (isa(Inst) || isa(Inst)) { + IRBuilder<> Post_IRB(Inst); + StoreInst *RestoreCtx; + RestoreCtx = Post_IRB.CreateStore(PrevCtx, AFLContext); + RestoreCtx->setMetadata(M.getMDKindID("nosanitize"), + MDNode::get(C, None)); + } + } + } + } + +#if USE_NEW_PM + auto PA = PreservedAnalyses::all(); + return PA; +#else + return true; +#endif +} + +#if USE_NEW_PM + +#else +static void registerCtxPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + PM.add(new CtxPass()); +} + +static RegisterPass X("ctx", "ctx instrumentation pass", false, false); + +static RegisterStandardPasses RegisterCtxPass( + PassManagerBuilder::EP_OptimizerLast, registerCtxPass); + +static RegisterStandardPasses RegisterCtxPass0( + PassManagerBuilder::EP_EnabledOnOptLevel0, registerCtxPass); +#endif diff --git a/libafl_concolic/symcc_runtime/Cargo.toml b/libafl_concolic/symcc_runtime/Cargo.toml index 3d51669052..d31a9deb85 100644 --- a/libafl_concolic/symcc_runtime/Cargo.toml +++ b/libafl_concolic/symcc_runtime/Cargo.toml @@ -30,7 +30,7 @@ libafl_bolts = { path = "../../libafl_bolts", version = "0.11.2", default-featur [build-dependencies] cmake = "0.1" -bindgen = "0.69" +bindgen = "0.69.4" regex = "1" which = "4.4" symcc_libafl = { path = "../symcc_libafl", version = "0.11.2" } diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml b/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml index a8ff8ab089..4fa4f3313a 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/Cargo.toml @@ -48,7 +48,7 @@ utf8-chars = "3.0.1" env_logger = "0.10" [build-dependencies] -bindgen = "0.68.1" +bindgen = "0.69.4" cc = { version = "1.0", features = ["parallel"] } [workspace] diff --git a/libafl_qemu/libafl_qemu_build/Cargo.toml b/libafl_qemu/libafl_qemu_build/Cargo.toml index 6a3b1aa51a..c4b6907f5f 100644 --- a/libafl_qemu/libafl_qemu_build/Cargo.toml +++ b/libafl_qemu/libafl_qemu_build/Cargo.toml @@ -27,7 +27,7 @@ slirp = [] # build qemu with host libslirp (for user networking) clippy = [] # special feature for clippy, don't use in normal projects§ [dependencies] -bindgen = "0.68" +bindgen = "0.69.4" which = "4.4" json = "0.12" shell-words = "1.1" diff --git a/libafl_qemu/libqasan/hooks.c b/libafl_qemu/libqasan/hooks.c index c16b0a8250..bad0ce17bc 100644 --- a/libafl_qemu/libqasan/hooks.c +++ b/libafl_qemu/libqasan/hooks.c @@ -480,7 +480,7 @@ char *strndup(const char *s, size_t n) { QASAN_LOAD(s, l + 1); void *r = __libqasan_malloc(l + 1); __libqasan_memcpy(r, s, l); - ((char*)r)[l] = 0; + ((char *)r)[l] = 0; QASAN_DEBUG("\t\t = %p\n", r); return r; diff --git a/libafl_targets/Cargo.toml b/libafl_targets/Cargo.toml index fa2f9d4e9f..88f4ad1914 100644 --- a/libafl_targets/Cargo.toml +++ b/libafl_targets/Cargo.toml @@ -42,6 +42,8 @@ sancov_pcguard_edges = ["coverage"] sancov_pcguard_hitcounts = ["coverage"] sancov_value_profile = ["common"] sancov_8bit = [] +sancov_ngram4 = ["coverage"] +sancov_ctx = ["coverage"] sancov_cmplog = ["common"] # Defines cmp and __sanitizer_weak_hook functions. Use libfuzzer_interceptors to define interceptors (only compatible with Linux) sancov_pcguard = ["sancov_pcguard_hitcounts"] sanitizer_interfaces = [] @@ -56,7 +58,7 @@ whole_archive = [] # use +whole-archive to ensure the presence of weak symbols cmplog_extended_instrumentation = [] # support for aflpp cmplog map, we will remove this once aflpp and libafl cmplog shares the same LLVM passes. [build-dependencies] -bindgen = "0.68" +bindgen = "0.69.4" cc = { version = "1.0", features = ["parallel"] } rustversion = "1.0" @@ -65,6 +67,7 @@ libafl = { path = "../libafl", version = "0.11.2", default-features = false, fea libafl_bolts = { path = "../libafl_bolts", version = "0.11.2", default-features = false, features = [] } libc = "0.2" log = "0.4.20" +rustversion = "1.0" rangemap = "1.3" serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index f3f0f4187d..aa7c3768b2 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -5,8 +5,17 @@ use std::{env, fs::File, io::Write, path::Path}; const TWO_MB: usize = 2_621_440; const SIXTY_FIVE_KB: usize = 65_536; +#[rustversion::nightly] +fn enable_nightly() { + println!("cargo:rustc-cfg=nightly"); +} + +#[rustversion::not(nightly)] +fn enable_nightly() {} + #[allow(clippy::too_many_lines)] fn main() { + enable_nightly(); let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = out_dir.to_string_lossy().to_string(); //let out_dir_path = Path::new(&out_dir); diff --git a/libafl_targets/src/coverage.c b/libafl_targets/src/coverage.c index 7f4987a15b..e6231c454c 100644 --- a/libafl_targets/src/coverage.c +++ b/libafl_targets/src/coverage.c @@ -33,7 +33,5 @@ uint8_t *__token_stop = &__stop_libafl_token; #endif // #if defined(__ANDROID__) || defined(__HAIKU__) -MAYBE_THREAD_LOCAL prev_loc_t __afl_prev_loc[NGRAM_SIZE_MAX]; -MAYBE_THREAD_LOCAL prev_loc_t __afl_prev_caller[CTX_MAX_K]; -MAYBE_THREAD_LOCAL uint32_t __afl_prev_ctx; +uint32_t __afl_prev_ctx; MAYBE_THREAD_LOCAL prev_loc_t __afl_acc_prev_loc; diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index 34df1f1ce6..5df6d2cbfc 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -3,6 +3,7 @@ #![deny(rustdoc::broken_intra_doc_links)] #![deny(clippy::all)] #![deny(clippy::pedantic)] +#![cfg_attr(nightly, feature(portable_simd))] #![allow( clippy::unreadable_literal, clippy::type_repetition_in_bounds, @@ -66,9 +67,19 @@ extern crate alloc; include!(concat!(env!("OUT_DIR"), "/constants.rs")); -#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts",))] +#[cfg(any( + feature = "sancov_pcguard_edges", + feature = "sancov_pcguard_hitcounts", + feature = "sancov_ngram4", + feature = "sancov_ctx" +))] pub mod sancov_pcguard; -#[cfg(any(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts",))] +#[cfg(any( + feature = "sancov_pcguard_edges", + feature = "sancov_pcguard_hitcounts", + feature = "sancov_ngram4", + feature = "sancov_ctx" +))] pub use sancov_pcguard::*; #[cfg(any(feature = "sancov_cmplog", feature = "sancov_value_profile"))] diff --git a/libafl_targets/src/sancov_pcguard.rs b/libafl_targets/src/sancov_pcguard.rs index aa18211309..38d795bc89 100644 --- a/libafl_targets/src/sancov_pcguard.rs +++ b/libafl_targets/src/sancov_pcguard.rs @@ -1,8 +1,18 @@ //! [`LLVM` `PcGuard`](https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards) runtime for `LibAFL`. -use crate::coverage::{EDGES_MAP, MAX_EDGES_NUM}; +#[rustversion::nightly] +#[cfg(feature = "sancov_ngram4")] +use core::simd::num::SimdUint; + +#[cfg(any(feature = "sancov_ngram4", feature = "sancov_ctx"))] +use libafl::executors::{hooks::ExecutorHook, HasObservers}; + #[cfg(feature = "pointer_maps")] use crate::coverage::{EDGES_MAP_PTR, EDGES_MAP_PTR_NUM}; +use crate::{ + coverage::{EDGES_MAP, MAX_EDGES_NUM}, + EDGES_MAP_SIZE, +}; #[cfg(all(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))] #[cfg(not(any(doc, feature = "clippy")))] @@ -10,14 +20,122 @@ compile_error!( "the libafl_targets `sancov_pcguard_edges` and `sancov_pcguard_hitcounts` features are mutually exclusive." ); +#[cfg(feature = "sancov_ngram4")] +#[rustversion::nightly] +type Ngram4 = core::simd::u32x4; + +/// The array holding the previous locs. This is required for NGRAM-4 instrumentation +#[cfg(feature = "sancov_ngram4")] +#[rustversion::nightly] +pub static mut PREV_ARRAY: Ngram4 = Ngram4::from_array([0, 0, 0, 0]); + +/// The hook to initialize ngram everytime we run the harness +#[cfg(feature = "sancov_ngram4")] +#[rustversion::nightly] +#[derive(Default, Debug, Clone, Copy)] +pub struct NgramHook {} + +/// The hook to initialize ctx everytime we run the harness +#[cfg(feature = "sancov_ctx")] +#[derive(Default, Debug, Clone, Copy)] +pub struct CtxHook {} + +#[cfg(feature = "sancov_ngram4")] +#[rustversion::nightly] +impl ExecutorHook for NgramHook { + fn init(&mut self, _state: &mut S) {} + fn pre_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) { + unsafe { + PREV_ARRAY = Ngram4::from_array([0, 0, 0, 0]); + } + } + fn post_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) { + } +} + +#[cfg(feature = "sancov_ctx")] +impl ExecutorHook for CtxHook { + fn init(&mut self, _state: &mut S) {} + fn pre_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) { + unsafe { + __afl_prev_ctx = 0; + } + } + fn post_exec( + &mut self, + _fuzzer: &mut Z, + _state: &mut S, + _mgr: &mut EM, + _input: &I, + ) { + } +} + +#[rustversion::nightly] +#[cfg(feature = "sancov_ngram4")] +unsafe fn update_ngram(mut pos: usize) -> usize { + #[cfg(feature = "sancov_ngram4")] + { + PREV_ARRAY = PREV_ARRAY.rotate_elements_right::<1>(); + PREV_ARRAY.as_mut_array()[0] = pos as u32; + let reduced = PREV_ARRAY.reduce_xor() as usize; + pos ^= reduced; + pos %= EDGES_MAP_SIZE; + } + pos +} + +#[rustversion::not(nightly)] +#[cfg(feature = "sancov_ngram4")] +unsafe fn update_ngram(pos: usize) -> usize { + pos +} + +extern "C" { + /// The ctx variable + pub static mut __afl_prev_ctx: u32; +} + /// Callback for sancov `pc_guard` - usually called by `llvm` on each block or edge. /// /// # Safety /// Dereferences `guard`, reads the position from there, then dereferences the [`EDGES_MAP`] at that position. /// Should usually not be called directly. #[no_mangle] +#[allow(unused_assignments)] pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) { - let pos = *guard as usize; + let mut pos = *guard as usize; + + #[cfg(feature = "sancov_ngram4")] + { + pos = update_ngram(pos); + // println!("Wrinting to {} {}", pos, EDGES_MAP_SIZE); + } + + #[cfg(feature = "sancov_ctx")] + { + pos ^= __afl_prev_ctx as usize; + // println!("Wrinting to {} {}", pos, EDGES_MAP_SIZE); + } + #[cfg(feature = "pointer_maps")] { #[cfg(feature = "sancov_pcguard_edges")] @@ -72,7 +190,7 @@ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32 #[cfg(not(feature = "pointer_maps"))] { MAX_EDGES_NUM = MAX_EDGES_NUM.wrapping_add(1); - assert!((MAX_EDGES_NUM <= EDGES_MAP.len()), "The number of edges reported by SanitizerCoverage exceed the size of the edges map ({}). Use the LIBAFL_EDGES_MAP_SIZE env to increase it at compile time.", EDGES_MAP.len()); + // assert!((MAX_EDGES_NUM <= EDGES_MAP.len()), "The number of edges reported by SanitizerCoverage exceed the size of the edges map ({}). Use the LIBAFL_EDGES_MAP_SIZE env to increase it at compile time.", EDGES_MAP.len()); } } }