pub mod events_hooks;
pub use events_hooks::*;
pub mod simple;
pub use simple::*;
#[cfg(all(unix, feature = "std"))]
pub mod centralized;
#[cfg(all(unix, feature = "std"))]
pub use centralized::*;
#[cfg(feature = "std")]
pub mod launcher;
pub mod llmp;
pub use llmp::*;
#[cfg(feature = "tcp_manager")]
pub mod tcp;
pub mod broker_hooks;
#[cfg(feature = "introspection")]
use alloc::boxed::Box;
use alloc::{borrow::Cow, string::String, vec::Vec};
use core::{
fmt,
hash::{BuildHasher, Hasher},
marker::PhantomData,
time::Duration,
};
use ahash::RandomState;
pub use broker_hooks::*;
#[cfg(feature = "std")]
pub use launcher::*;
use libafl_bolts::current_time;
#[cfg(all(unix, feature = "std"))]
use libafl_bolts::os::CTRL_C_EXIT;
#[cfg(all(unix, feature = "std"))]
use libafl_bolts::os::unix_signals::{Signal, SignalHandler, siginfo_t, ucontext_t};
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
use uuid::Uuid;
use crate::{
Error, HasMetadata,
executors::ExitKind,
inputs::Input,
monitors::stats::UserStats,
state::{HasExecutions, HasLastReportTime, MaybeHasClientPerfMonitor},
};
#[cfg(all(unix, feature = "std", feature = "multi_machine"))]
pub mod multi_machine;
#[cfg(all(unix, feature = "std"))]
pub static mut EVENTMGR_SIGHANDLER_STATE: ShutdownSignalData = ShutdownSignalData {};
#[cfg(all(unix, feature = "std"))]
#[derive(Debug, Clone)]
pub struct ShutdownSignalData {}
#[cfg(all(unix, feature = "std"))]
impl SignalHandler for ShutdownSignalData {
unsafe fn handle(
&mut self,
_signal: Signal,
_info: &mut siginfo_t,
_context: Option<&mut ucontext_t>,
) {
unsafe {
#[cfg(unix)]
libc::_exit(CTRL_C_EXIT);
#[cfg(windows)]
windows::Win32::System::Threading::ExitProcess(100);
}
}
fn signals(&self) -> Vec<Signal> {
vec![Signal::SigTerm, Signal::SigInterrupt, Signal::SigQuit]
}
}
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct EventManagerId(
pub usize,
);
#[cfg(all(unix, feature = "std", feature = "multi_machine"))]
use crate::events::multi_machine::NodeId;
#[cfg(feature = "introspection")]
use crate::monitors::stats::ClientPerfStats;
use crate::state::HasCurrentStageId;
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub enum LogSeverity {
Debug,
Info,
Warn,
Error,
}
impl From<LogSeverity> for log::Level {
fn from(value: LogSeverity) -> Self {
match value {
LogSeverity::Debug => log::Level::Debug,
LogSeverity::Info => log::Level::Info,
LogSeverity::Warn => log::Level::Trace,
LogSeverity::Error => log::Level::Error,
}
}
}
impl fmt::Display for LogSeverity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LogSeverity::Debug => write!(f, "Debug"),
LogSeverity::Info => write!(f, "Info"),
LogSeverity::Warn => write!(f, "Warn"),
LogSeverity::Error => write!(f, "Error"),
}
}
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub enum BrokerEventResult {
Handled,
Forward,
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
pub enum EventConfig {
AlwaysUnique,
FromName {
name_hash: u64,
},
#[cfg(feature = "std")]
BuildID {
id: Uuid,
},
}
impl EventConfig {
#[must_use]
pub fn from_name(name: &str) -> Self {
let mut hasher = RandomState::with_seeds(0, 0, 0, 0).build_hasher(); hasher.write(name.as_bytes());
EventConfig::FromName {
name_hash: hasher.finish(),
}
}
#[cfg(feature = "std")]
#[must_use]
pub fn from_build_id() -> Self {
EventConfig::BuildID {
id: libafl_bolts::build_id::get(),
}
}
#[must_use]
pub fn match_with(&self, other: &EventConfig) -> bool {
match self {
EventConfig::AlwaysUnique => false,
EventConfig::FromName { name_hash: a } => match other {
#[cfg(not(feature = "std"))]
EventConfig::AlwaysUnique => false,
EventConfig::FromName { name_hash: b } => a == b,
#[cfg(feature = "std")]
EventConfig::AlwaysUnique | EventConfig::BuildID { id: _ } => false,
},
#[cfg(feature = "std")]
EventConfig::BuildID { id: a } => match other {
EventConfig::AlwaysUnique | EventConfig::FromName { name_hash: _ } => false,
EventConfig::BuildID { id: b } => a == b,
},
}
}
}
impl From<&str> for EventConfig {
fn from(name: &str) -> Self {
Self::from_name(name)
}
}
impl From<String> for EventConfig {
fn from(name: String) -> Self {
Self::from_name(&name)
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ExecStats {
time: Duration,
executions: u64,
}
impl ExecStats {
#[must_use]
pub fn new(time: Duration, executions: u64) -> Self {
Self { time, executions }
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct EventWithStats<I> {
event: Event<I>,
stats: ExecStats,
}
impl<I> EventWithStats<I> {
pub fn new(event: Event<I>, stats: ExecStats) -> Self {
Self { event, stats }
}
pub fn with_current_time(event: Event<I>, executions: u64) -> Self {
let time = current_time();
Self {
event,
stats: ExecStats { time, executions },
}
}
pub fn event(&self) -> &Event<I> {
&self.event
}
pub fn event_mut(&mut self) -> &mut Event<I> {
&mut self.event
}
pub fn stats(&self) -> &ExecStats {
&self.stats
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Event<I> {
NewTestcase {
input: I,
observers_buf: Option<Vec<u8>>,
exit_kind: ExitKind,
corpus_size: usize,
client_config: EventConfig,
forward_id: Option<libafl_bolts::ClientId>,
#[cfg(all(unix, feature = "std", feature = "multi_machine"))]
node_id: Option<NodeId>,
},
Heartbeat,
UpdateUserStats {
name: Cow<'static, str>,
value: UserStats,
phantom: PhantomData<I>,
},
#[cfg(feature = "introspection")]
UpdatePerfMonitor {
introspection_stats: Box<ClientPerfStats>,
phantom: PhantomData<I>,
},
Objective {
input: Option<I>,
objective_size: usize,
},
Log {
severity_level: LogSeverity,
message: String,
phantom: PhantomData<I>,
},
Stop,
}
impl<I> Event<I> {
pub fn name(&self) -> &str {
match self {
Event::NewTestcase { .. } => "Testcase",
Event::Heartbeat => "Client Heartbeat",
Event::UpdateUserStats { .. } => "UserStats",
#[cfg(feature = "introspection")]
Event::UpdatePerfMonitor { .. } => "PerfMonitor",
Event::Objective { .. } => "Objective",
Event::Log { .. } => "Log",
Event::Stop => "Stop",
}
}
fn name_detailed(&self) -> Cow<'static, str>
where
I: Input,
{
match self {
Event::NewTestcase { input, .. } => {
Cow::Owned(format!("Testcase {}", input.generate_name(None)))
}
Event::Heartbeat => Cow::Borrowed("Client Heartbeat"),
Event::UpdateUserStats { .. } => Cow::Borrowed("UserStats"),
#[cfg(feature = "introspection")]
Event::UpdatePerfMonitor { .. } => Cow::Borrowed("PerfMonitor"),
Event::Objective { .. } => Cow::Borrowed("Objective"),
Event::Log { .. } => Cow::Borrowed("Log"),
Event::Stop => Cow::Borrowed("Stop"),
}
}
pub fn is_new_testcase(&self) -> bool {
matches!(self, Event::NewTestcase { .. })
}
}
pub trait EventFirer<I, S> {
fn fire(&mut self, state: &mut S, event: EventWithStats<I>) -> Result<(), Error>;
fn log(
&mut self,
state: &mut S,
severity_level: LogSeverity,
message: String,
) -> Result<(), Error>
where
S: HasExecutions,
{
let executions = *state.executions();
let cur = current_time();
let stats = ExecStats {
executions,
time: cur,
};
self.fire(
state,
EventWithStats {
event: Event::Log {
severity_level,
message,
phantom: PhantomData,
},
stats,
},
)
}
fn configuration(&self) -> EventConfig {
EventConfig::AlwaysUnique
}
fn should_send(&self) -> bool;
}
pub fn std_maybe_report_progress<PR, S>(
reporter: &mut PR,
state: &mut S,
monitor_timeout: Duration,
) -> Result<(), Error>
where
PR: ProgressReporter<S>,
S: HasMetadata + HasExecutions + HasLastReportTime,
{
let Some(last_report_time) = state.last_report_time() else {
*state.last_report_time_mut() = Some(current_time());
return Ok(());
};
let cur = current_time();
if cur.checked_sub(*last_report_time).unwrap_or_default() > monitor_timeout {
reporter.report_progress(state)?;
}
Ok(())
}
pub fn std_report_progress<EM, I, S>(reporter: &mut EM, state: &mut S) -> Result<(), Error>
where
EM: EventFirer<I, S>,
S: HasExecutions + HasLastReportTime + MaybeHasClientPerfMonitor,
{
let executions = *state.executions();
let cur = current_time();
let stats = ExecStats {
executions,
time: cur,
};
#[cfg(not(feature = "introspection"))]
reporter.fire(
state,
EventWithStats {
event: Event::Heartbeat,
stats,
},
)?;
#[cfg(feature = "introspection")]
{
state
.introspection_stats_mut()
.set_current_time(libafl_bolts::cpu::read_time_counter());
reporter.fire(
state,
EventWithStats::new(
Event::UpdatePerfMonitor {
introspection_stats: Box::new(state.introspection_stats().clone()),
phantom: PhantomData,
},
stats,
),
)?;
}
*state.last_report_time_mut() = Some(cur);
Ok(())
}
pub trait ProgressReporter<S> {
fn maybe_report_progress(
&mut self,
state: &mut S,
monitor_timeout: Duration,
) -> Result<(), Error>;
fn report_progress(&mut self, state: &mut S) -> Result<(), Error>;
}
pub trait EventRestarter<S> {
fn on_restart(&mut self, state: &mut S) -> Result<(), Error>;
}
pub fn std_on_restart<EM, S>(restarter: &mut EM, state: &mut S) -> Result<(), Error>
where
EM: EventRestarter<S> + AwaitRestartSafe,
S: HasCurrentStageId,
{
state.on_restart()?;
restarter.await_restart_safe();
Ok(())
}
pub trait SendExiting {
fn send_exiting(&mut self) -> Result<(), Error>;
fn on_shutdown(&mut self) -> Result<(), Error>;
}
pub trait AwaitRestartSafe {
fn await_restart_safe(&mut self);
}
pub trait EventReceiver<I, S> {
fn try_receive(&mut self, state: &mut S) -> Result<Option<(EventWithStats<I>, bool)>, Error>;
fn on_interesting(&mut self, state: &mut S, event: EventWithStats<I>) -> Result<(), Error>;
}
pub trait HasEventManagerId {
fn mgr_id(&self) -> EventManagerId;
}
#[derive(Debug, Copy, Clone, Default)]
pub struct NopEventManager {}
impl NopEventManager {
#[must_use]
pub fn new() -> Self {
NopEventManager {}
}
}
impl<I, S> EventFirer<I, S> for NopEventManager {
fn should_send(&self) -> bool {
true
}
fn fire(&mut self, _state: &mut S, _event: EventWithStats<I>) -> Result<(), Error> {
Ok(())
}
}
impl<S> EventRestarter<S> for NopEventManager
where
S: HasCurrentStageId,
{
fn on_restart(&mut self, state: &mut S) -> Result<(), Error> {
std_on_restart(self, state)
}
}
impl SendExiting for NopEventManager {
fn send_exiting(&mut self) -> Result<(), Error> {
Ok(())
}
fn on_shutdown(&mut self) -> Result<(), Error> {
Ok(())
}
}
impl AwaitRestartSafe for NopEventManager {
fn await_restart_safe(&mut self) {}
}
impl<I, S> EventReceiver<I, S> for NopEventManager {
fn try_receive(&mut self, _state: &mut S) -> Result<Option<(EventWithStats<I>, bool)>, Error> {
Ok(None)
}
fn on_interesting(
&mut self,
_state: &mut S,
_event_vec: EventWithStats<I>,
) -> Result<(), Error> {
Ok(())
}
}
impl<S> ProgressReporter<S> for NopEventManager {
fn maybe_report_progress(
&mut self,
_state: &mut S,
_monitor_timeout: Duration,
) -> Result<(), Error> {
Ok(())
}
fn report_progress(&mut self, _state: &mut S) -> Result<(), Error> {
Ok(())
}
}
impl HasEventManagerId for NopEventManager {
fn mgr_id(&self) -> EventManagerId {
EventManagerId(0)
}
}
#[cfg(test)]
mod tests {
use libafl_bolts::{Named, tuples::tuple_list};
use tuple_list::tuple_list_type;
use crate::{
events::{Event, EventConfig},
executors::ExitKind,
inputs::bytes::BytesInput,
observers::StdMapObserver,
};
static mut MAP: [u32; 4] = [0; 4];
#[test]
fn test_event_serde() {
let map_ptr = &raw const MAP;
let obv = unsafe {
let len = (*map_ptr).len();
StdMapObserver::from_mut_ptr("test", &raw mut MAP as *mut u32, len)
};
let map = tuple_list!(obv);
let observers_buf = postcard::to_allocvec(&map).unwrap();
let i = BytesInput::new(vec![0]);
let e = Event::NewTestcase {
input: i,
observers_buf: Some(observers_buf),
exit_kind: ExitKind::Ok,
corpus_size: 123,
client_config: EventConfig::AlwaysUnique,
forward_id: None,
#[cfg(all(unix, feature = "std", feature = "multi_machine"))]
node_id: None,
};
let serialized = postcard::to_allocvec(&e).unwrap();
let d = postcard::from_bytes::<Event<BytesInput>>(&serialized).unwrap();
match d {
Event::NewTestcase { observers_buf, .. } => {
let o: tuple_list_type!(StdMapObserver::<u32, false>) =
postcard::from_bytes(observers_buf.as_ref().unwrap()).unwrap();
assert_eq!("test", o.0.name());
}
_ => panic!("mistmatch"),
}
}
}