Skip to content

Commit

Permalink
Add Stoppable trait to State which exposes an API to stop the fuzzer (A…
Browse files Browse the repository at this point in the history
…FLplusplus#2325)

* add HasStopNext to State which exposes an API to stop the fuzzer. Stops the fuzzer in fuzz_loop or
fuzz_loop_for when set to true

* fix import

* rename HasStopNext to HasShouldStopFuzzing and stop_next to should_stop_fuzzing

* added HasShouldStopFuzzing trait constraint for libafl_libfuzzer_runtime fuzzer

* rename HasShouldStopFuzzing to Stoppable and add it as a type constraint in libafl_libfuzzer report.rs

* rename should_stop_fuzzing -> should_stop

* introduce Event::Stop

* fix prelude import

* Call send_exiting when processing Event::Stop in restartable managers

* fix clippy

* introduce on_shutdown function in EventProcessor, a function to exit
without saving state gracefully. In contrast with on_restart.

* call manager.on_shutdown when stopping in fuzz_loop due to state.should_stop

* Add missing on_shutdown implementations
Check after every stage in Stages::perform_all if should exit and do so.

* remove specialization

* fix doc

* introduce EventProcessor constraint in libafl_libfuzzer_runtime
run clippy in libafl_libfuzzer_runtime

* fix CentralizedEventManager's on_shutdown not calling inner.on_shutdown

* fix bugs in CentralizedLauncher that wouldn't allow children to terminate properly

* don't call send_exiting when processing Event::Stop since it will be called when calling on_shutdown anyways

* clippy

* add set_exit_after so broker does not need to inner_mut to set exit_cleanly_after

* return Cow<str> from Event::name_detailed instead of a String

* fix missing import in libafl_libfuzzer_runtime

* add initate_stop and reset_stop to Stoppable trait to superceed should_stop_mut

* clippy

* typo

* rename initate_stop to request_stop, should_stop to stop_requested and reset_stop to discard_stop_request

* fix missing import

* windows clippy fix

* fix broker typo
  • Loading branch information
R9295 authored Jul 2, 2024
1 parent 762b6e0 commit eff4032
Show file tree
Hide file tree
Showing 18 changed files with 236 additions and 96 deletions.
2 changes: 1 addition & 1 deletion libafl/src/events/broker_hooks/centralized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ where
event: &Event<I>,
) -> Result<BrokerEventResult, Error> {
match &event {
Event::NewTestcase { .. } => Ok(BrokerEventResult::Forward),
Event::NewTestcase { .. } | Event::Stop => Ok(BrokerEventResult::Forward),
_ => Ok(BrokerEventResult::Handled),
}
}
Expand Down
1 change: 1 addition & 0 deletions libafl/src/events/broker_hooks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ where
Ok(BrokerEventResult::Handled)
}
Event::CustomBuf { .. } => Ok(BrokerEventResult::Forward),
Event::Stop => Ok(BrokerEventResult::Forward),
//_ => Ok(BrokerEventResult::Forward),
}
}
Expand Down
14 changes: 12 additions & 2 deletions libafl/src/events/centralized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use crate::{
fuzzer::{EvaluatorObservers, ExecutionProcessor},
inputs::{Input, NopInput, UsesInput},
observers::{ObserversTuple, TimeObserver},
state::{HasExecutions, HasLastReportTime, NopState, State, UsesState},
state::{HasExecutions, HasLastReportTime, NopState, State, Stoppable, UsesState},
Error, HasMetadata,
};

Expand Down Expand Up @@ -279,6 +279,7 @@ where
self.inner.should_send()
}

#[allow(clippy::match_same_arms)]
fn fire(
&mut self,
state: &mut Self::State,
Expand All @@ -295,6 +296,7 @@ where
true
}
Event::UpdateExecStats { .. } => true, // send it but this guy won't be handled. the only purpose is to keep this client alive else the broker thinks it is dead and will dc it
Event::Stop => true,
_ => false,
};

Expand Down Expand Up @@ -391,6 +393,11 @@ where
self.inner.process(fuzzer, state, executor)
}
}

fn on_shutdown(&mut self) -> Result<(), Error> {
self.inner.on_shutdown()?;
self.client.sender_mut().send_exiting()
}
}

