From a0c03fccc500942b9a1c2f69a1e281a5b662c2fa Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Fri, 4 Aug 2023 15:36:48 +0200 Subject: [PATCH] Add `serdeany_autoreg` feature flag to allow disabling `ctor` use (#1398) * Add feature flag to allow disabling use * fix typo * undo cargo.toml change * Fix no_std * Backticks * rename register_at_startup to create_register * fix * Move Tui_monitor to default instead of std --- libafl/Cargo.toml | 8 +++--- libafl/src/events/launcher.rs | 6 +++++ libafl/src/lib.rs | 3 --- libafl_bolts/Cargo.toml | 7 +++-- libafl_bolts/src/serdeany.rs | 48 ++++++++++++++++++++++++----------- 5 files changed, 49 insertions(+), 23 deletions(-) diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index fd03a1200a..ff5bdac128 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -12,8 +12,8 @@ edition = "2021" categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"] [features] -default = ["std", "derive", "llmp_compression", "llmp_small_maps", "llmp_broker_timeouts", "rand_trait", "fork", "prelude", "gzip", "regex"] -std = ["serde_json", "serde_json/std", "nix", "serde/std", "bincode", "wait-timeout", "uuid", "tui_monitor", "ctor", "backtrace", "serial_test", "libafl_bolts/std", "typed-builder"] # print, env, launcher ... support +default = ["std", "derive", "llmp_compression", "llmp_small_maps", "llmp_broker_timeouts", "rand_trait", "fork", "prelude", "gzip", "regex", "serdeany_autoreg", "tui_monitor"] +std = ["serde_json", "serde_json/std", "nix", "serde/std", "bincode", "wait-timeout", "uuid", "backtrace", "serial_test", "libafl_bolts/std", "typed-builder"] # print, env, launcher ... support derive = ["libafl_derive", "libafl_bolts/derive"] # provide derive(SerdeAny) macro. fork = ["libafl_bolts/derive"] # uses the fork() syscall to spawn children, instead of launching a new command, if supported by the OS (has no effect on Windows, no_std). rand_trait = ["libafl_bolts/rand_trait"] # If set, libafl's rand implementations will implement `rand::Rng` @@ -42,6 +42,9 @@ gpl = [] agpl = ["gpl", "nautilus"] nautilus = ["grammartec", "std", "serde_json/std"] +# SerdeAny features +serdeany_autoreg = ["libafl_bolts/serdeany_autoreg"] # Automatically register all `#[derive(SerdeAny)]` types at startup. + # LLMP features llmp_bind_public = ["libafl_bolts/llmp_bind_public"] # If set, llmp will bind to 0.0.0.0, allowing cross-device communication. Binds to localhost by default. llmp_compression = ["libafl_bolts/llmp_compression"] # llmp compression using GZip @@ -74,7 +77,6 @@ intervaltree = { version = "0.2.7", default-features = false, features = ["serde backtrace = {version = "0.3", optional = true} # Used to get the stacktrace in StacktraceObserver typed-builder = { version = "0.15.1", optional = true } # Implement the builder pattern at compiletime -ctor = { optional = true, version = "0.2" } serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } nix = { version = "0.26", optional = true } regex = { version = "1", optional = true } diff --git a/libafl/src/events/launcher.rs b/libafl/src/events/launcher.rs index 0414729b29..e04ddca520 100644 --- a/libafl/src/events/launcher.rs +++ b/libafl/src/events/launcher.rs @@ -169,6 +169,8 @@ where if self.cores.ids.iter().any(|&x| x == id.into()) { index += 1; self.shmem_provider.pre_fork()?; + // # Safety + // Fork is safe in general, apart from potential side effects to the OS and other threads match unsafe { fork() }? { ForkResult::Parent(child) => { self.shmem_provider.post_fork(false)?; @@ -177,6 +179,8 @@ where log::info!("child spawned and bound to core {id}"); } ForkResult::Child => { + // # Safety + // A call to `getpid` is safe. log::info!("{:?} PostFork", unsafe { libc::getpid() }); self.shmem_provider.post_fork(true)?; @@ -232,6 +236,8 @@ where // Broker exited. kill all clients. for handle in &handles { + // # Safety + // Normal libc call, no dereferences whatsoever unsafe { libc::kill(*handle, libc::SIGINT); } diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index 58ec83f096..ab169b770a 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -79,9 +79,6 @@ extern crate std; #[macro_use] #[doc(hidden)] pub extern crate alloc; -#[cfg(feature = "ctor")] -#[doc(hidden)] -pub use ctor::ctor; // Re-export derive(SerdeAny) #[cfg(feature = "derive")] diff --git a/libafl_bolts/Cargo.toml b/libafl_bolts/Cargo.toml index 8fb3b73100..3cb5ccce8b 100644 --- a/libafl_bolts/Cargo.toml +++ b/libafl_bolts/Cargo.toml @@ -12,8 +12,8 @@ edition = "2021" categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"] [features] -default = ["std", "derive", "llmp_compression", "llmp_small_maps", "rand_trait", "prelude", "gzip"] -std = ["serde_json", "serde_json/std", "hostname", "nix", "serde/std", "once_cell", "uuid", "ctor", "byteorder", "backtrace", "uds", "serial_test"] # print, env, launcher ... support +default = ["std", "derive", "llmp_compression", "llmp_small_maps", "rand_trait", "prelude", "gzip", "serdeany_autoreg"] +std = ["serde_json", "serde_json/std", "hostname", "nix", "serde/std", "once_cell", "uuid", "byteorder", "backtrace", "uds", "serial_test"] # print, env, launcher ... support derive = ["libafl_derive"] # provide derive(SerdeAny) macro. rand_trait = ["rand_core"] # If set, libafl's rand implementations will implement `rand::Rng` python = ["pyo3"] @@ -24,6 +24,9 @@ frida_cli = ["cli"] # Commandline flags for frida-based fuzzers errors_backtrace = ["backtrace"] gzip = ["miniz_oxide"] # Enables gzip compression in certain parts of the lib +# SerdeAny features +serdeany_autoreg = ["ctor"] # Automatically register all `#[derive(SerdeAny)]` types at startup. + # LLMP features llmp_bind_public = [] # If set, llmp will bind to 0.0.0.0, allowing cross-device communication. Binds to localhost by default. llmp_compression = ["gzip"] # llmp compression using GZip diff --git a/libafl_bolts/src/serdeany.rs b/libafl_bolts/src/serdeany.rs index d3d5b680f4..8050dd7a17 100644 --- a/libafl_bolts/src/serdeany.rs +++ b/libafl_bolts/src/serdeany.rs @@ -143,7 +143,7 @@ macro_rules! create_serde_registry_for_trait { finalized: false, }; - /// This shugar must be used to register all the structs which + /// This sugar must be used to register all the structs which /// have trait objects that can be serialized and deserialized in the program #[derive(Debug)] pub struct RegistryBuilder {} @@ -151,7 +151,11 @@ macro_rules! create_serde_registry_for_trait { #[allow(unused_qualifications)] impl RegistryBuilder { /// Register a given struct type for trait object (de)serialization - pub fn register() + /// + /// # Safety + /// This may never be called concurrently or at the same time as `finalize`. + /// It dereferences the `REGISTRY` hashmap and adds the given type to it. + pub unsafe fn register() where T: $trait_name + Serialize + serde::de::DeserializeOwned, { @@ -161,6 +165,10 @@ macro_rules! create_serde_registry_for_trait { } /// Finalize the registry, no more registrations are allowed after this call + /// + /// # Safety + /// This may never be called concurrently or at the same time as `register`. + /// It dereferences the `REGISTRY` hashmap and adds the given type to it. pub fn finalize() { unsafe { REGISTRY.finalize(); @@ -603,26 +611,36 @@ create_serde_registry_for_trait!(serdeany_registry, crate::serdeany::SerdeAny); pub use serdeany_registry::*; /// Register a `SerdeAny` type in the [`RegistryBuilder`] -#[cfg(feature = "std")] +/// +/// Do nothing for without the `serdeany_autoreg` feature, you'll have to register it manually +/// in `main()` with [`RegistryBuilder::register`] or using `::register()`. #[macro_export] -macro_rules! register_at_startup { +macro_rules! create_register { ($struct_type:ty) => { const _: () = { - #[$crate::ctor] - fn constructor() { + /// Manually register this type at a later point in time + /// + /// # Safety + /// This may never be called concurrently as it dereferences the `RegistryBuilder` without acquiring a lock. + #[cfg(not(feature = "serdeany_autoreg"))] + pub unsafe fn register() { $crate::serdeany::RegistryBuilder::register::<$struct_type>(); } + + /// Automatically register this type + #[cfg(feature = "serdeany_autoreg")] + #[$crate::ctor] + fn register() { + // # Safety + // This `register` call will always run at startup and never in parallel. + unsafe { + $crate::serdeany::RegistryBuilder::register::<$struct_type>(); + } + } }; }; } -/// Do nothing for `no_std`, you have to register it manually in `main()` with [`RegistryBuilder::register`] -#[cfg(not(feature = "std"))] -#[macro_export] -macro_rules! register_at_startup { - ($struct_type:ty) => {}; -} - /// Implement a [`SerdeAny`], registering it in the [`RegistryBuilder`] when on std #[macro_export] macro_rules! impl_serdeany { @@ -648,7 +666,7 @@ macro_rules! impl_serdeany { } $( - $crate::register_at_startup!($struct_name < $( $opt ),+ >); + $crate::create_register!($struct_name < $( $opt ),+ >); )* }; ($struct_name:ident) => @@ -672,6 +690,6 @@ macro_rules! impl_serdeany { } } - $crate::register_at_startup!($struct_name); + $crate::create_register!($struct_name); }; }