diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index e97b75404a..3cbe062b55 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -12,6 +12,9 @@ use alloc::{ use core::{fmt, marker::PhantomData, time::Duration}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] +use std::process::Command; + #[cfg(feature = "std")] #[cfg(unix)] use crate::shmem::AflShmem; @@ -201,7 +204,10 @@ where } /// Send off an event to the broker - fn fire(&mut self, event: Event) -> Result<(), AflError>; + fn fire(&mut self, _state: &mut State, event: Event) -> Result<(), AflError> where + C: Corpus, + FT: FeedbacksTuple, + R: Rand; } /// An eventmgr for tests, and as placeholder if you really don't need an event manager. @@ -222,7 +228,10 @@ where Ok(0) } - fn fire(&mut self, _event: Event) -> Result<(), AflError> { + fn fire(&mut self, _state: &mut State, _event: Event) -> Result<(), AflError> where + C: Corpus, + FT: FeedbacksTuple, + R: Rand { Ok(()) } } @@ -259,7 +268,10 @@ where Ok(count) } - fn fire(&mut self, event: Event) -> Result<(), AflError> { + fn fire(&mut self, _state: &mut State, event: Event) -> Result<(), AflError> where + C: Corpus, + FT: FeedbacksTuple, + R: Rand, { match Self::handle_in_broker(&mut self.stats, 0, &event)? { BrokerEventResult::Forward => self.events.push(event), BrokerEventResult::Handled => (), @@ -501,14 +513,6 @@ where } } - /// Send an event kind via llmp - #[inline] - fn send_event_kind(&mut self, event: Event) -> Result<(), AflError> { - let serialized = postcard::to_allocvec(&event)?; - self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?; - Ok(()) - } - /// Handle arriving events in the broker fn handle_in_broker( stats: &mut ST, @@ -636,8 +640,16 @@ where Ok(count) } - fn fire(&mut self, event: Event) -> Result<(), AflError> { - self.send_event_kind(event) + fn fire(&mut self, _state: &mut State, event: Event) -> Result<(), AflError> + where + C: Corpus, + FT: FeedbacksTuple, + I: Input, + R: Rand, + { + let serialized = postcard::to_allocvec(&event)?; + self.llmp.send_buf(LLMP_TAG_EVENT_TO_BOTH, &serialized)?; + Ok(()) } } @@ -660,6 +672,232 @@ where */ + +#[derive(Clone, Debug)] +pub struct LlmpRestartingEventManager +where + I: Input, + SH: ShMem, + ST: Stats, + //CE: CustomEvent, +{ + llmp_mgr: LlmpEventManager, + sender: LlmpSender, + receiver: LlmpReceiver, +} + + +impl EventManager for LlmpRestartingEventManager +where + I: Input, + SH: ShMem, + ST: Stats, //CE: CustomEvent, +{ + fn process(&mut self, state: &mut State) -> Result + where + C: Corpus, + FT: FeedbacksTuple, + R: Rand, + { + self.llmp_mgr.process(state) + } + + fn fire(&mut self, state: &mut State, event: Event) -> Result<(), AflError> where + C: Corpus, + FT: FeedbacksTuple, + R: Rand, + { + match &event { + Event::Crash { input: _ } => { + let buf = postcard::to_allocvec(&(&state, &self.llmp_mgr.describe()?))?; + self.sender.send_buf(0x1, &buf).unwrap(); + }, + Event::Timeout { input: _ } => { + let buf = postcard::to_allocvec(&(&state, &self.llmp_mgr.describe()?))?; + self.sender.send_buf(0x1, &buf).unwrap(); + }, + _ => {} + }; + self.llmp_mgr.fire(state, event) + } +} + +/// The llmp connection from the actual fuzzer to the process supervising it +const ENV_FUZZER_SENDER: &str = &"_AFL_ENV_FUZZER_SENDER"; +const ENV_FUZZER_RECEIVER: &str = &"_AFL_ENV_FUZZER_RECEIVER"; +/// The llmp (2 way) connection from a fuzzer to the broker (broadcasting all other fuzzer messages) +const ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = &"_AFL_ENV_FUZZER_BROKER_CLIENT"; + + +impl LlmpRestartingEventManager +where + I: Input, + SH: ShMem, + ST: Stats, //CE: CustomEvent, +{ + + /// Create a new runner, the executed child doing the actual fuzzing. + pub fn new(llmp_mgr: LlmpEventManager, receiver: LlmpReceiver, sender: LlmpSender) -> Self { + Self { + llmp_mgr, + sender, + receiver, + } + } + + pub fn temp(stats: ST, broker_port: u16) -> Result<(Self, Option>), AflError> + where + C: Corpus, + FT: FeedbacksTuple, + R: Rand, + { + + let mut mgr; + + // We start ourself as child process to actually fuzz + if std::env::var(ENV_FUZZER_SENDER).is_err() { + + // We are either the broker, or the parent of the fuzzing instance + mgr = LlmpEventManager::new_on_port( stats, broker_port)?; + 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()?; + } 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_INITIAL); + + // First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts. + let sender = LlmpSender::new(0, false)?; + let receiver = LlmpReceiver::on_existing_map( + AflShmem::clone_ref(&sender.out_maps.last().unwrap().shmem)?, + None, + )?; + // Store the information to a map. + sender.to_env(ENV_FUZZER_SENDER)?; + receiver.to_env(ENV_FUZZER_RECEIVER)?; + + let mut ctr = 0; + // Client->parent loop + loop { + dbg!("Spawning next client"); + Command::new(env::current_exe()?) + .current_dir(env::current_dir()?) + .args(env::args()) + .status()?; + ctr += 1; + if ctr == 10 { + return Ok(()); + } + } + } + } + + println!("We're a client, let's fuzz :)"); + + // We are the fuzzing instance, first, connect to our own restore map. + // A sender and a receiver for single communication + let mut receiver = LlmpReceiver::::on_existing_from_env(ENV_FUZZER_RECEIVER)?; + let mut sender = LlmpSender::::on_existing_from_env(ENV_FUZZER_SENDER)?; + + // If we're restarting, deserialize the old state. + let (mut state, mut mgr) = match receiver.recv_buf()? { + None => { + println!("First run. Let's set it all up"); + // Mgr to send and receive msgs from/to all other fuzzer instances + mgr = LlmpEventManager::existing_client_from_env( + ENV_FUZZER_BROKER_CLIENT_INITIAL, + )?; + + (mgr, None) + } + // Restoring from a previous run, deserialize state and corpus. + Some((_sender, _tag, msg)) => { + println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len()); + deserialize_state_mgr(&msg, stats)? + } + }; + // We reset the sender, the next sender and receiver (after crash) will reuse the page from the initial message. + unsafe { sender.reset_last_page() }; + + Ok(mgr) + } + +} + +/// A restarting state is a combination of restarter and runner, that can be used on systems without `fork`. +/// The restarter will start a new process each time the child crashes or timeouts. +pub fn setup_restarting_state(mgr: &mut LlmpEventManager) -> Result>, AflError> + where + I: Input, + C: Corpus, + FT: FeedbacksTuple, + R: Rand, + SH: ShMem, + ST: Stats, +{ + + // We start ourself as child process to actually fuzz + if std::env::var(ENV_FUZZER_SENDER).is_err() { + + mgr.to_env(ENV_FUZZER_BROKER_CLIENT_INITIAL); + + // First, create a channel from the fuzzer (sender) to us (receiver) to report its state for restarts. + let sender = LlmpSender::new(0, false)?; + let receiver = LlmpReceiver::on_existing_map( + AflShmem::clone_ref(&sender.out_maps.last().unwrap().shmem)?, + None, + )?; + // Store the information to a map. + sender.to_env(ENV_FUZZER_SENDER)?; + receiver.to_env(ENV_FUZZER_RECEIVER)?; + + let mut ctr = 0; + // Client->parent loop + loop { + dbg!("Spawning next client"); + Command::new(env::current_exe()?) + .current_dir(env::current_dir()?) + .args(env::args()) + .status()?; + ctr += 1; + if ctr == 10 { + return Ok(()); + } + } + } + + println!("We're a client, let's fuzz :)"); + + // We are the fuzzing instance, first, connect to our own restore map. + // A sender and a receiver for single communication + let mut receiver = LlmpReceiver::::on_existing_from_env(ENV_FUZZER_RECEIVER)?; + let mut sender = LlmpSender::::on_existing_from_env(ENV_FUZZER_SENDER)?; + + // If we're restarting, deserialize the old state. + let (mut state, mut mgr) = match receiver.recv_buf()? { + None => { + println!("First run. Let's set it all up"); + // Mgr to send and receive msgs from/to all other fuzzer instances + let client_mgr = LlmpEventManager::existing_client_from_env( + ENV_FUZZER_BROKER_CLIENT_INITIAL, + )?; + + (LlmpRestartingEventManager::new(client_mgr, sender, receiver), receiver: LlmpReceiver, sender: LlmpSender), None) + } + // Restoring from a previous run, deserialize state and corpus. + Some((_sender, _tag, msg)) => { + println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len()); + deserialize_state_mgr(&msg, stats)? + } + }; + // We reset the sender, the next sender and receiver (after crash) will reuse the page from the initial message. + unsafe { sender.reset_last_page() }; +} + + #[cfg(test)] mod tests { diff --git a/afl/src/lib.rs b/afl/src/lib.rs index 9986c0649c..c2f2824050 100644 --- a/afl/src/lib.rs +++ b/afl/src/lib.rs @@ -86,7 +86,7 @@ where let cur = current_milliseconds(); if cur - last > 60 * 100 { last = cur; - manager.fire(Event::UpdateStats { + manager.fire(state, Event::UpdateStats { executions: state.executions(), execs_over_sec: state.executions_over_seconds(), phantom: PhantomData, diff --git a/afl/src/state/mod.rs b/afl/src/state/mod.rs index b607dd46ef..1a5ac19321 100644 --- a/afl/src/state/mod.rs +++ b/afl/src/state/mod.rs @@ -143,7 +143,7 @@ where for in_dir in in_dirs { self.load_from_directory(executor, generator, manager, in_dir)?; } - manager.fire(Event::Log { + manager.fire(self, Event::Log { severity_level: LogSeverity::Debug, message: format!("Loaded {} initial testcases.", self.corpus().count()), // get corpus count phantom: PhantomData, @@ -351,7 +351,7 @@ where added += 1; } } - manager.fire(Event::Log { + manager.fire(self, Event::Log { severity_level: LogSeverity::Debug, message: format!("Loaded {} over {} initial testcases", added, num), phantom: PhantomData, diff --git a/afl/src/utils.rs b/afl/src/utils.rs index 2b6593e054..bb6d5d113d 100644 --- a/afl/src/utils.rs +++ b/afl/src/utils.rs @@ -24,7 +24,7 @@ pub type StdRand = RomuTrioRand; /// Serialize the current state and corpus during an executiont to bytes. /// On top, add the current llmp event manager instance to be restored /// This method is needed when the fuzzer run crashes and has to restart. -pub fn serialize_state_corpus_mgr( +pub fn serialize_state_mgr( state: &State, mgr: &LlmpEventManager, ) -> Result, AflError> @@ -54,7 +54,7 @@ where } /// Deserialize the state and corpus tuple, previously serialized with `serialize_state_corpus(...)` -pub fn deserialize_state_corpus_mgr( +pub fn deserialize_state_mgr( state_corpus_serialized: &[u8], ) -> Result<(State, LlmpEventManager), AflError> where