impl<E, EM, EMH, S, SP, Z> EventManager<E, Z> for CentralizedEventManager<EM, EMH, S, SP>
Expand Down Expand Up @@ -476,7 +483,7 @@ impl<EM, EMH, S, SP> CentralizedEventManager<EM, EMH, S, SP>
where
EM: UsesState + EventFirer + AdaptiveSerializer + HasEventManagerId,
EMH: EventManagerHooksTuple<EM::State>,
S: State,
S: State + Stoppable,
SP: ShMemProvider,
{
#[cfg(feature = "llmp_compression")]
Expand Down Expand Up @@ -662,6 +669,9 @@ where
log::debug!("[{}] {} was discarded...)", process::id(), event_name);
}
}
Event::Stop => {
state.request_stop();
}
_ => {
return Err(Error::unknown(format!(
"Received illegal message that message should not have arrived: {:?}.",
Expand Down
27 changes: 13 additions & 14 deletions libafl/src/events/launcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ use std::process::Stdio;
#[cfg(all(unix, feature = "std"))]
use std::{fs::File, os::unix::io::AsRawFd};

#[cfg(all(unix, feature = "std", feature = "fork"))]
use libafl_bolts::llmp::Broker;
#[cfg(all(unix, feature = "std", feature = "fork"))]
use libafl_bolts::llmp::Brokers;
#[cfg(all(unix, feature = "std", feature = "fork"))]
Expand Down Expand Up @@ -716,7 +718,8 @@ where
self.time_obs.clone(),
)?;

self.main_run_client.take().unwrap()(state, c_mgr, *bind_to)
self.main_run_client.take().unwrap()(state, c_mgr, *bind_to)?;
Err(Error::shutting_down())
} else {
// Secondary clients
log::debug!("Running secondary client on PID {}", std::process::id());
Expand All @@ -733,7 +736,8 @@ where
self.time_obs.clone(),
)?;

self.secondary_run_client.take().unwrap()(state, c_mgr, *bind_to)
self.secondary_run_client.take().unwrap()(state, c_mgr, *bind_to)?;
Err(Error::shutting_down())
}
}?,
};
Expand All @@ -756,6 +760,7 @@ where
};

let mut brokers = Brokers::new();
let exit_cleanly_after = NonZeroUsize::try_from(self.cores.ids.len()).unwrap();

// Add centralized broker
brokers.add(Box::new({
Expand All @@ -769,12 +774,14 @@ where
let centralized_hooks = tuple_list!(CentralizedLlmpHook::<S::Input>::new()?);

// TODO switch to false after solving the bug
LlmpBroker::with_keep_pages_attach_to_tcp(
let mut broker = LlmpBroker::with_keep_pages_attach_to_tcp(
self.shmem_provider.clone(),
centralized_hooks,
self.centralized_broker_port,
true,
)?
)?;
broker.set_exit_after(exit_cleanly_after);
broker
}));

#[cfg(feature = "multi_machine")]
Expand Down Expand Up @@ -808,19 +815,11 @@ where
broker.inner_mut().connect_b2b(remote_broker_addr)?;
};

let exit_cleanly_after = NonZeroUsize::try_from(self.cores.ids.len()).unwrap();

broker
.inner_mut()
.set_exit_cleanly_after(exit_cleanly_after);
broker.set_exit_after(exit_cleanly_after);

brokers.add(Box::new(broker));
}

log::debug!(
"Brokers have been initialized on port {}.",
std::process::id()
);
log::debug!("Broker has been initialized; pid {}.", std::process::id());

// Loop over all the brokers that should be polled
brokers.loop_with_timeouts(Duration::from_secs(30), Some(Duration::from_millis(5)));
Expand Down
7 changes: 7 additions & 0 deletions libafl/src/events/llmp/mgr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,9 @@ where
}
}
}
Event::Stop => {
state.request_stop();
}
_ => {
return Err(Error::unknown(format!(
"Received illegal message that message should not have arrived: {:?}.",
Expand Down Expand Up @@ -626,6 +629,10 @@ where
}
Ok(count)
}

fn on_shutdown(&mut self) -> Result<(), Error> {
self.send_exiting()
}
}

impl<E, EMH, S, SP, Z> EventManager<E, Z> for LlmpEventManager<EMH, S, SP>
Expand Down
5 changes: 3 additions & 2 deletions libafl/src/events/llmp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
executors::{Executor, HasObservers},
fuzzer::{EvaluatorObservers, ExecutionProcessor},
inputs::{Input, InputConverter, NopInput, NopInputConverter, UsesInput},
state::{HasExecutions, NopState, State, UsesState},
state::{HasExecutions, NopState, State, Stoppable, UsesState},
Error, HasMetadata,
};

Expand Down Expand Up @@ -253,7 +253,7 @@ where

impl<DI, IC, ICB, S, SP> LlmpEventConverter<DI, IC, ICB, S, SP>
where
S: UsesInput + HasExecutions + HasMetadata,
S: UsesInput + HasExecutions + HasMetadata + Stoppable,
SP: ShMemProvider,
IC: InputConverter<From = S::Input, To = DI>,
ICB: InputConverter<From = DI, To = S::Input>,
Expand Down Expand Up @@ -329,6 +329,7 @@ where
}
Ok(())
}
Event::Stop => Ok(()),
_ => Err(Error::unknown(format!(
"Received illegal message that message should not have arrived: {:?}.",
event.name()
Expand Down
18 changes: 10 additions & 8 deletions libafl/src/events/llmp/restarting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ use libafl_bolts::os::startable_self;
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::{
llmp::LlmpBroker,
shmem::ShMemProvider,
tuples::{tuple_list, Handle},
};
#[cfg(feature = "std")]
use libafl_bolts::{
llmp::LlmpConnection, os::CTRL_C_EXIT, shmem::StdShMemProvider, staterestore::StateRestorer,
};
use libafl_bolts::{
llmp::{Broker, LlmpBroker},
shmem::ShMemProvider,
tuples::{tuple_list, Handle},
};
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
use typed_builder::TypedBuilder;
Expand Down Expand Up @@ -218,6 +218,10 @@ where
self.intermediate_save()?;
Ok(res)
}

fn on_shutdown(&mut self) -> Result<(), Error> {
self.send_exiting()
}
}

#[cfg(feature = "std")]
Expand Down Expand Up @@ -448,9 +452,7 @@ where
};

