diff --git a/afl/src/events/llmp.rs b/afl/src/events/llmp.rs index 1085efbec1..206edf8828 100644 --- a/afl/src/events/llmp.rs +++ b/afl/src/events/llmp.rs @@ -400,6 +400,19 @@ impl LlmpSender where SH: ShMem, { + pub fn new(id: u32, keep_pages_forever: bool) -> Result { + Ok(Self { + id, + last_msg_sent: 0 as *mut LlmpMsg, + out_maps: vec![LlmpSharedMap::new( + 0, + SH::new_map(new_map_size(LLMP_PREF_INITIAL_MAP_SIZE))?, + )], + // drop pages to the broker if it already read them + keep_pages_forever, + }) + } + /// Reattach to a vacant out_map, to with a previous sender stored the information in an env before. #[cfg(feature = "std")] pub fn on_existing_from_env(env_name: &str) -> Result { @@ -890,7 +903,7 @@ where } } - /// Returns the next message, tag, buf, looping until it becomes available + /// Returns the next sender, tag, buf, looping until it becomes available #[inline] pub fn recv_buf_blocking(&mut self) -> Result<(u32, u32, &[u8]), AflError> { unsafe { @@ -912,7 +925,7 @@ where { /// Shmem containg the actual (unsafe) page, /// shared between one LlmpSender and one LlmpReceiver - shmem: SH, + pub shmem: SH, } // TODO: May be obsolete diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index 890aa2c891..3fab16dc4e 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -118,7 +118,7 @@ pub trait Stats { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct SimpleStats where F: FnMut(String), @@ -743,6 +743,12 @@ where phantom: PhantomData, }) } + + /// If a client respawns, it may reuse the existing connection, previously stored by LlmpClient::to_env + /// Std uses AflShmem. + pub fn existing_client_from_env_std(env_name: &str, stats: ST) -> Result { + Self::existing_client_from_env(env_name, stats) + } } impl LlmpEventManager @@ -768,6 +774,19 @@ where }) } + /// If a client respawns, it may reuse the existing connection, previously stored by LlmpClient::to_env + pub fn existing_client_from_env(env_name: &str, stats: ST) -> Result { + Ok(Self { + llmp: llmp::LlmpConnection::IsClient { + client: LlmpClient::on_existing_from_env(env_name)?, + }, + // Inserting a nop-stats element here so rust won't complain. + // In any case, the client won't currently use it. + stats: stats, + phantom: PhantomData, + }) + } + /// A client on an existing map pub fn for_client(client: LlmpClient, stats: ST) -> Self { Self { @@ -777,6 +796,17 @@ where } } + #[cfg(feature = "std")] + /// Write the config for a client eventmgr to env vars, a new client can reattach using existing_client_from_env + pub fn to_env(&self, env_name: &str) { + match &self.llmp { + llmp::LlmpConnection::IsBroker { broker: _ } => { + todo!("There is probably no use storing the broker to env. Client only for now") + } + llmp::LlmpConnection::IsClient { client } => client.to_env(env_name).unwrap(), + } + } + /// Returns if we are the broker pub fn is_broker(&self) -> bool { match self.llmp { diff --git a/afl/src/events/shmem.rs b/afl/src/events/shmem.rs index 0baa452584..4e436b066a 100644 --- a/afl/src/events/shmem.rs +++ b/afl/src/events/shmem.rs @@ -60,6 +60,14 @@ struct shmid_ds { /// A Shared map pub trait ShMem: Sized + Debug { + /// Creates a new map with the given size + fn new_map(map_size: usize) -> Result; + + /// Creates a new reference to the same map + fn clone_ref(old_ref: &Self) -> Result { + Self::existing_from_shm_slice(old_ref.shm_slice(), old_ref.map().len()) + } + /// Creates a nes variable with the given name, strigified to 20 bytes. fn existing_from_shm_slice(map_str_bytes: &[u8; 20], map_size: usize) -> Result; @@ -73,9 +81,6 @@ pub trait ShMem: Sized + Debug { Self::existing_from_shm_slice(&slice, map_size) } - /// Creates a new map with the given size - fn new_map(map_size: usize) -> Result; - /// The string to identify this shm fn shm_str(&self) -> String { let bytes = self.shm_slice(); diff --git a/afl/src/executors/inmemory.rs b/afl/src/executors/inmemory.rs index 875e2d7f0e..dbb88bed74 100644 --- a/afl/src/executors/inmemory.rs +++ b/afl/src/executors/inmemory.rs @@ -20,9 +20,16 @@ where I: Input + HasTargetBytes, OT: ObserversTuple, { - harness: HarnessFunction, - observers: OT, + /// The name of this executor instance, to address it from other components name: &'static str, + /// The harness function, being executed for each fuzzing loop execution + harness: HarnessFunction, + /// The observers, observing each run + observers: OT, + /* + /// A special function being called right before the process crashes. It may save state to restore fuzzing after respawn. + on_crash_fn: Option>, + */ } impl Executor for InMemoryExecutor @@ -75,11 +82,22 @@ where I: Input + HasTargetBytes, OT: ObserversTuple, { - pub fn new(name: &'static str, harness_fn: HarnessFunction, observers: OT) -> Self { + /// Create a new in mem executor. + /// * `name` - the name of this executor (to address it along the way) + /// * `harness_fn` - the harness, executiong the function + /// * `on_crash_fn` - When an in-mem harness crashes, it may safe some state to continue fuzzing later. + /// Do that that in this function. The program will crash afterwards. + /// * `observers` - the observers observing the target during execution + pub fn new( + name: &'static str, + harness_fn: HarnessFunction, + observers: OT, /*on_crash_fn: Option*/ + ) -> Self { Self { harness: harness_fn, - observers: observers, - name: name, + //on_crash_fn, + observers, + name, } } } diff --git a/fuzzers/libfuzzer_libpng/src/mod.rs b/fuzzers/libfuzzer_libpng/src/mod.rs index 7f46839373..c454c338fa 100644 --- a/fuzzers/libfuzzer_libpng/src/mod.rs +++ b/fuzzers/libfuzzer_libpng/src/mod.rs @@ -1,31 +1,37 @@ -#![cfg_attr(not(feature = "std"), no_std)] - #[macro_use] extern crate clap; -extern crate alloc; use clap::{App, Arg}; -use std::env; -use std::path::PathBuf; +use std::{env, path::PathBuf, process::Command}; -use afl::corpus::Corpus; -use afl::corpus::InMemoryCorpus; -use afl::engines::Engine; -use afl::engines::Fuzzer; -use afl::engines::State; -use afl::engines::StdFuzzer; -use afl::events::{LlmpEventManager, SimpleStats}; -use afl::executors::inmemory::InMemoryExecutor; -use afl::executors::{Executor, ExitKind}; -use afl::feedbacks::MaxMapFeedback; -use afl::generators::RandPrintablesGenerator; -use afl::mutators::scheduled::HavocBytesMutator; -use afl::mutators::HasMaxSize; -use afl::observers::StdMapObserver; -use afl::stages::mutational::StdMutationalStage; -use afl::tuples::tuple_list; -use afl::utils::StdRand; +use afl::{ + corpus::{Corpus, InMemoryCorpus}, + engines::{Engine, Fuzzer, State, StdFuzzer}, + events::{ + llmp::LlmpReceiver, + llmp::LlmpSender, + shmem::{AflShmem, ShMem}, + LlmpEventManager, SimpleStats, + }, + executors::{inmemory::InMemoryExecutor, Executor, ExitKind}, + feedbacks::MaxMapFeedback, + generators::RandPrintablesGenerator, + mutators::{scheduled::HavocBytesMutator, HasMaxSize}, + observers::StdMapObserver, + stages::mutational::StdMutationalStage, + tuples::tuple_list, + utils::StdRand, +}; +/// The llmp connection from the actual fuzzer to the process supervising it +const ENV_FUZZER_PARENT_SENDER: &str = &"_AFL_ENV_FUZZER_PARENT_SENDER"; +/// The llmp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages) +const ENV_FUZZER_BROKER_CLIENT: &str = &"_AFL_ENV_FUZZER_BROKER_CLIENT"; + +/// The name of the coverage map observer, to find it again in the observer list +const NAME_COV_MAP: &str = "cov_map"; + +/// We will interact with a c++ target, so use external c functionality extern "C" { /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; @@ -38,6 +44,7 @@ extern "C" { static __lafl_max_edges_size: u32; } +/// The wrapped harness function, calling out to the llvm-style libfuzzer harness fn harness(_executor: &dyn Executor, buf: &[u8]) -> ExitKind { unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()); @@ -45,8 +52,6 @@ fn harness(_executor: &dyn Executor, buf: &[u8]) -> ExitKind { ExitKind::Ok } -const NAME_COV_MAP: &str = "cov_map"; - pub fn main() { let matches = App::new("libAFLrs fuzzer harness") .about("libAFLrs fuzzer harness help options.") @@ -109,15 +114,57 @@ pub fn main() { let mut corpus = InMemoryCorpus::new(); let mut generator = RandPrintablesGenerator::new(32); let stats = SimpleStats::new(|s| println!("{}", s)); - let mut mgr = LlmpEventManager::new_on_port_std(broker_port, stats).unwrap(); + let mut mgr; - if mgr.is_broker() { - println!("Doing broker things. Run this tool again to start fuzzing in a client."); - mgr.broker_loop().unwrap(); + // We start ourself as child process to actually fuzz + if std::env::var(ENV_FUZZER_PARENT_SENDER).is_err() { + // We are either the broker, or the parent of the fuzzing instance + mgr = LlmpEventManager::new_on_port_std(broker_port, stats.clone()).unwrap(); + if mgr.is_broker() { + // Yep, broker. Just loop here. + println!("Doing broker things. Run this tool again to start fuzzing in a client."); + mgr.broker_loop().unwrap(); + } else { + // we are one of the fuzzing instances. Let's launch the fuzzer. + + // First, store the mgr to an env so the client can use it + mgr.to_env(ENV_FUZZER_BROKER_CLIENT); + + // First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts. + let sender = LlmpSender::new(0, false).unwrap(); + let mut receiver = LlmpReceiver::on_existing_map( + AflShmem::clone_ref(&sender.out_maps.last().unwrap().shmem).unwrap(), + None, + ) + .unwrap(); + // Store the information to a map. + sender.to_env(ENV_FUZZER_PARENT_SENDER).unwrap(); + + loop { + dbg!("Spawning next client"); + Command::new(env::current_exe().unwrap()) + .current_dir(env::current_dir().unwrap()) + .args(env::args()) + .status() + .unwrap(); + + match receiver.recv_buf().unwrap() { + None => panic!("Fuzzer process exited without giving us its result."), + Some((sender, tag, msg)) => { + todo!("Restore this: {}, {}, {:?}", sender, tag, msg); + } + } + } + } } println!("We're a client, let's fuzz :)"); + // We are the fuzzing instance + mgr = LlmpEventManager::existing_client_from_env_std(ENV_FUZZER_BROKER_CLIENT, stats).unwrap(); + let channel_sender = + LlmpSender::::on_existing_from_env(ENV_FUZZER_PARENT_SENDER).unwrap(); + let edges_observer = StdMapObserver::new_from_ptr(&NAME_COV_MAP, unsafe { __lafl_edges_map }, unsafe { __lafl_max_edges_size as usize @@ -168,6 +215,4 @@ pub fn main() { fuzzer .fuzz_loop(&mut rand, &mut state, &mut corpus, &mut engine, &mut mgr) .expect("Fuzzer fatal error"); - #[cfg(feature = "std")] - println!("OK"); } diff --git a/fuzzers/libfuzzer_libpng/test.sh b/fuzzers/libfuzzer_libpng/test.sh index d62ee24987..a3881da017 100644 --- a/fuzzers/libfuzzer_libpng/test.sh +++ b/fuzzers/libfuzzer_libpng/test.sh @@ -3,12 +3,12 @@ cargo build --release || exit 1 cp ./target/release/libfuzzer ./.libfuzzer_test.elf -RUST_BACKTRACE=1 taskset -c 0 ./.libfuzzer_test.elf & +RUST_BACKTRACE=full taskset -c 0 ./.libfuzzer_test.elf & test "$!" -gt 0 && { usleep 250 - RUST_BACKTRACE=1 taskset -c 1 ./.libfuzzer_test.elf & + RUST_BACKTRACE=full taskset -c 1 ./.libfuzzer_test.elf & } diff --git a/fuzzers/libfuzzer_test/test.sh b/fuzzers/libfuzzer_test/test.sh index a644e3b63f..bdfc3fd8de 100644 --- a/fuzzers/libfuzzer_test/test.sh +++ b/fuzzers/libfuzzer_test/test.sh @@ -3,12 +3,12 @@ cargo build --release || exit 1 cp ./target/release/libfuzzer ./.libfuzzer_test.elf -RUST_BACKTRACE=1 taskset -c 0 ./.libfuzzer_test.elf & +RUST_BACKTRACE=full taskset -c 0 ./.libfuzzer_test.elf & test "$!" -gt 0 && { usleep 250 - RUST_BACKTRACE=1 taskset -c 1 ./.libfuzzer_test.elf -x a -x b -T5 in1 in2 & + RUST_BACKTRACE=full taskset -c 1 ./.libfuzzer_test.elf -x a -x b -T5 in1 in2 & }