diff --git a/fuzzers/qemu_launcher/Cargo.toml b/fuzzers/qemu_launcher/Cargo.toml index 7f06f96dd1..2334ced11c 100644 --- a/fuzzers/qemu_launcher/Cargo.toml +++ b/fuzzers/qemu_launcher/Cargo.toml @@ -8,6 +8,9 @@ edition = "2021" default = ["std", "injections"] std = [] +## Build with a simple event manager instead of Launcher - don't fork, and crash after the first bug. +simplemgr = [] + ## Enable fuzzing for injections (where supported) injections = ["libafl_qemu/injections"] diff --git a/fuzzers/qemu_launcher/src/client.rs b/fuzzers/qemu_launcher/src/client.rs index 534db00b76..39e2827dae 100644 --- a/fuzzers/qemu_launcher/src/client.rs +++ b/fuzzers/qemu_launcher/src/client.rs @@ -2,14 +2,12 @@ use std::{env, ops::Range}; use libafl::{ corpus::{InMemoryOnDiskCorpus, OnDiskCorpus}, - events::LlmpRestartingEventManager, inputs::BytesInput, + monitors::Monitor, state::StdState, Error, }; -use libafl_bolts::{ - core_affinity::CoreId, rands::StdRand, shmem::StdShMemProvider, tuples::tuple_list, -}; +use libafl_bolts::{core_affinity::CoreId, rands::StdRand, tuples::tuple_list}; #[cfg(feature = "injections")] use libafl_qemu::injections::QemuInjectionHelper; use libafl_qemu::{ @@ -20,7 +18,10 @@ use libafl_qemu::{ ArchExtras, Emulator, GuestAddr, QemuInstrumentationAddressRangeFilter, }; -use crate::{instance::Instance, options::FuzzerOptions}; +use crate::{ + instance::{ClientMgr, Instance}, + options::FuzzerOptions, +}; #[allow(clippy::module_name_repetitions)] pub type ClientState = @@ -100,10 +101,10 @@ impl<'a> Client<'a> { } } - pub fn run( + pub fn run( &self, state: Option, - mgr: LlmpRestartingEventManager, + mgr: ClientMgr, core_id: CoreId, ) -> Result<(), Error> { let mut args = self.args()?; diff --git a/fuzzers/qemu_launcher/src/fuzzer.rs b/fuzzers/qemu_launcher/src/fuzzer.rs index dfce4f264b..fe088f9f9e 100644 --- a/fuzzers/qemu_launcher/src/fuzzer.rs +++ b/fuzzers/qemu_launcher/src/fuzzer.rs @@ -5,18 +5,22 @@ use std::{ }; use clap::Parser; +#[cfg(feature = "simplemgr")] +use libafl::events::SimpleEventManager; +#[cfg(not(feature = "simplemgr"))] +use libafl::events::{EventConfig, Launcher, MonitorTypedEventManager}; use libafl::{ - events::{EventConfig, Launcher}, monitors::{ tui::{ui::TuiUI, TuiMonitor}, Monitor, MultiMonitor, }, Error, }; -use libafl_bolts::{ - current_time, - shmem::{ShMemProvider, StdShMemProvider}, -}; +#[cfg(feature = "simplemgr")] +use libafl_bolts::core_affinity::CoreId; +use libafl_bolts::current_time; +#[cfg(not(feature = "simplemgr"))] +use libafl_bolts::shmem::{ShMemProvider, StdShMemProvider}; #[cfg(unix)] use { nix::unistd::dup, @@ -78,9 +82,11 @@ impl Fuzzer { M: Monitor + Clone, { // The shared memory allocator + #[cfg(not(feature = "simplemgr"))] let shmem_provider = StdShMemProvider::new()?; /* If we are running in verbose, don't provide a replacement stdout, otherwise, use /dev/null */ + #[cfg(not(feature = "simplemgr"))] let stdout = if self.options.verbose { None } else { @@ -89,13 +95,17 @@ impl Fuzzer { let client = Client::new(&self.options); + #[cfg(feature = "simplemgr")] + return client.run(None, SimpleEventManager::new(monitor), CoreId(0)); + // Build and run a Launcher + #[cfg(not(feature = "simplemgr"))] match Launcher::builder() .shmem_provider(shmem_provider) .broker_port(self.options.port) .configuration(EventConfig::from_build_id()) .monitor(monitor) - .run_client(|s, m, c| client.run(s, m, c)) + .run_client(|s, m, c| client.run(s, MonitorTypedEventManager::<_, M>::new(m), c)) .cores(&self.options.cores) .stdout_file(stdout) .stderr_file(stdout) diff --git a/fuzzers/qemu_launcher/src/instance.rs b/fuzzers/qemu_launcher/src/instance.rs index 51b297aac8..81ee9b086b 100644 --- a/fuzzers/qemu_launcher/src/instance.rs +++ b/fuzzers/qemu_launcher/src/instance.rs @@ -1,14 +1,19 @@ use core::ptr::addr_of_mut; -use std::process; +use std::{marker::PhantomData, process}; +#[cfg(feature = "simplemgr")] +use libafl::events::SimpleEventManager; +#[cfg(not(feature = "simplemgr"))] +use libafl::events::{LlmpRestartingEventManager, MonitorTypedEventManager}; use libafl::{ corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, - events::{EventRestarter, LlmpRestartingEventManager}, + events::EventRestarter, executors::ShadowExecutor, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Evaluator, Fuzzer, StdFuzzer}, inputs::BytesInput, + monitors::Monitor, mutators::{ scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations, StdMOptMutator, StdScheduledMutator, Tokens, @@ -24,11 +29,12 @@ use libafl::{ state::{HasCorpus, HasMetadata, StdState, UsesState}, Error, }; +#[cfg(not(feature = "simplemgr"))] +use libafl_bolts::shmem::StdShMemProvider; use libafl_bolts::{ core_affinity::CoreId, current_nanos, rands::StdRand, - shmem::StdShMemProvider, tuples::{tuple_list, Merge}, }; use libafl_qemu::{ @@ -44,18 +50,24 @@ use crate::{harness::Harness, options::FuzzerOptions}; pub type ClientState = StdState, StdRand, OnDiskCorpus>; -pub type ClientMgr = LlmpRestartingEventManager; +#[cfg(feature = "simplemgr")] +pub type ClientMgr = SimpleEventManager; +#[cfg(not(feature = "simplemgr"))] +pub type ClientMgr = + MonitorTypedEventManager, M>; #[derive(TypedBuilder)] -pub struct Instance<'a> { +pub struct Instance<'a, M: Monitor> { options: &'a FuzzerOptions, emu: &'a Emulator, - mgr: ClientMgr, + mgr: ClientMgr, core_id: CoreId, extra_tokens: Option>, + #[builder(default=PhantomData)] + phantom: PhantomData, } -impl<'a> Instance<'a> { +impl<'a, M: Monitor> Instance<'a, M> { pub fn run(&mut self, helpers: QT, state: Option) -> Result<(), Error> where QT: QemuHelperTuple, @@ -207,11 +219,11 @@ impl<'a> Instance<'a> { stages: &mut ST, ) -> Result<(), Error> where - Z: Fuzzer + Z: Fuzzer, ST> + UsesState - + Evaluator, + + Evaluator, State = ClientState>, E: UsesState, - ST: StagesTuple, + ST: StagesTuple, ClientState, Z>, { let corpus_dirs = [self.options.input_dir()]; diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index fbf563e9c6..536bb3177f 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -521,16 +521,16 @@ where // If we are measuring scalability stuff.. #[cfg(feature = "scalability_introspection")] { - let received_with_observer = state.scalability_monitor().testcase_with_observers; - let received_without_observer = state.scalability_monitor().testcase_without_observers; + let imported_with_observer = state.scalability_monitor().testcase_with_observers; + let imported_without_observer = state.scalability_monitor().testcase_without_observers; self.fire( state, Event::UpdateUserStats { - name: "total received".to_string(), + name: "total imported".to_string(), value: UserStats::new( UserStatsValue::Number( - (received_with_observer + received_without_observer) as u64, + (imported_with_observer + imported_without_observer) as u64, ), AggregatorOps::Avg, ), @@ -694,6 +694,158 @@ impl HasEventManagerId for NopEventManager { } } +/// An [`EventManager`] type that wraps another manager, but captures a `monitor` type as well. +/// This is useful to keep the same API between managers with and without an internal `monitor`. +#[derive(Copy, Clone, Debug)] +pub struct MonitorTypedEventManager { + inner: EM, + phantom: PhantomData, +} + +impl MonitorTypedEventManager { + /// Creates a new [`EventManager`] that wraps another manager, but captures a `monitor` type as well. + #[must_use] + pub fn new(inner: EM) -> Self { + MonitorTypedEventManager { + inner, + phantom: PhantomData, + } + } +} + +impl UsesState for MonitorTypedEventManager +where + EM: UsesState, +{ + type State = EM::State; +} + +impl EventFirer for MonitorTypedEventManager +where + EM: EventFirer, +{ + #[inline] + fn fire( + &mut self, + state: &mut Self::State, + event: Event<::Input>, + ) -> Result<(), Error> { + self.inner.fire(state, event) + } + + #[inline] + fn log( + &mut self, + state: &mut Self::State, + severity_level: LogSeverity, + message: String, + ) -> Result<(), Error> { + self.inner.log(state, severity_level, message) + } + + #[inline] + fn serialize_observers(&mut self, observers: &OT) -> Result>, Error> + where + OT: ObserversTuple + Serialize, + { + self.inner.serialize_observers(observers) + } + + #[inline] + fn configuration(&self) -> EventConfig { + self.inner.configuration() + } +} + +impl EventRestarter for MonitorTypedEventManager +where + EM: EventRestarter, +{ + #[inline] + fn on_restart(&mut self, state: &mut Self::State) -> Result<(), Error> { + self.inner.on_restart(state) + } + + #[inline] + fn send_exiting(&mut self) -> Result<(), Error> { + self.inner.send_exiting() + } + + #[inline] + fn await_restart_safe(&mut self) { + self.inner.await_restart_safe(); + } +} + +impl EventProcessor for MonitorTypedEventManager +where + EM: EventProcessor, +{ + #[inline] + fn process( + &mut self, + fuzzer: &mut Z, + state: &mut Self::State, + executor: &mut E, + ) -> Result { + self.inner.process(fuzzer, state, executor) + } +} + +impl EventManager for MonitorTypedEventManager +where + EM: EventManager, + EM::State: HasLastReportTime + HasExecutions + HasMetadata, +{ +} + +impl HasCustomBufHandlers for MonitorTypedEventManager +where + Self: UsesState, + EM: HasCustomBufHandlers, +{ + #[inline] + fn add_custom_buf_handler( + &mut self, + handler: Box< + dyn FnMut(&mut Self::State, &String, &[u8]) -> Result, + >, + ) { + self.inner.add_custom_buf_handler(handler); + } +} + +impl ProgressReporter for MonitorTypedEventManager +where + Self: UsesState, + EM: ProgressReporter, + EM::State: HasLastReportTime + HasExecutions + HasMetadata, +{ + #[inline] + fn maybe_report_progress( + &mut self, + state: &mut Self::State, + monitor_timeout: Duration, + ) -> Result<(), Error> { + self.inner.maybe_report_progress(state, monitor_timeout) + } + + #[inline] + fn report_progress(&mut self, state: &mut Self::State) -> Result<(), Error> { + self.inner.report_progress(state) + } +} + +impl HasEventManagerId for MonitorTypedEventManager +where + EM: HasEventManagerId, +{ + #[inline] + fn mgr_id(&self) -> EventManagerId { + self.inner.mgr_id() + } +} + #[cfg(test)] mod tests {