if let Some(exit_cleanly_after) = self.exit_cleanly_after {
broker
.inner_mut()
.set_exit_cleanly_after(exit_cleanly_after);
broker.set_exit_after(exit_cleanly_after);
}

broker.loop_with_timeouts(Duration::from_secs(30), Some(Duration::from_millis(5)));
Expand Down
44 changes: 28 additions & 16 deletions libafl/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,7 @@ pub use llmp::*;
pub mod tcp;

pub mod broker_hooks;
use alloc::{
borrow::Cow,
boxed::Box,
string::{String, ToString},
vec::Vec,
};
use alloc::{borrow::Cow, boxed::Box, string::String, vec::Vec};
use core::{
fmt,
hash::{BuildHasher, Hasher},
Expand Down Expand Up @@ -353,6 +348,8 @@ where
/// Tag of this buffer
tag: String,
},
/// Exit gracefully
Stop,
/*/// A custom type
Custom {
// TODO: Allow custom events
Expand All @@ -364,7 +361,8 @@ impl<I> Event<I>
where
I: Input,
{
fn name(&self) -> &str {
/// Event's corresponding name
pub fn name(&self) -> &str {
match self {
Event::NewTestcase { .. } => "Testcase",
Event::UpdateExecStats { .. } => "Client Heartbeat",
Expand All @@ -377,29 +375,32 @@ where
/*Event::Custom {
sender_id: _, /*custom_event} => custom_event.name()*/
} => "todo",*/
Event::Stop => "Stop",
}
}

fn name_detailed(&self) -> String {
/// Event's corresponding name with additional info
fn name_detailed(&self) -> Cow<'static, str> {
match self {
Event::NewTestcase { input, .. } => {
format!("Testcase {}", input.generate_name(None))
Cow::Owned(format!("Testcase {}", input.generate_name(None)))
}
Event::UpdateExecStats { .. } => "Client Heartbeat".to_string(),
Event::UpdateUserStats { .. } => "UserStats".to_string(),
Event::UpdateExecStats { .. } => Cow::Borrowed("Client Heartbeat"),
Event::UpdateUserStats { .. } => Cow::Borrowed("UserStats"),
#[cfg(feature = "introspection")]
Event::UpdatePerfMonitor { .. } => "PerfMonitor".to_string(),
Event::Objective { .. } => "Objective".to_string(),
Event::Log { .. } => "Log".to_string(),
Event::CustomBuf { .. } => "CustomBuf".to_string(),
Event::UpdatePerfMonitor { .. } => Cow::Borrowed("PerfMonitor"),
Event::Objective { .. } => Cow::Borrowed("Objective"),
Event::Log { .. } => Cow::Borrowed("Log"),
Event::CustomBuf { .. } => Cow::Borrowed("CustomBuf"),
Event::Stop => Cow::Borrowed("Stop"),
/*Event::Custom {
sender_id: _, /*custom_event} => custom_event.name()*/
} => "todo",*/
}
}
}

/// [`EventFirer`] fire an event.
/// [`EventFirer`] fires an event.
pub trait EventFirer: UsesState {
/// Send off an [`Event`] to the broker
///
Expand Down Expand Up @@ -574,6 +575,9 @@ pub trait EventProcessor<E, Z>: UsesState {
state: &mut Self::State,
executor: &mut E,
) -> Result<usize, Error>;

/// Shutdown gracefully; typically without saving state.
fn on_shutdown(&mut self) -> Result<(), Error>;
}
/// The id of this [`EventManager`].
/// For multi processed [`EventManager`]s,
Expand Down Expand Up @@ -662,6 +666,10 @@ where
) -> Result<usize, Error> {
Ok(0)
}

fn on_shutdown(&mut self) -> Result<(), Error> {
Ok(())
}
}

impl<E, S, Z> EventManager<E, Z> for NopEventManager<S> where
Expand Down Expand Up @@ -793,6 +801,10 @@ where
) -> Result<usize, Error> {
self.inner.process(fuzzer, state, executor)
}

fn on_shutdown(&mut self) -> Result<(), Error> {
self.inner.on_shutdown()
}
}

impl<E, EM, M, Z> EventManager<E, Z> for MonitorTypedEventManager<EM, M>
Expand Down
Loading

0 comments on commit eff4032

Please sign in to comment.