use alloc::{string::ToString, vec::Vec};
#[cfg(feature = "std")]
use core::{
marker::PhantomData,
ptr::{addr_of, read_volatile},
};
#[cfg(feature = "std")]
use serde::{de::DeserializeOwned, Serialize};
#[cfg(all(feature = "std", windows))]
use crate::bolts::os::startable_self;
#[cfg(all(feature = "std", unix))]
use crate::bolts::os::{fork, ForkResult};
#[cfg(feature = "std")]
use crate::bolts::{
llmp::{LlmpReceiver, LlmpSender},
shmem::ShMemProvider,
};
use crate::{
bolts::llmp,
events::{
BrokerEventResult, Event, EventFirer, EventManager, EventManagerId, EventProcessor,
EventRestarter, HasEventManagerId,
},
inputs::Input,
stats::Stats,
Error,
};
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";
const _LLMP_TAG_RESTART: llmp::Tag = 0x8357A87;
#[derive(Clone, Debug)]
pub struct SimpleEventManager<I, ST>
where
I: Input,
ST: Stats, {
stats: ST,
events: Vec<Event<I>>,
}
impl<I, S, ST> EventFirer<I, S> for SimpleEventManager<I, ST>
where
I: Input,
ST: Stats, {
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
match Self::handle_in_broker(&mut self.stats, &event)? {
BrokerEventResult::Forward => self.events.push(event),
BrokerEventResult::Handled => (),
};
Ok(())
}
}
impl<I, S, ST> EventRestarter<S> for SimpleEventManager<I, ST>
where
I: Input,
ST: Stats, {
}
impl<E, I, S, ST, Z> EventProcessor<E, I, S, Z> for SimpleEventManager<I, ST>
where
I: Input,
ST: Stats, {
fn process(
&mut self,
_fuzzer: &mut Z,
state: &mut S,
_executor: &mut E,
) -> Result<usize, Error> {
let count = self.events.len();
while !self.events.is_empty() {
let event = self.events.pop().unwrap();
self.handle_in_client(state, event)?;
}
Ok(count)
}
}
impl<E, I, S, ST, Z> EventManager<E, I, S, Z> for SimpleEventManager<I, ST>
where
I: Input,
ST: Stats, {
}
impl<I, ST> HasEventManagerId for SimpleEventManager<I, ST>
where
I: Input,
ST: Stats,
{
fn mgr_id(&self) -> EventManagerId {
EventManagerId { id: 0 }
}
}
impl<I, ST> SimpleEventManager<I, ST>
where
I: Input,
ST: Stats, {
pub fn new(stats: ST) -> Self {
Self {
stats,
events: vec![],
}
}
#[allow(clippy::unnecessary_wraps)]
fn handle_in_broker(stats: &mut ST, event: &Event<I>) -> Result<BrokerEventResult, Error> {
match event {
Event::NewTestcase {
input: _,
client_config: _,
exit_kind: _,
corpus_size,
observers_buf: _,
time,
executions,
} => {
stats
.client_stats_mut_for(0)
.update_corpus_size(*corpus_size as u64);
stats
.client_stats_mut_for(0)
.update_executions(*executions as u64, *time);
stats.display(event.name().to_string(), 0);
Ok(BrokerEventResult::Handled)
}
Event::UpdateStats {
time,
executions,
phantom: _,
} => {
stats
.client_stats_mut_for(0)
.update_executions(*executions as u64, *time);
stats.display(event.name().to_string(), 0);
Ok(BrokerEventResult::Handled)
}
Event::UpdateUserStats {
name,
value,
phantom: _,
} => {
stats
.client_stats_mut_for(0)
.update_user_stats(name.clone(), value.clone());
stats.display(event.name().to_string(), 0);
Ok(BrokerEventResult::Handled)
}
#[cfg(feature = "introspection")]
Event::UpdatePerfStats {
time,
executions,
introspection_stats,
phantom: _,
} => {
stats.client_stats_mut()[0].update_executions(*executions as u64, *time);
stats.client_stats_mut()[0]
.update_introspection_stats((**introspection_stats).clone());
stats.display(event.name().to_string(), 0);
Ok(BrokerEventResult::Handled)
}
Event::Objective { objective_size } => {
stats
.client_stats_mut_for(0)
.update_objective_size(*objective_size as u64);
stats.display(event.name().to_string(), 0);
Ok(BrokerEventResult::Handled)
}
Event::Log {
severity_level,
message,
phantom: _,
} => {
let (_, _) = (message, severity_level);
#[cfg(feature = "std")]
println!("[LOG {}]: {}", severity_level, message);
Ok(BrokerEventResult::Handled)
} }
}
#[allow(clippy::needless_pass_by_value, clippy::unused_self)]
fn handle_in_client<S>(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
Err(Error::Unknown(format!(
"Received illegal message that message should not have arrived: {:?}.",
event
)))
}
}
#[cfg(feature = "std")]
#[allow(clippy::default_trait_access)]
pub struct SimpleRestartingEventManager<I, S, SP, ST>
where
I: Input,
S: Serialize,
SP: ShMemProvider,
ST: Stats, {
simple_event_mgr: SimpleEventManager<I, ST>,
sender: LlmpSender<SP>,
_phantom: PhantomData<(I, S)>,
}
#[cfg(feature = "std")]
impl<I, S, SP, ST> EventFirer<I, S> for SimpleRestartingEventManager<I, S, SP, ST>
where
I: Input,
S: Serialize,
SP: ShMemProvider,
ST: Stats, {
fn fire(&mut self, _state: &mut S, event: Event<I>) -> Result<(), Error> {
self.simple_event_mgr.fire(_state, event)
}
}
#[cfg(feature = "std")]
impl<I, S, SP, ST> EventRestarter<S> for SimpleRestartingEventManager<I, S, SP, ST>
where
I: Input,
S: Serialize,
SP: ShMemProvider,
ST: Stats, {
fn on_restart(&mut self, state: &mut S) -> Result<(), Error> {
unsafe {
self.sender.reset();
}
self.sender
.send_buf(_LLMP_TAG_RESTART, &postcard::to_allocvec(state)?)
}
}
#[cfg(feature = "std")]
impl<E, I, S, SP, ST, Z> EventProcessor<E, I, S, Z> for SimpleRestartingEventManager<I, S, SP, ST>
where
I: Input,
S: Serialize,
SP: ShMemProvider,
ST: Stats, {
fn process(&mut self, fuzzer: &mut Z, state: &mut S, executor: &mut E) -> Result<usize, Error> {
self.simple_event_mgr.process(fuzzer, state, executor)
}
}
#[cfg(feature = "std")]
impl<E, I, S, SP, ST, Z> EventManager<E, I, S, Z> for SimpleRestartingEventManager<I, S, SP, ST>
where
I: Input,
S: Serialize,
SP: ShMemProvider,
ST: Stats, {
}
#[cfg(feature = "std")]
impl<I, S, SP, ST> HasEventManagerId for SimpleRestartingEventManager<I, S, SP, ST>
where
I: Input,
S: Serialize,
SP: ShMemProvider,
ST: Stats,
{
fn mgr_id(&self) -> EventManagerId {
self.simple_event_mgr.mgr_id()
}
}
#[cfg(feature = "std")]
#[allow(clippy::type_complexity, clippy::too_many_lines)]
impl<I, S, SP, ST> SimpleRestartingEventManager<I, S, SP, ST>
where
I: Input,
S: DeserializeOwned + Serialize,
SP: ShMemProvider,
ST: Stats, {
fn new_launched(stats: ST, sender: LlmpSender<SP>) -> Self {
Self {
sender,
simple_event_mgr: SimpleEventManager::new(stats),
_phantom: PhantomData {},
}
}
#[allow(clippy::similar_names)]
pub fn launch(
stats: ST,
shmem_provider: &mut SP,
) -> Result<(Option<S>, SimpleRestartingEventManager<I, S, SP, ST>), Error> {
let (mut sender, mut receiver) = if std::env::var(_ENV_FUZZER_SENDER).is_err() {
let sender = { LlmpSender::new(shmem_provider.clone(), 0, false)? };
let map = { shmem_provider.clone_ref(&sender.out_maps.last().unwrap().shmem)? };
let receiver = LlmpReceiver::on_existing_map(shmem_provider.clone(), map, None)?;
sender.to_env(_ENV_FUZZER_SENDER)?;
receiver.to_env(_ENV_FUZZER_RECEIVER)?;
let mut ctr: u64 = 0;
loop {
dbg!("Spawning next client (id {})", ctr);
#[cfg(unix)]
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 (sender, receiver);
}
}
};
#[cfg(windows)]
let child_status = startable_self()?.status()?;
if unsafe { read_volatile(addr_of!((*receiver.current_recv_map.page()).size_used)) }
== 0
{
#[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! (Child exited with: {})", child_status);
}
ctr = ctr.wrapping_add(1);
}
} else {
(
LlmpSender::on_existing_from_env(shmem_provider.clone(), _ENV_FUZZER_SENDER)?,
LlmpReceiver::on_existing_from_env(shmem_provider.clone(), _ENV_FUZZER_RECEIVER)?,
)
};
println!("We're a client, let's fuzz :)");
let (state, mgr) = match receiver.recv_buf()? {
None => {
println!("First run. Let's set it all up");
(
None,
SimpleRestartingEventManager::new_launched(stats, sender),
)
}
Some((_sender, _tag, msg)) => {
println!("Subsequent run. Let's load all data from shmem (received {} bytes from previous instance)", msg.len());
let state: S = postcard::from_bytes(msg)?;
unsafe {
sender.reset();
}
(
Some(state),
SimpleRestartingEventManager::new_launched(stats, sender),
)
}
};
Ok((state, mgr))
}
}