diff --git a/fuzzers/forkserver_libafl_cc/Cargo.toml b/fuzzers/forkserver_libafl_cc/Cargo.toml new file mode 100644 index 0000000000..f8e4b9b63a --- /dev/null +++ b/fuzzers/forkserver_libafl_cc/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "forkserver_libafl_cc" +version = "0.8.2" +authors = ["ergrelet "] +edition = "2021" + +[features] +default = ["std"] +std = [] +# Forces a crash +crash = [] + +[profile.release] +lto = true +codegen-units = 1 +opt-level = 3 +debug = true + +[build-dependencies] +cc = { version = "1.0", features = ["parallel"] } +which = { version = "4.0.2" } + +[dependencies] +libafl = { path = "../../libafl/", features = ["default"] } +clap = { version = "4.0", features = ["derive"] } +nix = "0.25" +libafl_targets = { path = "../../libafl_targets/" } +libafl_cc = { path = "../../libafl_cc/" } + +[lib] +name = "libforkserver_libafl_cc" +crate-type = ["staticlib"] diff --git a/fuzzers/forkserver_libafl_cc/Makefile.toml b/fuzzers/forkserver_libafl_cc/Makefile.toml new file mode 100644 index 0000000000..b8b46e6ca5 --- /dev/null +++ b/fuzzers/forkserver_libafl_cc/Makefile.toml @@ -0,0 +1,115 @@ +# Variables +[env] +FUZZER_NAME='fuzzer_libafl_cc' +CARGO_TARGET_DIR = { value = "${PROJECT_DIR}/target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } } +LIBAFL_CC = '${CARGO_TARGET_DIR}/release/libafl_cc' +LIBAFL_CXX = '${CARGO_TARGET_DIR}/release/libafl_cxx' +FUZZER = '${CARGO_TARGET_DIR}/release/${FUZZER_NAME}' +PROJECT_DIR = { script = ["pwd"] } + +[tasks.unsupported] +script_runner="@shell" +script=''' +echo "Cargo-make not integrated yet on this" +''' + +# Compilers +[tasks.cxx] +linux_alias = "cxx_unix" +mac_alias = "cxx_unix" +windows_alias = "unsupported" + +[tasks.cxx_unix] +command = "cargo" +args = ["build" , "--release"] + +[tasks.cc] +linux_alias = "cc_unix" +mac_alias = "cc_unix" +windows_alias = "unsupported" + +[tasks.cc_unix] +command = "cargo" +args = ["build" , "--release"] + +[tasks.crash_cxx] +linux_alias = "crash_cxx_unix" +mac_alias = "crash_cxx_unix" +windows_alias = "unsupported" + +[tasks.crash_cxx_unix] +command = "cargo" +args = ["build" , "--release", "--features=crash"] + +[tasks.crash_cc] +linux_alias = "crash_cc_unix" +mac_alias = "crash_cc_unix" +windows_alias = "unsupported" + +[tasks.crash_cc_unix] +command = "cargo" +args = ["build" , "--release", "--features=crash"] + +# Harness +[tasks.fuzzer] +linux_alias = "fuzzer_unix" +mac_alias = "fuzzer_unix" +windows_alias = "unsupported" + +[tasks.fuzzer_unix] +command = "${CARGO_TARGET_DIR}/release/libafl_cc" +args = ["${PROJECT_DIR}/src/program.c", "-o", "${FUZZER_NAME}", "-lm"] +dependencies = [ "cxx", "cc" ] + +# Crashing Harness +[tasks.fuzzer_crash] +linux_alias = "fuzzer_crash_unix" +mac_alias = "fuzzer_crash_unix" +windows_alias = "unsupported" + +[tasks.fuzzer_crash_unix] +command = "${CARGO_TARGET_DIR}/release/libafl_cc" +args = ["${PROJECT_DIR}/src/program.c", "-o", "${FUZZER_NAME}_crash", "-lm"] +dependencies = [ "crash_cxx", "crash_cc" ] + +# Run the fuzzer +[tasks.run] +linux_alias = "run_unix" +mac_alias = "run_unix" +windows_alias = "unsupported" + +[tasks.run_unix] +script_runner = "@shell" +script=''' +taskset -c 1 ${CARGO_TARGET_DIR}/release/${CARGO_MAKE_PROJECT_NAME} ./${FUZZER_NAME} ./corpus/ -t 1000 +''' +dependencies = [ "fuzzer" ] + + +# Run the fuzzer with a crash +[tasks.crash] +linux_alias = "crash_unix" +mac_alias = "crash_unix" +windows_alias = "unsupported" + +[tasks.crash_unix] +script_runner = "@shell" +script=''' +taskset -c 1 ${CARGO_TARGET_DIR}/release/${CARGO_MAKE_PROJECT_NAME} ./${FUZZER_NAME}_crash ./corpus/ -t 1000 +''' +dependencies = [ "fuzzer_crash" ] + +# 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} +cargo clean +''' diff --git a/fuzzers/forkserver_libafl_cc/README.md b/fuzzers/forkserver_libafl_cc/README.md new file mode 100644 index 0000000000..a3a93e5041 --- /dev/null +++ b/fuzzers/forkserver_libafl_cc/README.md @@ -0,0 +1,13 @@ +# Simple Forkserver Fuzzer + +This is a simple example fuzzer to fuzz an executable instrumented by libafl_cc. + +## Usage + +You can build this example by running `cargo make fuzzer`. +This compiles, libafl_cc, the fuzzer and the example harness program in +`src/program.c` with libafl_cc. + +## Run + +You can run this example by running `cargo make run`. diff --git a/fuzzers/forkserver_libafl_cc/corpus/testfile b/fuzzers/forkserver_libafl_cc/corpus/testfile new file mode 100644 index 0000000000..72943a16fb --- /dev/null +++ b/fuzzers/forkserver_libafl_cc/corpus/testfile @@ -0,0 +1 @@ +aaa diff --git a/fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs b/fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs new file mode 100644 index 0000000000..4cfd4af756 --- /dev/null +++ b/fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs @@ -0,0 +1,45 @@ +use std::env; + +use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; + +pub fn main() { + let 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(); + + let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() { + "cc" => false, + "++" | "pp" | "xx" => true, + _ => panic!("Could not figure out if c or c++ wrapper was called. Expected {dir:?} to end with c or cxx"), + }; + + dir.pop(); + + let mut cc = ClangWrapper::new(); + if let Some(code) = cc + .cpp(is_cpp) + // silence the compiler wrapper output, needed for some configure scripts. + .silence(true) + .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)") + .add_arg("-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : (*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff ? 0 : *__afl_fuzz_len)") + .add_arg("-D__AFL_INIT()=libafl_start_forkserver()") + // Link with libafl's forkserver implementation + .link_staticlib(&dir, "libforkserver_libafl_cc") + .run() + .expect("Failed to run the wrapped compiler") + { + std::process::exit(code); + } + } else { + panic!("LibAFL CC: No Arguments given"); + } +} diff --git a/fuzzers/forkserver_libafl_cc/src/bin/libafl_cxx.rs b/fuzzers/forkserver_libafl_cc/src/bin/libafl_cxx.rs new file mode 100644 index 0000000000..dabd22971a --- /dev/null +++ b/fuzzers/forkserver_libafl_cc/src/bin/libafl_cxx.rs @@ -0,0 +1,5 @@ +pub mod libafl_cc; + +fn main() { + libafl_cc::main(); +} diff --git a/fuzzers/forkserver_libafl_cc/src/lib.rs b/fuzzers/forkserver_libafl_cc/src/lib.rs new file mode 100644 index 0000000000..2a56df02cd --- /dev/null +++ b/fuzzers/forkserver_libafl_cc/src/lib.rs @@ -0,0 +1,9 @@ +use libafl_targets::{map_shared_memory, start_forkserver}; + +#[no_mangle] +pub fn libafl_start_forkserver() { + // Map shared memory region for the edge coverage map + map_shared_memory(); + // Start the forkserver + start_forkserver(); +} diff --git a/fuzzers/forkserver_libafl_cc/src/main.rs b/fuzzers/forkserver_libafl_cc/src/main.rs new file mode 100644 index 0000000000..af0746c7b3 --- /dev/null +++ b/fuzzers/forkserver_libafl_cc/src/main.rs @@ -0,0 +1,215 @@ +use core::time::Duration; +use std::path::PathBuf; + +use clap::{self, Parser}; +use libafl::{ + bolts::{ + current_nanos, + rands::StdRand, + shmem::{ShMem, ShMemProvider, UnixShMemProvider}, + tuples::{tuple_list, MatchName, Merge}, + AsMutSlice, + }, + corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, + events::SimpleEventManager, + executors::{ + forkserver::{ForkserverExecutor, TimeoutForkserverExecutor}, + HasObservers, + }, + feedback_and_fast, feedback_or, + feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + inputs::BytesInput, + monitors::SimpleMonitor, + mutators::{scheduled::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens}, + observers::{HitcountsMapObserver, MapObserver, StdMapObserver, TimeObserver}, + schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, + stages::mutational::StdMutationalStage, + state::{HasCorpus, HasMetadata, StdState}, +}; +use nix::sys::signal::Signal; + +/// The commandline args this fuzzer accepts +#[derive(Debug, Parser)] +#[command( + name = "forkserver_libafl_cc", + about = "This is a simple example fuzzer to fuzz a executable instrumented by libafl_cc.", + author = "ergrelet " +)] +struct Opt { + #[arg( + help = "The instrumented binary we want to fuzz", + name = "EXEC", + required = true + )] + executable: String, + + #[arg( + help = "The directory to read initial inputs from ('seeds')", + name = "INPUT_DIR", + required = true + )] + in_dir: PathBuf, + + #[arg( + help = "Timeout for each individual execution, in milliseconds", + short = 't', + long = "timeout", + default_value = "1200" + )] + timeout: u64, + + #[arg( + help = "If not set, the child's stdout and stderror will be redirected to /dev/null", + short = 'd', + long = "debug-child", + default_value = "false" + )] + debug_child: bool, + + #[arg( + help = "Arguments passed to the target", + name = "arguments", + num_args(1..), + allow_hyphen_values = true, + )] + arguments: Vec, + + #[arg( + help = "Signal used to stop child", + short = 's', + long = "signal", + value_parser = str::parse::, + default_value = "SIGKILL" + )] + signal: Signal, +} + +#[allow(clippy::similar_names)] +pub fn main() { + const MAP_SIZE: usize = 65536; + + let opt = Opt::parse(); + + let corpus_dirs: Vec = [opt.in_dir].to_vec(); + + // The unix shmem provider supported by LibAFL for shared memory + let mut shmem_provider = UnixShMemProvider::new().unwrap(); + + // The coverage map shared between observer and executor + let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap(); + // let the forkserver know the shmid + shmem.write_to_env("__AFL_SHM_ID").unwrap(); + let shmem_buf = shmem.as_mut_slice(); + + // Create an observation channel using the signals map + let edges_observer = + unsafe { HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf)) }; + + // 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::new_tracking(&edges_observer, true, false), + // Time feedback, this one does not need a feedback state + TimeFeedback::new_with_observer(&time_observer) + ); + + // A feedback to choose if an input is a solution or not + // We want to do the same crash deduplication that AFL does + let mut objective = feedback_and_fast!( + // Must be a crash + CrashFeedback::new(), + // Take it onlt if trigger new coverage over crashes + MaxMapFeedback::new(&edges_observer) + ); + + // create a State from scratch + let mut state = 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(PathBuf::from("./crashes")).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(); + + // The Monitor trait define how the fuzzer stats are reported to the user + let monitor = SimpleMonitor::new(|s| println!("{s}")); + + // The event manager handle the various events generated during the fuzzing loop + // such as the notification of the addition of a new item to the corpus + let mut mgr = SimpleEventManager::new(monitor); + + // 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); + + // If we should debug the child + let debug_child = opt.debug_child; + + // Create the executor for the forkserver + let args = opt.arguments; + + let mut tokens = Tokens::new(); + let mut forkserver = ForkserverExecutor::builder() + .program(opt.executable) + .debug_child(debug_child) + .shmem_provider(&mut shmem_provider) + .autotokens(&mut tokens) + .parse_afl_cmdline(args) + .coverage_map_size(MAP_SIZE) + .build(tuple_list!(time_observer, edges_observer)) + .unwrap(); + + if let Some(dynamic_map_size) = forkserver.coverage_map_size() { + forkserver + .observers_mut() + .match_name_mut::>>("shared_mem") + .unwrap() + .downsize_map(dynamic_map_size); + } + + let mut executor = TimeoutForkserverExecutor::with_signal( + forkserver, + Duration::from_millis(opt.timeout), + opt.signal, + ) + .expect("Failed to create the executor."); + + // In case the corpus is empty (on first run), reset + if state.corpus().count() < 1 { + state + .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs) + .unwrap_or_else(|err| { + panic!( + "Failed to load initial corpus at {:?}: {:?}", + &corpus_dirs, err + ) + }); + println!("We imported {} inputs from disk.", state.corpus().count()); + } + + state.add_metadata(tokens); + + // Setup a mutational stage with a basic bytes mutator + let mutator = + StdScheduledMutator::with_max_stack_pow(havoc_mutations().merge(tokens_mutations()), 6); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); + + fuzzer + .fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr) + .expect("Error in the fuzzing loop"); +} diff --git a/fuzzers/forkserver_libafl_cc/src/program.c b/fuzzers/forkserver_libafl_cc/src/program.c new file mode 100644 index 0000000000..7b65e79e7a --- /dev/null +++ b/fuzzers/forkserver_libafl_cc/src/program.c @@ -0,0 +1,37 @@ +#include +#include +#include + +// The following line is needed for shared memory testcase fuzzing +__AFL_FUZZ_INIT(); + +void vuln(char *buf) { + if (strcmp(buf, "vuln") == 0) { abort(); } +} + +int main(int argc, char **argv) { + // Start the forkserver at this point (i.e., forks will happen here) + __AFL_INIT(); + + // The following five lines are for normal fuzzing. + /* + FILE *file = stdin; + if (argc > 1) { file = fopen(argv[1], "rb"); } + char buf[16]; + char *p = fgets(buf, 16, file); + buf[15] = 0; + */ + + // The following line is also needed for shared memory testcase fuzzing + unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT + + // printf("input: %s\n", buf); + if (buf[0] == 'b') { + if (buf[1] == 'a') { + if (buf[2] == 'd') { abort(); } + } + } + vuln((char *)buf); + + return 0; +} \ No newline at end of file diff --git a/libafl_targets/src/forkserver.c b/libafl_targets/src/forkserver.c index 217a01db70..d229c5ecf2 100644 --- a/libafl_targets/src/forkserver.c +++ b/libafl_targets/src/forkserver.c @@ -7,17 +7,24 @@ #include #ifndef USEMMAP #include +#else + #include + #include + #include #endif #include #include -#define write_error(s) fprintf(stderr, "Error at %s:%d: %s\n", __FILE__, __LINE__, s) +#define write_error(s) \ + fprintf(stderr, "Error at %s:%d: %s\n", __FILE__, __LINE__, s) // AFL++ constants #define FORKSRV_FD 198 #define MAX_FILE (1024 * 1024) #define SHMEM_FUZZ_HDR_SIZE 4 +#define SHM_ENV_VAR "__AFL_SHM_ID" #define SHM_FUZZ_ENV_VAR "__AFL_SHM_FUZZ_ID" +#define DEFAULT_PERMISSION 0600 /* Reporting errors */ #define FS_OPT_ERROR 0xf800008f @@ -45,18 +52,21 @@ #define FS_OPT_SET_MAPSIZE(x) \ (x <= 1 || x > FS_OPT_MAX_MAPSIZE ? 0 : ((x - 1) << 1)) -// Set by this macro https://github.com/AFLplusplus/AFLplusplus/blob/stable/src/afl-cc.c#L993 +// Set by this macro +// https://github.com/AFLplusplus/AFLplusplus/blob/stable/src/afl-cc.c#L993 int __afl_sharedmem_fuzzing __attribute__((weak)); -extern size_t __afl_map_size; -extern uint8_t* __token_start; -extern uint8_t* __token_stop; +extern uint8_t *__afl_area_ptr; +extern size_t __afl_map_size; +extern uint8_t *__token_start; +extern uint8_t *__token_stop; -uint8_t* __afl_fuzz_ptr; +uint8_t *__afl_fuzz_ptr; static uint32_t __afl_fuzz_len_local; -uint32_t* __afl_fuzz_len = &__afl_fuzz_len_local; +uint32_t *__afl_fuzz_len = &__afl_fuzz_len_local; +int already_initialized_shm; int already_initialized_forkserver; static int child_pid; @@ -65,48 +75,98 @@ static void (*old_sigterm_handler)(int) = 0; static uint8_t is_persistent; void __afl_set_persistent_mode(uint8_t mode) { - is_persistent = mode; - } /* Error reporting to forkserver controller */ static void send_forkserver_error(int error) { - uint32_t status; if (!error || error > 0xffff) return; status = (FS_OPT_ERROR | FS_OPT_SET_ERROR(error)); if (write(FORKSRV_FD + 1, (char *)&status, 4) != 4) { return; } - } /* Ensure we kill the child on termination */ static void at_exit(int signal) { - (void)signal; if (child_pid > 0) { - kill(child_pid, SIGKILL); child_pid = -1; - } _exit(0); - } /* SHM fuzzing setup. */ -static void map_input_shared_memory() { +void __afl_map_shm(void) { + if (already_initialized_shm) return; + already_initialized_shm = 1; + char *id_str = getenv(SHM_ENV_VAR); + + if (id_str) { +#ifdef USEMMAP + const char *shm_file_path = id_str; + int shm_fd = -1; + unsigned char *shm_base = NULL; + + /* create the shared memory segment as if it was a file */ + shm_fd = shm_open(shm_file_path, O_RDWR, DEFAULT_PERMISSION); + if (shm_fd == -1) { + fprintf(stderr, "shm_open() failed\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); + exit(1); + } + + shm_base = + mmap(0, __afl_map_size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); + + close(shm_fd); + shm_fd = -1; + + if (shm_base == MAP_FAILED) { + fprintf(stderr, "mmap() failed\n"); + perror("mmap for map"); + send_forkserver_error(FS_ERROR_MMAP); + exit(2); + } + + __afl_area_ptr = shm_base; +#else + uint32_t shm_id = atoi(id_str); + __afl_area_ptr = (uint8_t *)shmat(shm_id, NULL, 0); + + /* Whooooops. */ + + if (!__afl_area_ptr || __afl_area_ptr == (void *)-1) { + send_forkserver_error(FS_ERROR_SHMAT); + perror("shmat for map"); + exit(1); + } + +#endif + + /* Write something into the bitmap so that even with low AFL_INST_RATIO, + our parent doesn't give up on us. */ + + __afl_area_ptr[0] = 1; + } else { + fprintf(stderr, + "Error: variable for edge coverage shared memory is not set\n"); + send_forkserver_error(FS_ERROR_SHM_OPEN); + exit(1); + } +} + +static void map_input_shared_memory() { char *id_str = getenv(SHM_FUZZ_ENV_VAR); if (id_str) { - - uint8_t* map = NULL; + uint8_t *map = NULL; #ifdef USEMMAP const char *shm_file_path = id_str; @@ -115,48 +175,41 @@ static void map_input_shared_memory() { /* create the shared memory segment as if it was a file */ shm_fd = shm_open(shm_file_path, O_RDWR, DEFAULT_PERMISSION); if (shm_fd == -1) { - fprintf(stderr, "shm_open() failed for fuzz\n"); send_forkserver_error(FS_ERROR_SHM_OPEN); exit(1); - } - map = (uint8_t* )mmap(0, MAX_FILE + sizeof(uint32_t), PROT_READ, MAP_SHARED, shm_fd, 0); + map = (uint8_t *)mmap(0, MAX_FILE + sizeof(uint32_t), PROT_READ, MAP_SHARED, + shm_fd, 0); #else uint32_t shm_id = atoi(id_str); - map = (uint8_t* )shmat(shm_id, NULL, 0); + map = (uint8_t *)shmat(shm_id, NULL, 0); #endif /* Whooooops. */ if (!map || map == (void *)-1) { - perror("Could not access fuzzing shared memory"); send_forkserver_error(FS_ERROR_SHM_OPEN); exit(1); - } - __afl_fuzz_len = (uint32_t* )map; + __afl_fuzz_len = (uint32_t *)map; __afl_fuzz_ptr = map + sizeof(uint32_t); } else { - fprintf(stderr, "Error: variable for fuzzing shared memory is not set\n"); send_forkserver_error(FS_ERROR_SHM_OPEN); exit(1); - } - } /* Fork server logic. */ void __afl_start_forkserver(void) { - if (already_initialized_forkserver) return; already_initialized_forkserver = 1; @@ -175,24 +228,14 @@ void __afl_start_forkserver(void) { void (*old_sigchld_handler)(int) = signal(SIGCHLD, SIG_DFL); if (__afl_map_size <= FS_OPT_MAX_MAPSIZE) { - status_for_fsrv |= (FS_OPT_SET_MAPSIZE(__afl_map_size) | FS_OPT_MAPSIZE); - } int autodict_on = __token_start != NULL && __token_stop != NULL; - if (autodict_on) { - - status_for_fsrv |= FS_OPT_AUTODICT; - - } + if (autodict_on) { status_for_fsrv |= FS_OPT_AUTODICT; } if (__afl_sharedmem_fuzzing != 0) { status_for_fsrv |= FS_OPT_SHDMEM_FUZZ; } - if (status_for_fsrv) { - - status_for_fsrv |= FS_OPT_ENABLED; - - } + if (status_for_fsrv) { status_for_fsrv |= FS_OPT_ENABLED; } memcpy(tmp, &status_for_fsrv, 4); @@ -202,73 +245,57 @@ void __afl_start_forkserver(void) { if (write(FORKSRV_FD + 1, tmp, 4) != 4) { return; } if (__afl_sharedmem_fuzzing || autodict_on) { - if (read(FORKSRV_FD, &was_killed, 4) != 4) _exit(1); - if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { - + if ((was_killed & (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) == + (FS_OPT_ENABLED | FS_OPT_SHDMEM_FUZZ)) { map_input_shared_memory(); - } - if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) == (FS_OPT_ENABLED | FS_OPT_AUTODICT) && autodict_on) { - + if ((was_killed & (FS_OPT_ENABLED | FS_OPT_AUTODICT)) == + (FS_OPT_ENABLED | FS_OPT_AUTODICT) && + autodict_on) { // great lets pass the dictionary through the forkserver FD uint32_t len = (__token_stop - __token_start), offset = 0; if (write(FORKSRV_FD + 1, &len, 4) != 4) { - write_error("could not send dictionary len"); _exit(1); - } while (len != 0) { - int32_t ret; ret = write(FORKSRV_FD + 1, __token_start + offset, len); if (ret < 1) { - write_error("could not send dictionary"); _exit(1); - } len -= ret; offset += ret; - } } else { - // uh this forkserver does not understand extended option passing // or does not want the dictionary if (!__afl_fuzz_ptr) already_read_first = 1; - } - } while (1) { - int status; /* Wait for parent by reading from the pipe. Abort if read fails. */ if (already_read_first) { - already_read_first = 0; } else { - if (read(FORKSRV_FD, &was_killed, 4) != 4) { - // write_error("read from afl-fuzz"); _exit(1); - } - } /* If we stopped the child in persistent mode, but there was a race @@ -276,33 +303,25 @@ void __afl_start_forkserver(void) { process. */ if (child_stopped && was_killed) { - child_stopped = 0; if (waitpid(child_pid, &status, 0) < 0) { - write_error("child_stopped && was_killed"); _exit(1); - } - } if (!child_stopped) { - /* Once woken up, create a clone of our process. */ child_pid = fork(); if (child_pid < 0) { - write_error("fork"); _exit(1); - } /* In child process: close fds, resume execution. */ if (!child_pid) { - //(void)nice(-20); signal(SIGCHLD, old_sigchld_handler); @@ -311,33 +330,26 @@ void __afl_start_forkserver(void) { close(FORKSRV_FD); close(FORKSRV_FD + 1); return; - } } else { - /* Special handling for persistent mode: if the child is alive but currently stopped, simply restart it with SIGCONT. */ kill(child_pid, SIGCONT); child_stopped = 0; - } /* In parent process: write PID to pipe, then wait for child. */ if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) { - write_error("write to afl-fuzz"); _exit(1); - } if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0) { - write_error("waitpid"); _exit(1); - } /* In persistent mode, the child stops itself with SIGSTOP to indicate @@ -349,12 +361,8 @@ void __afl_start_forkserver(void) { /* Relay wait status to pipe, then loop back. */ if (write(FORKSRV_FD + 1, &status, 4) != 4) { - write_error("writing to afl-fuzz"); _exit(1); - } - } - } diff --git a/libafl_targets/src/forkserver.rs b/libafl_targets/src/forkserver.rs index 4519c66945..ed9a595c6e 100644 --- a/libafl_targets/src/forkserver.rs +++ b/libafl_targets/src/forkserver.rs @@ -1,8 +1,19 @@ //! Forkserver logic into targets extern "C" { + /// Map a shared memory region for the edge coverage map. + fn __afl_map_shm(); /// Start the forkserver. - fn __afl_start_forkserver() -> !; + fn __afl_start_forkserver(); +} + +/// Map a shared memory region for the edge coverage map. +/// +/// # Note +/// +/// The function's logic is written in C and this code is a wrapper. +pub fn map_shared_memory() { + unsafe { __afl_map_shm() } } /// Start the forkserver from this point. Any shared memory must be created before. @@ -10,6 +21,6 @@ extern "C" { /// # Note /// /// The forkserver logic is written in C and this code is a wrapper. -pub fn start_forkserver() -> ! { +pub fn start_forkserver() { unsafe { __afl_start_forkserver() } }