From 1fafaf645412f21ce6b02b14f80f6745968d50c7 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Thu, 23 May 2024 18:56:39 +0200 Subject: [PATCH] Make every Builder ::builder(), so BobTheBuilder::new() becomes BobThe::builder() (#2242) * Make every builder ::builder() * Fix no_std * More * Fix clippy, stuff * More fun * Make NopShMem do something * Alloc * more fmt * Remove UB in tinyinst executor builder * Make builder order not matter for tinyinst * More better * fix * docs * fmt * more fmt * clippy * fix fixes * tiny thing * more betterg * more more * more builder * more builder * more nyx * undo breaking clippy * clip --- fuzzers/nautilus_sync/src/lib.rs | 4 +- fuzzers/nyx_libxml2_parallel/src/main.rs | 4 +- fuzzers/nyx_libxml2_standalone/src/main.rs | 4 +- fuzzers/tinyinst_simple/src/main.rs | 15 +-- libafl/src/events/centralized.rs | 15 ++- libafl/src/events/launcher.rs | 6 +- libafl/src/events/llmp/mgr.rs | 14 ++- libafl/src/events/llmp/mod.rs | 22 ++++- libafl/src/events/llmp/restarting.rs | 33 ++++--- libafl/src/events/tcp.rs | 11 +++ libafl/src/executors/command.rs | 3 +- libafl/src/executors/forkserver.rs | 4 +- libafl/src/executors/inprocess/inner.rs | 1 + libafl/src/executors/inprocess/mod.rs | 4 +- libafl/src/executors/inprocess/stateful.rs | 2 + .../concolic/serialization_format.rs | 23 ++--- libafl_bolts/src/build_id.rs | 9 +- libafl_bolts/src/shmem.rs | 72 +++++++++++++- libafl_frida/src/helper.rs | 4 +- .../libafl_libfuzzer_runtime/src/merge.rs | 1 + libafl_nyx/src/executor.rs | 7 ++ libafl_sugar/src/forkserver.rs | 6 +- libafl_targets/build.rs | 1 + libafl_tinyinst/src/executor.rs | 97 +++++++++++-------- 24 files changed, 253 insertions(+), 109 deletions(-) diff --git a/fuzzers/nautilus_sync/src/lib.rs b/fuzzers/nautilus_sync/src/lib.rs index 35076ee4be..793e18e6fe 100644 --- a/fuzzers/nautilus_sync/src/lib.rs +++ b/fuzzers/nautilus_sync/src/lib.rs @@ -9,7 +9,7 @@ use std::{env, net::SocketAddr, path::PathBuf, time::Duration}; use clap::Parser; use libafl::{ corpus::{InMemoryCorpus, OnDiskCorpus}, - events::{launcher::Launcher, EventConfig, LlmpEventConverterBuilder}, + events::{launcher::Launcher, llmp::LlmpEventConverter, EventConfig}, executors::{inprocess::InProcessExecutor, ExitKind}, feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, NautilusChunksMetadata, NautilusFeedback}, @@ -120,7 +120,7 @@ pub extern "C" fn libafl_main() { let context = NautilusContext::from_file(15, "grammar.json"); let mut event_converter = opt.bytes_broker_port.map(|port| { - LlmpEventConverterBuilder::new() + LlmpEventConverter::builder() .build_on_port( shmem_provider.clone(), port, diff --git a/fuzzers/nyx_libxml2_parallel/src/main.rs b/fuzzers/nyx_libxml2_parallel/src/main.rs index baa72f7ac2..017d9c667d 100644 --- a/fuzzers/nyx_libxml2_parallel/src/main.rs +++ b/fuzzers/nyx_libxml2_parallel/src/main.rs @@ -19,7 +19,7 @@ use libafl_bolts::{ shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, }; -use libafl_nyx::{executor::NyxExecutorBuilder, helper::NyxHelper, settings::NyxSettings}; +use libafl_nyx::{executor::NyxExecutor, helper::NyxHelper, settings::NyxSettings}; fn main() { let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); @@ -54,7 +54,7 @@ fn main() { let mut feedback = MaxMapFeedback::new(&observer); let mut objective = CrashFeedback::new(); let scheduler = RandScheduler::new(); - let mut executor = NyxExecutorBuilder::new().build(helper, tuple_list!(observer)); + let mut executor = NyxExecutor::builder().build(helper, tuple_list!(observer)); // If not restarting, create a State from scratch let mut state = state.unwrap_or_else(|| { diff --git a/fuzzers/nyx_libxml2_standalone/src/main.rs b/fuzzers/nyx_libxml2_standalone/src/main.rs index 22f718e4d5..3ba9e02d39 100644 --- a/fuzzers/nyx_libxml2_standalone/src/main.rs +++ b/fuzzers/nyx_libxml2_standalone/src/main.rs @@ -14,7 +14,7 @@ use libafl::{ Fuzzer, StdFuzzer, }; use libafl_bolts::{rands::StdRand, tuples::tuple_list}; -use libafl_nyx::{executor::NyxExecutorBuilder, helper::NyxHelper, settings::NyxSettings}; +use libafl_nyx::{executor::NyxExecutor, helper::NyxHelper, settings::NyxSettings}; fn main() { // nyx stuff @@ -44,7 +44,7 @@ fn main() { let monitor = TuiMonitor::new(ui); let mut mgr = SimpleEventManager::new(monitor); - let mut executor = NyxExecutorBuilder::new().build(helper, tuple_list!(observer)); + let mut executor = NyxExecutor::builder().build(helper, tuple_list!(observer)); let mutator = StdScheduledMutator::new(havoc_mutations()); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); diff --git a/fuzzers/tinyinst_simple/src/main.rs b/fuzzers/tinyinst_simple/src/main.rs index a0d7349296..bfc96b4e2b 100644 --- a/fuzzers/tinyinst_simple/src/main.rs +++ b/fuzzers/tinyinst_simple/src/main.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{path::PathBuf, ptr::addr_of_mut, time::Duration}; use libafl::{ corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus, Testcase}, @@ -20,7 +20,7 @@ use libafl_bolts::shmem::Win32ShMemProvider; use libafl_bolts::{ ownedref::OwnedMutPtr, rands::StdRand, shmem::ShMemProvider, tuples::tuple_list, }; -use libafl_tinyinst::executor::TinyInstExecutorBuilder; +use libafl_tinyinst::executor::TinyInstExecutor; static mut COVERAGE: Vec = vec![]; #[cfg(not(any(target_vendor = "apple", windows, target_os = "linux")))] @@ -37,7 +37,7 @@ fn main() { // use file to pass testcases // let args = vec!["test.exe".to_string(), "-f".to_string(), "@@".to_string()]; - let coverage = unsafe { OwnedMutPtr::Ptr(core::ptr::addr_of_mut!(COVERAGE)) }; + let coverage = unsafe { OwnedMutPtr::Ptr(addr_of_mut!(COVERAGE)) }; let observer = ListObserver::new("cov", coverage); let mut feedback = ListFeedback::new(&observer); #[cfg(windows)] @@ -59,18 +59,19 @@ fn main() { let scheduler = RandScheduler::new(); let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); - let monitor = SimpleMonitor::new(|x| println!("{}", x)); + let monitor = SimpleMonitor::new(|x| println!("{x}")); let mut mgr = SimpleEventManager::new(monitor); let mut executor = unsafe { - TinyInstExecutorBuilder::new() + TinyInstExecutor::builder() .tinyinst_args(tinyinst_args) .program_args(args) .use_shmem() .persistent("test.exe".to_string(), "fuzz".to_string(), 1, 10000) - .timeout(std::time::Duration::new(5, 0)) + .timeout(Duration::new(5, 0)) .shmem_provider(&mut shmem_provider) - .build(&mut COVERAGE, tuple_list!(observer)) + .coverage_ptr(addr_of_mut!(COVERAGE)) + .build(tuple_list!(observer)) .unwrap() }; let mutator = StdScheduledMutator::new(havoc_mutations()); diff --git a/libafl/src/events/centralized.rs b/libafl/src/events/centralized.rs index 0a7481ac9c..02b9e4c276 100644 --- a/libafl/src/events/centralized.rs +++ b/libafl/src/events/centralized.rs @@ -19,11 +19,12 @@ use libafl_bolts::{ }; use libafl_bolts::{ llmp::{self, LlmpBroker, LlmpClient, LlmpClientDescription, Tag}, - shmem::ShMemProvider, + shmem::{NopShMemProvider, ShMemProvider}, ClientId, }; use serde::{Deserialize, Serialize}; +use super::NopEventManager; #[cfg(feature = "llmp_compression")] use crate::events::llmp::COMPRESS_THRESHOLD; #[cfg(feature = "adaptive_serialization")] @@ -38,9 +39,9 @@ use crate::{ }, executors::{Executor, HasObservers}, fuzzer::{EvaluatorObservers, ExecutionProcessor}, - inputs::{Input, UsesInput}, + inputs::{Input, NopInput, UsesInput}, observers::ObserversTuple, - state::{HasExecutions, HasLastReportTime, UsesState}, + state::{HasExecutions, HasLastReportTime, NopState, UsesState}, Error, HasMetadata, }; @@ -230,6 +231,14 @@ where is_main: bool, } +impl CentralizedEventManager>, NopShMemProvider> { + /// Creates a builder for [`CentralizedEventManager`] + #[must_use] + pub fn builder() -> CentralizedEventManagerBuilder { + CentralizedEventManagerBuilder::new() + } +} + /// The builder or `CentralizedEventManager` #[derive(Debug)] pub struct CentralizedEventManagerBuilder { diff --git a/libafl/src/events/launcher.rs b/libafl/src/events/launcher.rs index e2b2f7ce17..a29ef5307b 100644 --- a/libafl/src/events/launcher.rs +++ b/libafl/src/events/launcher.rs @@ -48,10 +48,8 @@ use libafl_bolts::{ use typed_builder::TypedBuilder; use super::hooks::EventManagerHooksTuple; -#[cfg(all(unix, feature = "std"))] -use crate::events::centralized::CentralizedEventManagerBuilder; #[cfg(all(unix, feature = "std", feature = "fork"))] -use crate::events::{CentralizedEventManager, CentralizedLlmpEventBroker}; +use crate::events::centralized::{CentralizedEventManager, CentralizedLlmpEventBroker}; #[cfg(feature = "adaptive_serialization")] use crate::observers::TimeObserver; #[cfg(feature = "std")] @@ -698,7 +696,7 @@ where let builder = builder.time_ref(self.time_obs.handle()); let (state, mgr) = builder.build().launch()?; - let mut centralized_builder = CentralizedEventManagerBuilder::new(); + let mut centralized_builder = CentralizedEventManager::builder(); if index == 1 { centralized_builder = centralized_builder.is_main(true); diff --git a/libafl/src/events/llmp/mgr.rs b/libafl/src/events/llmp/mgr.rs index b1f867eb47..58deecd018 100644 --- a/libafl/src/events/llmp/mgr.rs +++ b/libafl/src/events/llmp/mgr.rs @@ -18,7 +18,7 @@ use libafl_bolts::{ use libafl_bolts::{ current_time, llmp::{LlmpClient, LlmpClientDescription}, - shmem::ShMemProvider, + shmem::{NopShMemProvider, ShMemProvider}, ClientId, }; #[cfg(feature = "std")] @@ -44,9 +44,9 @@ use crate::{ }, executors::{Executor, HasObservers}, fuzzer::{EvaluatorObservers, ExecutionProcessor}, - inputs::UsesInput, + inputs::{NopInput, UsesInput}, observers::ObserversTuple, - state::{HasExecutions, HasLastReportTime, State, UsesState}, + state::{HasExecutions, HasLastReportTime, NopState, State, UsesState}, Error, HasMetadata, }; @@ -85,6 +85,14 @@ where phantom: PhantomData, } +impl LlmpEventManager<(), NopState, NopShMemProvider> { + /// Creates a builder for [`LlmpEventManager`] + #[must_use] + pub fn builder() -> LlmpEventManagerBuilder<()> { + LlmpEventManagerBuilder::new() + } +} + /// Builder for `LlmpEventManager` #[derive(Debug, Copy, Clone)] pub struct LlmpEventManagerBuilder { diff --git a/libafl/src/events/llmp/mod.rs b/libafl/src/events/llmp/mod.rs index c60cf2c0c7..318c751e16 100644 --- a/libafl/src/events/llmp/mod.rs +++ b/libafl/src/events/llmp/mod.rs @@ -10,7 +10,7 @@ use libafl_bolts::{ }; use libafl_bolts::{ llmp::{LlmpClient, LlmpClientDescription, Tag}, - shmem::ShMemProvider, + shmem::{NopShMemProvider, ShMemProvider}, ClientId, }; use serde::Deserialize; @@ -19,8 +19,8 @@ use crate::{ events::{CustomBufEventResult, CustomBufHandlerFn, Event, EventFirer}, executors::{Executor, HasObservers}, fuzzer::{EvaluatorObservers, ExecutionProcessor}, - inputs::{Input, InputConverter, UsesInput}, - state::{HasExecutions, State, UsesState}, + inputs::{Input, InputConverter, NopInput, NopInputConverter, UsesInput}, + state::{HasExecutions, NopState, State, UsesState}, Error, HasMetadata, }; @@ -108,6 +108,22 @@ where phantom: PhantomData, } +impl + LlmpEventConverter< + NopInput, + NopInputConverter, + NopInputConverter, + NopState, + NopShMemProvider, + > +{ + /// Create a builder for [`LlmpEventConverter`] + #[must_use] + pub fn builder() -> LlmpEventConverterBuilder { + LlmpEventConverterBuilder::new() + } +} + /// Build `LlmpEventConverter` #[derive(Debug, Clone, Default)] pub struct LlmpEventConverterBuilder { diff --git a/libafl/src/events/llmp/restarting.rs b/libafl/src/events/llmp/restarting.rs index ad9c7f98ca..0abe3ec087 100644 --- a/libafl/src/events/llmp/restarting.rs +++ b/libafl/src/events/llmp/restarting.rs @@ -1,4 +1,7 @@ -//! Llmp restarting manager +//! The `LLMP` restarting manager will +//! forward messages over lockless shared maps. +//! When the target crashes, a watch process (the parent) will +//! restart/refork it. use alloc::vec::Vec; #[cfg(all(unix, not(miri), feature = "std"))] @@ -42,7 +45,7 @@ use crate::{ events::{ hooks::EventManagerHooksTuple, Event, EventConfig, EventFirer, EventManager, EventManagerId, EventProcessor, EventRestarter, HasEventManagerId, LlmpEventBroker, - LlmpEventManager, LlmpEventManagerBuilder, LlmpShouldSaveState, ProgressReporter, + LlmpEventManager, LlmpShouldSaveState, ProgressReporter, }, executors::{Executor, HasObservers}, fuzzer::{EvaluatorObservers, ExecutionProcessor}, @@ -483,11 +486,11 @@ where } LlmpConnection::IsClient { client } => { #[cfg(not(feature = "adaptive_serialization"))] - let mgr: LlmpEventManager = LlmpEventManagerBuilder::new() + let mgr: LlmpEventManager = LlmpEventManager::builder() .hooks(self.hooks) .build_from_client(client, self.configuration)?; #[cfg(feature = "adaptive_serialization")] - let mgr: LlmpEventManager = LlmpEventManagerBuilder::new() + let mgr: LlmpEventManager = LlmpEventManager::builder() .hooks(self.hooks) .build_from_client( client, @@ -511,7 +514,7 @@ where ManagerKind::Client { cpu_core } => { // We are a client #[cfg(not(feature = "adaptive_serialization"))] - let mgr = LlmpEventManagerBuilder::new() + let mgr = LlmpEventManager::builder() .hooks(self.hooks) .build_on_port( self.shmem_provider.clone(), @@ -519,7 +522,7 @@ where self.configuration, )?; #[cfg(feature = "adaptive_serialization")] - let mgr = LlmpEventManagerBuilder::new() + let mgr = LlmpEventManager::builder() .hooks(self.hooks) .build_on_port( self.shmem_provider.clone(), @@ -648,7 +651,7 @@ where let (state, mut mgr) = if let Some((state_opt, mgr_description)) = staterestorer.restore()? { #[cfg(not(feature = "adaptive_serialization"))] - let llmp_mgr = LlmpEventManagerBuilder::new() + let llmp_mgr = LlmpEventManager::builder() .hooks(self.hooks) .build_existing_client_from_description( new_shmem_provider, @@ -656,7 +659,7 @@ where self.configuration, )?; #[cfg(feature = "adaptive_serialization")] - let llmp_mgr = LlmpEventManagerBuilder::new() + let llmp_mgr = LlmpEventManager::builder() .hooks(self.hooks) .build_existing_client_from_description( new_shmem_provider, @@ -676,7 +679,7 @@ where log::info!("First run. Let's set it all up"); // Mgr to send and receive msgs from/to all other fuzzer instances #[cfg(not(feature = "adaptive_serialization"))] - let mgr = LlmpEventManagerBuilder::new() + let mgr = LlmpEventManager::builder() .hooks(self.hooks) .build_existing_client_from_env( new_shmem_provider, @@ -684,7 +687,7 @@ where self.configuration, )?; #[cfg(feature = "adaptive_serialization")] - let mgr = LlmpEventManagerBuilder::new() + let mgr = LlmpEventManager::builder() .hooks(self.hooks) .build_existing_client_from_env( new_shmem_provider, @@ -738,7 +741,7 @@ mod tests { use crate::{ corpus::{Corpus, InMemoryCorpus, Testcase}, - events::llmp::{restarting::_ENV_FUZZER_SENDER, LlmpEventManagerBuilder}, + events::llmp::{restarting::_ENV_FUZZER_SENDER, LlmpEventManager}, executors::{ExitKind, InProcessExecutor}, feedbacks::ConstFeedback, fuzzer::Fuzzer, @@ -788,11 +791,11 @@ mod tests { } #[cfg(not(feature = "adaptive_serialization"))] - let mut llmp_mgr = LlmpEventManagerBuilder::new() + let mut llmp_mgr = LlmpEventManager::builder() .build_from_client(llmp_client, "fuzzer".into()) .unwrap(); #[cfg(feature = "adaptive_serialization")] - let mut llmp_mgr = LlmpEventManagerBuilder::new() + let mut llmp_mgr = LlmpEventManager::builder() .build_from_client(llmp_client, "fuzzer".into(), time_ref.clone()) .unwrap(); @@ -837,7 +840,7 @@ mod tests { let (mut state_clone, mgr_description) = staterestorer.restore().unwrap().unwrap(); #[cfg(not(feature = "adaptive_serialization"))] - let mut llmp_clone = LlmpEventManagerBuilder::new() + let mut llmp_clone = LlmpEventManager::builder() .build_existing_client_from_description( shmem_provider, &mgr_description, @@ -845,7 +848,7 @@ mod tests { ) .unwrap(); #[cfg(feature = "adaptive_serialization")] - let mut llmp_clone = LlmpEventManagerBuilder::new() + let mut llmp_clone = LlmpEventManager::builder() .build_existing_client_from_description( shmem_provider, &mgr_description, diff --git a/libafl/src/events/tcp.rs b/libafl/src/events/tcp.rs index 1f1d57b48b..2cf5b2cb13 100644 --- a/libafl/src/events/tcp.rs +++ b/libafl/src/events/tcp.rs @@ -440,6 +440,17 @@ where phantom: PhantomData, } +impl TcpEventManager<(), S> +where + S: State, +{ + /// Create a builder for [`TcpEventManager`] + #[must_use] + pub fn builder() -> TcpEventManagerBuilder<(), S> { + TcpEventManagerBuilder::new() + } +} + /// Builder for `TcpEventManager` #[derive(Debug, Copy, Clone)] pub struct TcpEventManagerBuilder { diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index 2614b711eb..a53a22ee98 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -182,8 +182,7 @@ impl CommandExecutor<(), (), ()> { /// By default, input is read from stdin, unless you specify a different location using /// * `arg_input_arg` for input delivered _as_ an command line argument /// * `arg_input_file` for input via a file of a specific name - /// * `arg_input_file_std` for a file with default name - /// (at the right location in the arguments) + /// * `arg_input_file_std` for a file with default name (at the right location in the arguments) #[must_use] pub fn builder() -> CommandExecutorBuilder { CommandExecutorBuilder::new() diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index 00120d7b9d..e4e1ef187a 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -1324,7 +1324,7 @@ mod tests { use serial_test::serial; use crate::{ - executors::forkserver::ForkserverExecutorBuilder, + executors::forkserver::ForkserverExecutor, observers::{ConstMapObserver, HitcountsMapObserver}, Error, }; @@ -1348,7 +1348,7 @@ mod tests { shmem_buf, )); - let executor = ForkserverExecutorBuilder::new() + let executor = ForkserverExecutor::builder() .program(bin) .args(args) .debug_child(false) diff --git a/libafl/src/executors/inprocess/inner.rs b/libafl/src/executors/inprocess/inner.rs index 492e474dae..e0d850ac93 100644 --- a/libafl/src/executors/inprocess/inner.rs +++ b/libafl/src/executors/inprocess/inner.rs @@ -219,6 +219,7 @@ where /// * `user_hooks` - the hooks run before and after the harness's execution /// * `harness_fn` - the harness, executing the function /// * `observers` - the observers observing the target during execution + /// /// This may return an error on unix, if signal handler setup fails pub fn with_timeout_generic( user_hooks: HT, diff --git a/libafl/src/executors/inprocess/mod.rs b/libafl/src/executors/inprocess/mod.rs index 97df8116b0..901dd31b56 100644 --- a/libafl/src/executors/inprocess/mod.rs +++ b/libafl/src/executors/inprocess/mod.rs @@ -232,6 +232,7 @@ where /// * `user_hooks` - the hooks run before and after the harness's execution /// * `harness_fn` - the harness, executing the function /// * `observers` - the observers observing the target during execution + /// /// This may return an error on unix, if signal handler setup fails pub fn with_timeout( harness_fn: &'a mut H, @@ -329,12 +330,13 @@ where }) } - /// Create a new in mem executor. + /// Create a new [`InProcessExecutor`]. /// Caution: crash and restart in one of them will lead to odd behavior if multiple are used, /// depending on different corpus or state. /// * `user_hooks` - the hooks run before and after the harness's execution /// * `harness_fn` - the harness, executing the function /// * `observers` - the observers observing the target during execution + /// /// This may return an error on unix, if signal handler setup fails pub fn with_timeout_generic( user_hooks: HT, diff --git a/libafl/src/executors/inprocess/stateful.rs b/libafl/src/executors/inprocess/stateful.rs index b84a7e0793..e18ece1351 100644 --- a/libafl/src/executors/inprocess/stateful.rs +++ b/libafl/src/executors/inprocess/stateful.rs @@ -227,6 +227,7 @@ where /// * `user_hooks` - the hooks run before and after the harness's execution /// * `harness_fn` - the harness, executing the function /// * `observers` - the observers observing the target during execution + /// /// This may return an error on unix, if signal handler setup fails pub fn with_timeout( harness_fn: &'a mut H, @@ -354,6 +355,7 @@ where /// * `user_hooks` - the hooks run before and after the harness's execution /// * `harness_fn` - the harness, executing the function /// * `observers` - the observers observing the target during execution + /// /// This may return an error on unix, if signal handler setup fails #[allow(clippy::too_many_arguments)] pub fn with_timeout_generic( diff --git a/libafl/src/observers/concolic/serialization_format.rs b/libafl/src/observers/concolic/serialization_format.rs index bac1063843..a72213a05e 100644 --- a/libafl/src/observers/concolic/serialization_format.rs +++ b/libafl/src/observers/concolic/serialization_format.rs @@ -1,35 +1,36 @@ //! # Concolic Tracing Serialization Format +//! //! ## Design Goals //! * The serialization format for concolic tracing was developed with the goal of being space and time efficient. //! * Additionally, it should be easy to maintain and extend. //! * It does not have to be compatible with other programming languages. //! * It should be resilient to crashes. Since we are fuzzing, we are expecting the traced program to crash at some -//! point. +//! point. //! //! The format as implemented fulfils these design goals. //! Specifically: //! * it requires only constant memory space for serialization, which allows for tracing complex and/or -//! long-running programs. +//! long-running programs. //! * the trace itself requires little space. A typical binary operation (such as an add) typically takes just 3 bytes. //! * it easy to encode. There is no translation between the interface of the runtime itself and the trace it generates. //! * it is similarly easy to decode and can be easily translated into an in-memory AST without overhead, because -//! expressions are decoded from leaf to root instead of root to leaf. +//! expressions are decoded from leaf to root instead of root to leaf. //! * At its core, it is just [`SymExpr`]s, which can be added to, modified and removed from with ease. The -//! definitions are automatically shared between the runtime and the consuming program, since both depend on the same -//! `LibAFL`. +//! definitions are automatically shared between the runtime and the consuming program, since both depend on the same +//! `LibAFL`. //! //! ## Techniques //! The serialization format applies multiple techniques to achieve its goals. //! * It uses bincode for efficient binary serialization. Crucially, bincode uses variable length integer encoding, -//! allowing it encode small integers use fewer bytes. +//! allowing it encode small integers use fewer bytes. //! * References to previous expressions are stored relative to the current expressions id. The vast majority of -//! expressions refer to other expressions that were defined close to their use. Therefore, encoding relative references -//! keeps references small. Therefore, they make optimal use of bincodes variable length integer encoding. +//! expressions refer to other expressions that were defined close to their use. Therefore, encoding relative references +//! keeps references small. Therefore, they make optimal use of bincodes variable length integer encoding. //! * Ids of expressions ([`SymExprRef`]s) are implicitly derived by their position in the message stream. Effectively, -//! a counter is used to identify expressions. +//! a counter is used to identify expressions. //! * The current length of the trace in bytes in serialized in a fixed format at the beginning of the trace. -//! This length is updated regularly when the trace is in a consistent state. This allows the reader to avoid reading -//! malformed data if the traced process crashed. +//! This length is updated regularly when the trace is in a consistent state. This allows the reader to avoid reading +//! malformed data if the traced process crashed. //! //! ## Example //! The expression `SymExpr::BoolAnd { a: SymExpr::True, b: SymExpr::False }` would be encoded as: diff --git a/libafl_bolts/src/build_id.rs b/libafl_bolts/src/build_id.rs index 33da263b43..d88762e280 100644 --- a/libafl_bolts/src/build_id.rs +++ b/libafl_bolts/src/build_id.rs @@ -22,13 +22,12 @@ static BUILD_ID: OnceLock = OnceLock::new(); /// invocations of identically laid out binaries. /// /// As such: -/// * It is guaranteed to be identical within multiple invocations of the same -/// binary. +/// * It is guaranteed to be identical within multiple invocations of the same binary. /// * It is guaranteed to be different across binaries with different code or -/// data segments or layout. +/// data segments or layout. /// * Equality is unspecified if the binaries have identical code and data -/// segments and layout but differ immaterially (e.g. if a timestamp is included -/// in the binary at compile time). +/// segments and layout but differ immaterially (e.g. if a timestamp is included +/// in the binary at compile time). /// /// # Examples /// diff --git a/libafl_bolts/src/shmem.rs b/libafl_bolts/src/shmem.rs index a14c3e29f7..091410a990 100644 --- a/libafl_bolts/src/shmem.rs +++ b/libafl_bolts/src/shmem.rs @@ -2,11 +2,9 @@ //! too.) #[cfg(feature = "alloc")] -use alloc::{rc::Rc, string::ToString}; +use alloc::{rc::Rc, string::ToString, vec::Vec}; #[cfg(feature = "alloc")] -use core::fmt::Display; -#[cfg(feature = "alloc")] -use core::{cell::RefCell, fmt, mem::ManuallyDrop}; +use core::{cell::RefCell, fmt, fmt::Display, mem::ManuallyDrop}; use core::{ fmt::Debug, mem, @@ -318,6 +316,72 @@ pub trait ShMemProvider: Clone + Default + Debug { } } +/// An [`ShMemProvider`] that does not provide any [`ShMem`]. +/// This is mainly for testing and type magic. +/// The resulting [`NopShMem`] is backed by a simple byte buffer to do some simple non-shared things with. +/// Calling [`NopShMemProvider::shmem_from_id_and_size`] will return new maps for the same id every time. +/// +/// # Note +/// If you just want a simple shared memory implementation, use [`StdShMemProvider`] instead. +#[cfg(feature = "alloc")] +#[derive(Debug, Clone, Default)] +pub struct NopShMemProvider; + +#[cfg(feature = "alloc")] +impl ShMemProvider for NopShMemProvider { + type ShMem = NopShMem; + + fn new() -> Result { + Ok(Self) + } + + fn new_shmem(&mut self, map_size: usize) -> Result { + self.shmem_from_id_and_size(ShMemId::default(), map_size) + } + + fn shmem_from_id_and_size( + &mut self, + id: ShMemId, + map_size: usize, + ) -> Result { + Ok(NopShMem { + id, + buf: vec![0; map_size], + }) + } +} + +/// An [`ShMem]`] that does not have any mem nor share anything. +#[cfg(feature = "alloc")] +#[derive(Debug, Clone, Default)] +pub struct NopShMem { + id: ShMemId, + buf: Vec, +} + +#[cfg(feature = "alloc")] +impl ShMem for NopShMem { + fn id(&self) -> ShMemId { + self.id + } +} + +#[cfg(feature = "alloc")] +impl DerefMut for NopShMem { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buf + } +} + +#[cfg(feature = "alloc")] +impl Deref for NopShMem { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.buf + } +} + /// A Handle Counted shared map, /// that can use internal mutability. /// Useful if the `ShMemProvider` needs to keep local state. diff --git a/libafl_frida/src/helper.rs b/libafl_frida/src/helper.rs index f32e85e487..a5292850da 100644 --- a/libafl_frida/src/helper.rs +++ b/libafl_frida/src/helper.rs @@ -189,7 +189,7 @@ impl FridaInstrumentationHelperBuilder { /// Instrument all modules in `/usr/lib` as well as `libfoo.so`: /// ``` ///# use libafl_frida::helper::FridaInstrumentationHelperBuilder; - /// let builder = FridaInstrumentationHelperBuilder::new() + /// let builder = FridaInstrumentationHelper::builder() /// .instrument_module_if(|module| module.name() == "libfoo.so") /// .instrument_module_if(|module| module.path().starts_with("/usr/lib")); /// ``` @@ -218,7 +218,7 @@ impl FridaInstrumentationHelperBuilder { /// /// ``` ///# use libafl_frida::helper::FridaInstrumentationHelperBuilder; - /// let builder = FridaInstrumentationHelperBuilder::new() + /// let builder = FridaInstrumentationHelper::builder() /// .instrument_module_if(|module| module.path().starts_with("/usr/lib")) /// .skip_module_if(|module| module.name() == "libfoo.so"); /// ``` diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/merge.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/merge.rs index 80f288821d..2f88a93025 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/merge.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/merge.rs @@ -236,6 +236,7 @@ pub fn merge( .scheduler_mut() .on_remove(&mut state, idx, &Some(testcase))?; } else { + #[allow(clippy::needless_borrows_for_generic_args)] // False-positive: file_path is used just below rename(&file_path, &new_file_path)?; *file_path = new_file_path; } diff --git a/libafl_nyx/src/executor.rs b/libafl_nyx/src/executor.rs index 218ac7aa75..2383e6090e 100644 --- a/libafl_nyx/src/executor.rs +++ b/libafl_nyx/src/executor.rs @@ -30,6 +30,13 @@ pub struct NyxExecutor { phantom: PhantomData, } +impl NyxExecutor<(), ()> { + /// Create a builder for [`NyxExeuctor`] + pub fn builder() -> NyxExecutorBuilder { + NyxExecutorBuilder::new() + } +} + impl UsesState for NyxExecutor where S: State, diff --git a/libafl_sugar/src/forkserver.rs b/libafl_sugar/src/forkserver.rs index c6a2eba63d..6560fe153f 100644 --- a/libafl_sugar/src/forkserver.rs +++ b/libafl_sugar/src/forkserver.rs @@ -5,7 +5,7 @@ use std::{fs, net::SocketAddr, path::PathBuf, time::Duration}; use libafl::{ corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig, EventRestarter, LlmpRestartingEventManager}, - executors::forkserver::ForkserverExecutorBuilder, + executors::forkserver::ForkserverExecutor, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -175,7 +175,7 @@ impl<'a> ForkserverBytesCoverageSugar<'a> { let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); let forkserver = if self.shmem_testcase { - ForkserverExecutorBuilder::new() + ForkserverExecutor::builder() .program(self.program.clone()) .parse_afl_cmdline(self.arguments) .is_persistent(true) @@ -186,7 +186,7 @@ impl<'a> ForkserverBytesCoverageSugar<'a> { .shmem_provider(&mut shmem_provider_client) .build_dynamic_map(edges_observer, tuple_list!(time_observer)) } else { - ForkserverExecutorBuilder::new() + ForkserverExecutor::builder() .program(self.program.clone()) .parse_afl_cmdline(self.arguments) .is_persistent(true) diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index d1ba79984c..674851d41e 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -200,6 +200,7 @@ fn main() { } } + #[cfg(any(feature = "forkserver", feature = "windows_asan"))] let target_family = std::env::var("CARGO_CFG_TARGET_FAMILY").unwrap(); #[cfg(feature = "forkserver")] diff --git a/libafl_tinyinst/src/executor.rs b/libafl_tinyinst/src/executor.rs index d2a63c3715..d293156329 100644 --- a/libafl_tinyinst/src/executor.rs +++ b/libafl_tinyinst/src/executor.rs @@ -1,5 +1,4 @@ -use core::marker::PhantomData; -use std::time::Duration; +use core::{marker::PhantomData, ptr, time::Duration}; use libafl::{ executors::{Executor, ExitKind, HasObservers}, @@ -10,19 +9,19 @@ use libafl::{ }; use libafl_bolts::{ fs::{InputFile, INPUTFILE_STD}, - shmem::{ShMem, ShMemProvider, StdShMemProvider}, + shmem::{NopShMemProvider, ShMem, ShMemProvider}, tuples::RefIndexable, AsSlice, AsSliceMut, }; use tinyinst::tinyinst::{litecov::RunResult, TinyInst}; -/// Tinyinst executor -pub struct TinyInstExecutor<'a, S, SP, OT> +/// [`TinyInst`](https://github.com/googleprojectzero/TinyInst) executor +pub struct TinyInstExecutor where SP: ShMemProvider, { tinyinst: TinyInst, - coverage: &'a mut Vec, + coverage_ptr: *mut Vec, timeout: Duration, observers: OT, phantom: PhantomData, @@ -30,7 +29,15 @@ where map: Option<::ShMem>, } -impl<'a, S, SP, OT> std::fmt::Debug for TinyInstExecutor<'a, S, SP, OT> +impl<'a> TinyInstExecutor<(), NopShMemProvider, ()> { + /// Create a builder for [`TinyInstExecutor`] + #[must_use] + pub fn builder() -> TinyInstExecutorBuilder<'a, NopShMemProvider> { + TinyInstExecutorBuilder::new() + } +} + +impl std::fmt::Debug for TinyInstExecutor where SP: ShMemProvider, { @@ -41,7 +48,7 @@ where } } -impl<'a, EM, S, SP, OT, Z> Executor for TinyInstExecutor<'a, S, SP, OT> +impl Executor for TinyInstExecutor where EM: UsesState, S: State + HasExecutions, @@ -80,7 +87,8 @@ where let mut status = RunResult::OK; unsafe { status = self.tinyinst.run(); - self.tinyinst.vec_coverage(self.coverage, false); + self.tinyinst + .vec_coverage(self.coverage_ptr.as_mut().unwrap(), false); } match status { @@ -100,30 +108,52 @@ pub struct TinyInstExecutorBuilder<'a, SP> { tinyinst_args: Vec, program_args: Vec, timeout: Duration, + coverage_ptr: *mut Vec, shmem_provider: Option<&'a mut SP>, } const MAX_FILE: usize = 1024 * 1024; const SHMEM_FUZZ_HDR_SIZE: usize = 4; -impl<'a> Default for TinyInstExecutorBuilder<'a, StdShMemProvider> { +impl<'a> Default for TinyInstExecutorBuilder<'a, NopShMemProvider> { fn default() -> Self { Self::new() } } -impl<'a> TinyInstExecutorBuilder<'a, StdShMemProvider> { +impl<'a> TinyInstExecutorBuilder<'a, NopShMemProvider> { /// Constructor #[must_use] - pub fn new() -> TinyInstExecutorBuilder<'a, StdShMemProvider> { + pub fn new() -> TinyInstExecutorBuilder<'a, NopShMemProvider> { Self { tinyinst_args: vec![], program_args: vec![], timeout: Duration::new(3, 0), shmem_provider: None, + coverage_ptr: ptr::null_mut(), } } + /// Use this to enable shmem testcase passing. + #[must_use] + pub fn shmem_provider( + self, + shmem_provider: &'a mut SP, + ) -> TinyInstExecutorBuilder<'a, SP> { + TinyInstExecutorBuilder { + tinyinst_args: self.tinyinst_args, + program_args: self.program_args, + timeout: self.timeout, + shmem_provider: Some(shmem_provider), + coverage_ptr: ptr::null_mut(), + } + } +} + +impl<'a, SP> TinyInstExecutorBuilder<'a, SP> +where + SP: ShMemProvider, +{ /// Argument for tinyinst instrumentation #[must_use] pub fn tinyinst_arg(mut self, arg: String) -> Self { @@ -207,31 +237,22 @@ impl<'a> TinyInstExecutorBuilder<'a, StdShMemProvider> { self } - /// Use this to enable shmem testcase passing. + /// Set the pointer to the coverage vec used to observer the execution. + /// + /// # Safety + /// The coverage vec pointer must point to a valid vec and outlive the time the [`TinyInstExecutor`] is alive. + /// The map will be dereferenced and borrowed mutably during execution. This may not happen concurrently. #[must_use] - pub fn shmem_provider( - self, - shmem_provider: &'a mut SP, - ) -> TinyInstExecutorBuilder<'a, SP> { - TinyInstExecutorBuilder { - tinyinst_args: self.tinyinst_args, - program_args: self.program_args, - timeout: self.timeout, - shmem_provider: Some(shmem_provider), - } + pub fn coverage_ptr(mut self, coverage_ptr: *mut Vec) -> Self { + self.coverage_ptr = coverage_ptr; + self } -} -impl<'a, SP> TinyInstExecutorBuilder<'a, SP> -where - SP: ShMemProvider, -{ - /// Build tinyinst executor - pub fn build( - &mut self, - coverage: &'a mut Vec, - observers: OT, - ) -> Result, Error> { + /// Build [`TinyInst`](https://github.com/googleprojectzero/TinyInst) executor + pub fn build(&mut self, observers: OT) -> Result, Error> { + if self.coverage_ptr.is_null() { + return Err(Error::illegal_argument("Coverage pointer may not be null.")); + } let (map, shmem_id) = match &mut self.shmem_provider { Some(provider) => { // setup shared memory @@ -285,7 +306,7 @@ where Ok(TinyInstExecutor { tinyinst, - coverage, + coverage_ptr: self.coverage_ptr, timeout: self.timeout, observers, phantom: PhantomData, @@ -295,7 +316,7 @@ where } } -impl<'a, S, SP, OT> HasObservers for TinyInstExecutor<'a, S, SP, OT> +impl HasObservers for TinyInstExecutor where S: State, SP: ShMemProvider, @@ -309,14 +330,14 @@ where RefIndexable::from(&mut self.observers) } } -impl<'a, S, SP, OT> UsesState for TinyInstExecutor<'a, S, SP, OT> +impl UsesState for TinyInstExecutor where S: State, SP: ShMemProvider, { type State = S; } -impl<'a, S, SP, OT> UsesObservers for TinyInstExecutor<'a, S, SP, OT> +impl UsesObservers for TinyInstExecutor where OT: ObserversTuple, S: State,