use alloc::{
boxed::Box,
string::{String, ToString},
vec::Vec,
};
use core::{fmt::Debug, marker::PhantomData};
#[cfg(feature = "std")]
use core::{
sync::atomic::{compiler_fence, Ordering},
time::Duration,
};
#[cfg(all(feature = "std", any(windows, not(feature = "fork"))))]
use libafl_bolts::os::startable_self;
#[cfg(all(unix, feature = "std", not(miri)))]
use libafl_bolts::os::unix_signals::setup_signal_handler;
#[cfg(all(feature = "std", feature = "fork", unix))]
use libafl_bolts::os::{fork, ForkResult};
use libafl_bolts::ClientId;
#[cfg(feature = "std")]
use libafl_bolts::{shmem::ShMemProvider, staterestore::StateRestorer};
#[cfg(feature = "std")]
use serde::{de::DeserializeOwned, Serialize};
use super::{CustomBufEventResult, CustomBufHandlerFn, HasCustomBufHandlers, ProgressReporter};
#[cfg(all(unix, feature = "std"))]
use crate::events::EVENTMGR_SIGHANDLER_STATE;
use crate::{
events::{
BrokerEventResult, Event, EventFirer, EventManager, EventManagerId, EventProcessor,
EventRestarter, HasEventManagerId,
},
inputs::UsesInput,
monitors::Monitor,
state::{HasExecutions, HasLastReportTime, HasMetadata, State, UsesState},
Error,
};
#[cfg(feature = "std")]
use crate::{
monitors::{ClientStats, SimplePrintingMonitor},
state::{HasCorpus, HasSolutions},
};
const _ENV_FUZZER_SENDER: &str = "_AFL_ENV_FUZZER_SENDER";
const _ENV_FUZZER_RECEIVER: &str = "_AFL_ENV_FUZZER_RECEIVER";
const _ENV_FUZZER_BROKER_CLIENT_INITIAL: &str = "_AFL_ENV_FUZZER_BROKER_CLIENT";
pub struct SimpleEventManager<MT, S>
where
S: UsesInput,
{
monitor: MT,
events: Vec<Event<S::Input>>,
custom_buf_handlers: Vec<Box<CustomBufHandlerFn<S>>>,
phantom: PhantomData<S>,
}
impl<MT, S> Debug for SimpleEventManager<MT, S>
where
MT: Debug,
S: UsesInput,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SimpleEventManager")
.field("monitor", &self.monitor)
.field("events", &self.events)
.finish_non_exhaustive()
}
}
impl<MT, S> UsesState for SimpleEventManager<MT, S>
where
S: State,
{
type State = S;
}
impl<MT, S> EventFirer for SimpleEventManager<MT, S>
where
MT: Monitor,
S: State,
{
fn fire(
&mut self,
_state: &mut Self::State,
event: Event<<Self::State as UsesInput>::Input>,
) -> Result<(), Error> {
match Self::handle_in_broker(&mut self.monitor, &event)? {
BrokerEventResult::Forward => self.events.push(event),
BrokerEventResult::Handled => (),
};
Ok(())
}
}
impl<MT, S> EventRestarter for SimpleEventManager<MT, S>
where
MT: Monitor,
S: State,
{
}
impl<E, MT, S, Z> EventProcessor<E, Z> for SimpleEventManager<MT, S>
where
MT: Monitor,
S: State,
{
fn process(
&mut self,
_fuzzer: &mut Z,
state: &mut S,
_executor: &mut E,
) -> Result<usize, Error> {
let count = self.events.len();
while let Some(event) = self.events.pop() {
self.handle_in_client(state, event)?;
}
Ok(count)
}
}
impl<E, MT, S, Z> EventManager<E, Z> for SimpleEventManager<MT, S>
where
MT: Monitor,
S: State + HasExecutions + HasLastReportTime + HasMetadata,
{
}
impl<MT, S> HasCustomBufHandlers for SimpleEventManager<MT, S>
where
MT: Monitor, S: State,
{
fn add_custom_buf_handler(
&mut self,
handler: Box<
dyn FnMut(&mut Self::State, &String, &[u8]) -> Result<CustomBufEventResult, Error>,
>,
) {
self.custom_buf_handlers.push(handler);
}
}
impl<MT, S> ProgressReporter for SimpleEventManager<MT, S>
where
MT: Monitor,
S: State + HasExecutions + HasMetadata + HasLastReportTime,
{
}
impl<MT, S> HasEventManagerId for SimpleEventManager<MT, S>
where
MT: Monitor,
S: UsesInput,
{
fn mgr_id(&self) -> EventManagerId {
EventManagerId(0)
}
}
#[cfg(feature = "std")]
impl<S> SimpleEventManager<SimplePrintingMonitor, S>
where
S: UsesInput,
{
#[must_use]
pub fn printing() -> Self {
Self::new(SimplePrintingMonitor::new())
}
}
impl<MT, S> SimpleEventManager<MT, S>
where
MT: Monitor, S: UsesInput,
{
pub fn new(monitor: MT) -> Self {
Self {
monitor,
events: vec![],
custom_buf_handlers: vec![],
phantom: PhantomData,
}
}
#[allow(clippy::unnecessary_wraps)]
fn handle_in_broker(
monitor: &mut MT,
event: &Event<S::Input>,
) -> Result<BrokerEventResult, Error> {
match event {
Event::NewTestcase {
input: _,
client_config: _,
exit_kind: _,
corpus_size,
observers_buf: _,
time,
executions,
forward_id: _,
} => {
monitor.client_stats_insert(ClientId(0));
monitor
.client_stats_mut_for(ClientId(0))
.update_corpus_size(*corpus_size as u64);
monitor
.client_stats_mut_for(ClientId(0))
.update_executions(*executions as u64, *time);
monitor.display(event.name().to_string(), ClientId(0));
Ok(BrokerEventResult::Handled)
}
Event::UpdateExecStats {
time,
executions,
phantom: _,
} => {
monitor.client_stats_insert(ClientId(0));
let client = monitor.client_stats_mut_for(ClientId(0));
client.update_executions(*executions as u64, *time);
monitor.display(event.name().to_string(), ClientId(0));
Ok(BrokerEventResult::Handled)
}
Event::UpdateUserStats {
name,
value,
phantom: _,
} => {
monitor.client_stats_insert(ClientId(0));
monitor
.client_stats_mut_for(ClientId(0))
.update_user_stats(name.clone(), value.clone());
monitor.aggregate(name);
monitor.display(event.name().to_string(), ClientId(0));
Ok(BrokerEventResult::Handled)
}
#[cfg(feature = "introspection")]
Event::UpdatePerfMonitor {
time,
executions,
introspection_monitor,
phantom: _,
} => {
monitor.client_stats_insert(ClientId(0));
let client = monitor.client_stats_mut_for(ClientId(0));
client.update_executions(*executions as u64, *time);
client.update_introspection_monitor((**introspection_monitor).clone());
monitor.display(event.name().to_string(), ClientId(0));
Ok(BrokerEventResult::Handled)
}
Event::Objective { objective_size } => {
monitor.client_stats_insert(ClientId(0));
monitor
.client_stats_mut_for(ClientId(0))
.update_objective_size(*objective_size as u64);
monitor.display(event.name().to_string(), ClientId(0));
Ok(BrokerEventResult::Handled)
}
Event::Log {
severity_level,
message,
phantom: _,
} => {
let (_, _) = (message, severity_level);
log::log!((*severity_level).into(), "{message}");
Ok(BrokerEventResult::Handled)
}
Event::CustomBuf { .. } => Ok(BrokerEventResult::Forward),
}
}
#[allow(clippy::needless_pass_by_value, clippy::unused_self)]
fn handle_in_client(&mut self, state: &mut S, event: Event<S::Input>) -> Result<(), Error> {
if let Event::CustomBuf { tag, buf } = &event {
for handler in &mut self.custom_buf_handlers {
handler(state, tag, buf)?;
}
Ok(())
} else {
Err(Error::unknown(format!(
"Received illegal message that message should not have arrived: {event:?}."
)))
}
}
}
#[cfg(feature = "std")]
#[allow(clippy::default_trait_access)]
#[derive(Debug)]
pub struct SimpleRestartingEventManager<MT, S, SP>
where
S: UsesInput,
SP: ShMemProvider, {
simple_event_mgr: SimpleEventManager<MT, S>,
staterestorer: StateRestorer<SP>,
}
#[cfg(feature = "std")]
impl<MT, S, SP> UsesState for SimpleRestartingEventManager<MT, S, SP>
where
S: State,
SP: ShMemProvider,
{
type State = S;
}
#[cfg(feature = "std")]
impl<MT, S, SP> EventFirer for SimpleRestartingEventManager<MT, S, SP>
where
MT: Monitor,
S: State,
SP: ShMemProvider,
{
fn fire(
&mut self,
_state: &mut Self::State,
event: Event<<Self::State as UsesInput>::Input>,
) -> Result<(), Error> {
self.simple_event_mgr.fire(_state, event)
}
}
#[cfg(feature = "std")]
impl<MT, S, SP> EventRestarter for SimpleRestartingEventManager<MT, S, SP>
where
MT: Monitor,
S: State,
SP: ShMemProvider,
{
fn on_restart(&mut self, state: &mut S) -> Result<(), Error> {
self.staterestorer.reset();
self.staterestorer.save(&(
state,
self.simple_event_mgr.monitor.start_time(),
self.simple_event_mgr.monitor.client_stats(),
))
}
fn send_exiting(&mut self) -> Result<(), Error> {
self.staterestorer.send_exiting();
Ok(())
}
}
#[cfg(feature = "std")]
impl<E, MT, S, SP, Z> EventProcessor<E, Z> for SimpleRestartingEventManager<MT, S, SP>
where
MT: Monitor,
S: State + HasExecutions,
SP: ShMemProvider,
{
fn process(
&mut self,
fuzzer: &mut Z,
state: &mut Self::State,
executor: &mut E,
) -> Result<usize, Error> {
self.simple_event_mgr.process(fuzzer, state, executor)
}
}
#[cfg(feature = "std")]
impl<E, MT, S, SP, Z> EventManager<E, Z> for SimpleRestartingEventManager<MT, S, SP>
where
MT: Monitor,
S: State + HasExecutions + HasMetadata + HasLastReportTime + Serialize,
SP: ShMemProvider,
{
}
#[cfg(feature = "std")]
impl<MT, S, SP> HasCustomBufHandlers for SimpleRestartingEventManager<MT, S, SP>
where
MT: Monitor,
S: State,
SP: ShMemProvider,
{
fn add_custom_buf_handler(
&mut self,
handler: Box<dyn FnMut(&mut S, &String, &[u8]) -> Result<CustomBufEventResult, Error>>,
) {
self.simple_event_mgr.add_custom_buf_handler(handler);
}
}
#[cfg(feature = "std")]
impl<MT, S, SP> ProgressReporter for SimpleRestartingEventManager<MT, S, SP>
where
MT: Monitor,
S: State + HasExecutions + HasMetadata + HasLastReportTime,
SP: ShMemProvider,
{
}
#[cfg(feature = "std")]
impl<MT, S, SP> HasEventManagerId for SimpleRestartingEventManager<MT, S, SP>
where
MT: Monitor,
S: UsesInput,
SP: ShMemProvider,
{
fn mgr_id(&self) -> EventManagerId {
self.simple_event_mgr.mgr_id()
}
}
#[cfg(feature = "std")]
#[allow(clippy::type_complexity, clippy::too_many_lines)]
impl<MT, S, SP> SimpleRestartingEventManager<MT, S, SP>
where
S: UsesInput,
SP: ShMemProvider,
MT: Monitor, {
fn launched(monitor: MT, staterestorer: StateRestorer<SP>) -> Self {
Self {
staterestorer,
simple_event_mgr: SimpleEventManager::new(monitor),
}
}
#[inline]
#[allow(clippy::unused_self)]
fn is_shutting_down() -> bool {
#[cfg(unix)]
unsafe {
core::ptr::read_volatile(core::ptr::addr_of!(EVENTMGR_SIGHANDLER_STATE.shutting_down))
}
#[cfg(windows)]
false
}
#[allow(clippy::similar_names)]
pub fn launch(mut monitor: MT, shmem_provider: &mut SP) -> Result<(Option<S>, Self), Error>
where
S: DeserializeOwned + Serialize + HasCorpus + HasSolutions,
MT: Debug,
{
let mut staterestorer = if std::env::var(_ENV_FUZZER_SENDER).is_err() {
#[cfg(unix)]
let staterestorer: StateRestorer<SP> =
StateRestorer::new(shmem_provider.new_shmem(256 * 1024 * 1024)?);
#[cfg(not(unix))]
let staterestorer: StateRestorer<SP> =
StateRestorer::new(shmem_provider.new_shmem(256 * 1024 * 1024)?);
staterestorer.write_to_env(_ENV_FUZZER_SENDER)?;
#[cfg(all(unix, not(miri)))]
if let Err(_e) = unsafe { setup_signal_handler(&mut EVENTMGR_SIGHANDLER_STATE) } {
log::error!("Failed to setup signal handlers: {_e}");
}
let mut ctr: u64 = 0;
loop {
log::info!("Spawning next client (id {ctr})");
#[cfg(all(unix, feature = "fork"))]
let child_status = {
shmem_provider.pre_fork()?;
match unsafe { fork() }? {
ForkResult::Parent(handle) => {
shmem_provider.post_fork(false)?;
handle.status()
}
ForkResult::Child => {
shmem_provider.post_fork(true)?;
break staterestorer;
}
}
};
#[cfg(any(windows, not(feature = "fork")))]
let child_status = startable_self()?.status()?;
#[cfg(all(unix, not(feature = "fork")))]
let child_status = child_status.code().unwrap_or_default();
compiler_fence(Ordering::SeqCst);
if staterestorer.wants_to_exit() || Self::is_shutting_down() {
return Err(Error::shutting_down());
}
#[allow(clippy::manual_assert)]
if !staterestorer.has_content() {
#[cfg(unix)]
if child_status == 137 {
panic!("Fuzzer-respawner: The fuzzed target crashed with an out of memory error! Fix your harness, or switch to another executor (for example, a forkserver).");
}
panic!("Fuzzer-respawner: Storing state in crashed fuzzer instance did not work, no point to spawn the next client! This can happen if the child calls `exit()`, in that case make sure it uses `abort()`, if it got killed unrecoverable (OOM), or if there is a bug in the fuzzer itself. (Child exited with: {child_status})");
}
ctr = ctr.wrapping_add(1);
}
} else {
StateRestorer::from_env(shmem_provider, _ENV_FUZZER_SENDER)?
};
let (state, mgr) = match staterestorer.restore::<(S, Duration, Vec<ClientStats>)>()? {
None => {
log::info!("First run. Let's set it all up");
(
None,
SimpleRestartingEventManager::launched(monitor, staterestorer),
)
}
Some((state, start_time, clients_stats)) => {
log::info!("Subsequent run. Loaded previous state.");
staterestorer.reset();
monitor.set_start_time(start_time);
*monitor.client_stats_mut() = clients_stats;
(
Some(state),
SimpleRestartingEventManager::launched(monitor, staterestorer),
)
}
};
Ok((state, mgr))
}
}
#[cfg(feature = "python")]
#[allow(missing_docs)]
#[allow(clippy::unnecessary_fallible_conversions)]
pub mod pybind {
use pyo3::prelude::*;
use crate::{
events::{pybind::PythonEventManager, SimpleEventManager},
monitors::pybind::PythonMonitor,
state::pybind::PythonStdState,
};
#[pyclass(unsendable, name = "SimpleEventManager")]
#[derive(Debug)]
pub struct PythonSimpleEventManager {
pub inner: SimpleEventManager<PythonMonitor, PythonStdState>,
}
#[pymethods]
impl PythonSimpleEventManager {
#[new]
fn new(py_monitor: PythonMonitor) -> Self {
Self {
inner: SimpleEventManager::new(py_monitor),
}
}
fn as_manager(slf: Py<Self>) -> PythonEventManager {
PythonEventManager::new_simple(slf)
}
}
pub fn register(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<PythonSimpleEventManager>()?;
Ok(())
}
}