diff --git a/Cargo.toml b/Cargo.toml index 834eeedfc7c..6544d5598c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,57 +1,69 @@ [workspace] resolver = "2" members = [ - "crates/libafl", + "crates/build_id2", + "crates/core_affinity2", + "crates/exceptional", + "crates/fast_rands", "crates/libafl_asan", "crates/libafl_asan/libafl_asan_fuzz", "crates/libafl_asan/libafl_asan_libc", "crates/libafl_bolts", "crates/libafl_cc", - "crates/libafl_concolic/symcc_runtime", "crates/libafl_concolic/symcc_libafl", + "crates/libafl_concolic/symcc_runtime", + "crates/libafl_concolic/test/dump_constraints", + "crates/libafl_concolic/test/runtime_test", + "crates/libafl_core", "crates/libafl_derive", "crates/libafl_frida", "crates/libafl_intelpt", "crates/libafl_libfuzzer", "crates/libafl_nyx", - "crates/libafl_unicorn", - "crates/libafl_targets", - "crates/libafl_tinyinst", "crates/libafl_qemu", "crates/libafl_qemu/libafl_qemu_build", "crates/libafl_qemu/libafl_qemu_runner", "crates/libafl_qemu/libafl_qemu_sys", "crates/libafl_sugar", - "crates/libafl_concolic/test/dump_constraints", - "crates/libafl_concolic/test/runtime_test", + "crates/libafl_targets", + "crates/libafl_tinyinst", + "crates/libafl_unicorn", + "crates/libafl", + "crates/ll_mp", + "crates/minibsod", + "crates/no_std_time", + "crates/ownedref", + "crates/serde_anymap", + "crates/shmem_providers", + "crates/tuple_list_ex", "utils/build_and_test_fuzzers", + "utils/ci_runner", + "utils/ci_splitter", "utils/deexit", "utils/drcov_utils", "utils/gramatron/construct_automata", "utils/libafl_benches", "utils/libafl_jumper", - "utils/ci_runner", - "utils/ci_splitter", ] default-members = [ - "crates/libafl", "crates/libafl_bolts", "crates/libafl_cc", "crates/libafl_derive", "crates/libafl_targets", + "crates/libafl", ] exclude = [ "bindings/pylibafl", + "crates/libafl_libfuzzer_runtime", "docs", "fuzzers", - "crates/libafl_libfuzzer_runtime", - "utils/noaslr", + "scripts", "utils/gdb_qemu", "utils/libafl_repo_tools", "utils/multi_machine_generator", - "scripts", + "utils/noaslr", # additional crates "crates/libafl_concolic/test/symcc/util/symcc_fuzzing_helper", ] @@ -64,28 +76,44 @@ readme = "./README.md" [workspace.dependencies] # Internal deps +build_id2 = { path = "./crates/build_id2", version = "0.15.3", default-features = false } +core_affinity2 = { path = "./crates/core_affinity2", version = "0.15.3", default-features = false } +exceptional = { path = "./crates/exceptional", version = "0.15.3", default-features = false } +fast_rands = { path = "./crates/fast_rands", version = "0.15.3", default-features = false } libafl = { path = "./crates/libafl", version = "0.15.3", default-features = false } libafl_bolts = { path = "./crates/libafl_bolts", version = "0.15.3", default-features = false } libafl_cc = { path = "./crates/libafl_cc", version = "0.15.3", default-features = false } -symcc_runtime = { path = "./crates/libafl_concolic/symcc_runtime", version = "0.15.2", default-features = false } -symcc_libafl = { path = "./crates/libafl_concolic/symcc_libafl", version = "0.15.3", default-features = false } +libafl_core = { path = "./crates/libafl_core", version = "0.15.3", default-features = false } libafl_derive = { path = "./crates/libafl_derive", version = "0.15.3", default-features = false } libafl_frida = { path = "./crates/libafl_frida", version = "0.15.3", default-features = false } libafl_intelpt = { path = "./crates/libafl_intelpt", version = "0.15.3", default-features = false } libafl_libfuzzer = { path = "./crates/libafl_libfuzzer", version = "0.15.3", default-features = false } libafl_nyx = { path = "./crates/libafl_nyx", version = "0.15.3", default-features = false } -libafl_targets = { path = "./crates/libafl_targets", version = "0.15.3", default-features = false } -libafl_tinyinst = { path = "./crates/libafl_tinyinst", version = "0.15.3", default-features = false } libafl_qemu = { path = "./crates/libafl_qemu", version = "0.15.3", default-features = false } libafl_qemu_build = { path = "./crates/libafl_qemu/libafl_qemu_build", version = "0.15.3", default-features = false } libafl_qemu_sys = { path = "./crates/libafl_qemu/libafl_qemu_sys", version = "0.15.3", default-features = false } libafl_sugar = { path = "./crates/libafl_sugar", version = "0.15.3", default-features = false } +libafl_targets = { path = "./crates/libafl_targets", version = "0.15.3", default-features = false } +libafl_tinyinst = { path = "./crates/libafl_tinyinst", version = "0.15.3", default-features = false } +ll_mp = { path = "./crates/ll_mp", version = "0.15.3", default-features = false } +minibsod = { path = "./crates/minibsod", version = "0.15.3", default-features = false } +no_std_time = { path = "./crates/no_std_time", version = "0.15.3", default-features = false } +ownedref = { path = "./crates/ownedref", version = "0.15.3", default-features = false } +serde_anymap = { path = "./crates/serde_anymap", version = "0.15.3", default-features = false } +shmem_providers = { path = "./crates/shmem_providers", version = "0.15.3", default-features = false } +tuple_list_ex = { path = "./crates/tuple_list_ex", version = "0.15.2", default-features = false } + +# Concolic fuzzing crates dump_constraints = { path = "./crates/libafl_concolic/test/dump_constraints", version = "0.15.2", default-features = false } runtime_test = { path = "./crates/libafl_concolic/test/runtime_test", version = "0.15.2", default-features = false } +symcc_libafl = { path = "./crates/libafl_concolic/symcc_libafl", version = "0.15.3", default-features = false } +symcc_runtime = { path = "./crates/libafl_concolic/symcc_runtime", version = "0.15.2", default-features = false } + +# Utils build_and_test_fuzzers = { path = "./utils/build_and_test_fuzzers", version = "0.15.2", default-features = false } +construct_automata = { path = "./utils/gramatron/construct_automata", version = "0.15.2", default-features = false } deexit = { path = "./utils/deexit", version = "0.15.2", default-features = false } drcov_utils = { path = "./utils/drcov_utils", version = "0.15.2", default-features = false } -construct_automata = { path = "./utils/gramatron/construct_automata", version = "0.15.2", default-features = false } libafl_benches = { path = "./utils/libafl_benches", version = "0.15.3", default-features = false } libafl_jumper = { path = "./utils/libafl_jumper", version = "0.15.3", default-features = false } @@ -98,11 +126,14 @@ bindgen = "0.72.0" # fixme: Change this to 1.3.3 when the issue https://github.com/danlehmann/bitfield/issues/66 is resolved. bitbybit = "=1.3.2" # bitfields, use this for bit fields and bit enums capstone = "0.13.0" # Disassembler used in libafl_unicorn to provide disassembly on crash -clap = "4.5.18" cc = "1.1.21" +clap = "4.5.18" cmake = "0.1.51" +ctor = "0.4.0" document-features = "0.2.10" +erased-serde = { version = "0.4.5", default-features = false } # erased serde fastbloom = { version = "0.12.0", default-features = false } +fs2 = "0.4.3" # Used by OnDisk Corpus for file locking hashbrown = { version = "0.14.5", default-features = false } # A faster hashmap, nostd compatible just = "=1.40.0" libc = "0.2.159" # For (*nix) libc @@ -123,13 +154,14 @@ rangemap = "1.5.1" regex = "1.10.6" rustversion = "1.0.17" serde = { version = "1.0.210", default-features = false } # serialization lib -serial_test = { version = "3.2.0", default-features = false } serde_json = { version = "1.0.128", default-features = false } serde_yaml = { version = "0.9.34" } # For parsing the injections yaml file +serial_test = { version = "3.2.0", default-features = false } static_assertions = "1.1.0" strum = "0.27.0" strum_macros = "0.27.0" toml = "0.9.0" # For parsing the injections toml file +tuple_list = { version = "0.1.3" } typed-builder = "0.21.0" # Implement the builder pattern at compiletime typeid = "1.0.0" # Safe type_eq that doesn't rely on std specialization unicorn-engine = "2.0.1" # Used in libafl_unicorn @@ -137,7 +169,6 @@ uuid = { version = "1.10.0", features = ["serde", "v4"] } which = "8.0.0" windows = "0.59.0" z3 = "0.12.1" -fs2 = "0.4.3" # Used by OnDisk Corpus for file locking [workspace.lints.rust] # Deny @@ -165,21 +196,21 @@ std_instead_of_core = "deny" cargo = { level = "warn", priority = -1 } # Allow -negative_feature_names = "allow" # TODO: turn into 'warn' when working -multiple_crate_versions = "allow" # TODO: turn into `warn` when working -unreadable_literal = "allow" -type_repetition_in_bounds = "allow" -missing_errors_doc = "allow" cast_possible_truncation = "allow" -used_underscore_binding = "allow" -ptr_as_ptr = "allow" +comparison_chain = "allow" # This lint makes **ZERO** sense +missing_errors_doc = "allow" missing_panics_doc = "allow" module_name_repetitions = "allow" -unsafe_derive_deserialize = "allow" +multiple_crate_versions = "allow" # TODO: turn into `warn` when working +negative_feature_names = "allow" # TODO: turn into 'warn' when working +ptr_as_ptr = "allow" similar_names = "allow" -too_many_lines = "allow" -comparison_chain = "allow" # This lint makes **ZERO** sense struct_field_names = "allow" # ???? +too_many_lines = "allow" +type_repetition_in_bounds = "allow" +unreadable_literal = "allow" +unsafe_derive_deserialize = "allow" +used_underscore_binding = "allow" [workspace.lints.rustdoc] # Deny diff --git a/crates/LICENSE-APACHE b/crates/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/LICENSE-MIT b/crates/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/build_id2/Cargo.toml b/crates/build_id2/Cargo.toml new file mode 100644 index 00000000000..5acfa2c2582 --- /dev/null +++ b/crates/build_id2/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "build_id2" +version.workspace = true +authors = [ + "Andrea Fioraldi ", + "Dominik Maier ", +] +description = "Updated and maintained build id library" +documentation = "https://docs.rs/libafl" +repository = "https://github.com/AFLplusplus/LibAFL/" +readme = "./README.md" +license = "MIT OR Apache-2.0" +keywords = ["os", "build-id"] +edition = "2024" +rust-version = "1.87" +categories = ["os"] + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +[features] +default = [] + +[build-dependencies] +rustversion = { workspace = true } + +[dev-dependencies] + +[dependencies] +uuid = { workspace = true } +ahash = { workspace = true } + +[lints] +workspace = true diff --git a/crates/build_id2/LICENSE-APACHE b/crates/build_id2/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/build_id2/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/build_id2/LICENSE-MIT b/crates/build_id2/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/build_id2/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/build_id2/README.md b/crates/build_id2/README.md new file mode 100644 index 00000000000..0a638ac1244 --- /dev/null +++ b/crates/build_id2/README.md @@ -0,0 +1,58 @@ +# LibAFL_bolts: OS and Fuzzer Dev's Libary Collection. + + LibAFL logo + +The `libafl_bolts` crate exposes a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just fuzzers completely different to LibAFL. +Some cross-platform things in bolts include (but are not limited to): + +* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable +* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation +* LLMP: A fast, lock-free IPC mechanism via SharedMap +* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores +* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/)) +* MiniBSOD: get and print information about the current process state including important registers. +* Tuples: Haskel-like compile-time tuple lists +* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork` + +LibAFL_bolts is written and maintained by + +* [Andrea Fioraldi](https://twitter.com/andreafioraldi) +* [Dominik Maier](https://twitter.com/domenuk) +* [s1341](https://twitter.com/srubenst1341) +* [Dongjia Zhang](https://github.com/tokatoka) +* [Addison Crump](https://github.com/addisoncrump) + +## Contributing + +For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 + +Even though we will gladly assist you in finishing up your PR, try to +- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26)) +- run `cargo nightly fmt` on your code before pushing +- check the output of `cargo clippy --all` or `./clippy.sh` +- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code. + +Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. + +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + + +
+ + +Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled +using the respective feature in each crate when it is present, such as the +'agpl' feature of the libafl crate. + diff --git a/crates/libafl_bolts/src/build_id.rs b/crates/build_id2/src/lib.rs similarity index 70% rename from crates/libafl_bolts/src/build_id.rs rename to crates/build_id2/src/lib.rs index 876ac1a6610..903db20603b 100644 --- a/crates/libafl_bolts/src/build_id.rs +++ b/crates/build_id2/src/lib.rs @@ -1,16 +1,59 @@ //! Based on //! (C) Alec Mocatta under license MIT or Apache 2 +//! +//! Maintained by the `LibAFL` team. +#![doc = include_str!("../../../README.md")] +/*! */ +#![cfg_attr(not(test), warn( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr(test, deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results +))] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] use core::{ any::TypeId, hash::{Hash, Hasher}, }; -use std::{env, fs::File, io, sync::OnceLock}; +use std::{env, fs::File, hash::BuildHasher, io, sync::OnceLock}; +use ahash::RandomState; use uuid::Uuid; -use crate::hasher_std; - static BUILD_ID: OnceLock = OnceLock::new(); /// Returns a [Uuid] uniquely representing the build of the current binary. @@ -29,8 +72,8 @@ static BUILD_ID: OnceLock = OnceLock::new(); /// # Examples /// /// ``` -/// # let remote_build_id = libafl_bolts::build_id::get(); -/// let local_build_id = libafl_bolts::build_id::get(); +/// # let remote_build_id = build_id2::get(); +/// let local_build_id = build_id2::get(); /// if local_build_id == remote_build_id { /// println!("We're running the same binary as remote!"); /// } else { @@ -79,7 +122,7 @@ fn from_type_id(mut hasher: H) -> H { } fn calculate() -> Uuid { - let hasher = hasher_std(); + let hasher = RandomState::with_seeds(0, 0, 0, 0).build_hasher(); let hasher = from_exe(hasher.clone()).unwrap_or(hasher); let mut hasher = from_type_id(hasher); diff --git a/crates/core_affinity2/Cargo.toml b/crates/core_affinity2/Cargo.toml new file mode 100644 index 00000000000..d6c657a37bb --- /dev/null +++ b/crates/core_affinity2/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "core_affinity2" +version.workspace = true +authors = [ + "Andrea Fioraldi ", + "Dominik Maier ", +] +description = "Core Affinity crate to bind to cores, cross platform" +documentation = "https://docs.rs/libafl" +repository = "https://github.com/AFLplusplus/LibAFL/" +readme = "./README.md" +license = "MIT OR Apache-2.0" +keywords = ["os", "core-affinity", "no-std"] +edition = "2024" +rust-version = "1.87" +categories = ["embedded", "os", "no-std"] + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +[features] +document-features = ["dep:document-features"] + +[build-dependencies] +rustversion = { workspace = true } + +[dev-dependencies] + +[dependencies] +# Document all features of this crate (for `cargo doc`) +document-features = { workspace = true, optional = true } +serde = { workspace = true, features = ["alloc", "derive"] } +libafl_core = { workspace = true, features = ["alloc", "std"] } +libc = { workspace = true } + +[lints] +workspace = true diff --git a/crates/core_affinity2/LICENSE-APACHE b/crates/core_affinity2/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/core_affinity2/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/core_affinity2/LICENSE-MIT b/crates/core_affinity2/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/core_affinity2/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/core_affinity2/README.md b/crates/core_affinity2/README.md new file mode 100644 index 00000000000..0a638ac1244 --- /dev/null +++ b/crates/core_affinity2/README.md @@ -0,0 +1,58 @@ +# LibAFL_bolts: OS and Fuzzer Dev's Libary Collection. + + LibAFL logo + +The `libafl_bolts` crate exposes a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just fuzzers completely different to LibAFL. +Some cross-platform things in bolts include (but are not limited to): + +* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable +* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation +* LLMP: A fast, lock-free IPC mechanism via SharedMap +* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores +* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/)) +* MiniBSOD: get and print information about the current process state including important registers. +* Tuples: Haskel-like compile-time tuple lists +* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork` + +LibAFL_bolts is written and maintained by + +* [Andrea Fioraldi](https://twitter.com/andreafioraldi) +* [Dominik Maier](https://twitter.com/domenuk) +* [s1341](https://twitter.com/srubenst1341) +* [Dongjia Zhang](https://github.com/tokatoka) +* [Addison Crump](https://github.com/addisoncrump) + +## Contributing + +For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 + +Even though we will gladly assist you in finishing up your PR, try to +- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26)) +- run `cargo nightly fmt` on your code before pushing +- check the output of `cargo clippy --all` or `./clippy.sh` +- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code. + +Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. + +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + + +
+ + +Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled +using the respective feature in each crate when it is present, such as the +'agpl' feature of the libafl crate. + diff --git a/crates/libafl_bolts/src/core_affinity.rs b/crates/core_affinity2/src/lib.rs similarity index 94% rename from crates/libafl_bolts/src/core_affinity.rs rename to crates/core_affinity2/src/lib.rs index ae8bad52173..4b5109fa8f8 100644 --- a/crates/libafl_bolts/src/core_affinity.rs +++ b/crates/core_affinity2/src/lib.rs @@ -6,11 +6,11 @@ //! //! ```rust //! # use std::thread; -//! use libafl_bolts::core_affinity; +//! use core_affinity2; //! //! // Retrieve the IDs of all active CPU cores. //! # #[cfg(not(miri))] -//! let core_ids = core_affinity::get_core_ids().unwrap(); +//! let core_ids = core_affinity2::get_core_ids().unwrap(); //! //! // Create a thread for each active CPU core. //! # #[cfg(not(miri))] @@ -32,16 +32,64 @@ //! ``` //! //! *This file is a fork of * +#![doc = include_str!("../../../README.md")] +/*! */ +#![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +#![no_std] +#![cfg_attr(not(test), warn( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr(test, deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results +))] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] + +#[macro_use] +extern crate std; +#[doc(hidden)] +pub extern crate alloc; use alloc::{ string::{String, ToString}, vec::Vec, }; +use libafl_core::Error; use serde::{Deserialize, Serialize}; -use crate::Error; - /// This function tries to retrieve information /// on all the "cores" active on this system. pub fn get_core_ids() -> Result, Error> { @@ -97,7 +145,6 @@ pub struct Cores { pub ids: Vec, } -#[cfg(feature = "std")] impl Cores { /// Pick all cores pub fn all() -> Result { @@ -196,7 +243,6 @@ impl From> for Cores { } } -#[cfg(feature = "std")] impl TryFrom<&str> for Cores { type Error = Error; fn try_from(cores: &str) -> Result { @@ -249,8 +295,9 @@ mod linux { #[cfg(target_os = "dragonfly")] const CPU_SETSIZE: libc::c_int = 256; + use libafl_core::Error; + use super::CoreId; - use crate::Error; #[allow(trivial_numeric_casts)] pub fn get_core_ids() -> Result, Error> { @@ -380,7 +427,7 @@ mod haiku { use alloc::vec::Vec; use std::thread::available_parallelism; - use crate::core_affinity::{CoreId, Error}; + use crate::{CoreId, Error}; #[expect(clippy::unnecessary_wraps)] pub fn get_core_ids() -> Result, Error> { @@ -413,7 +460,7 @@ mod windows { Threading::{GetCurrentThread, SetThreadGroupAffinity}, }; - use crate::core_affinity::{CoreId, Error}; + use crate::{CoreId, Error}; pub fn get_core_ids() -> Result, Error> { let mut core_ids: Vec = Vec::new(); @@ -594,6 +641,7 @@ mod apple { use alloc::vec::Vec; use std::thread::available_parallelism; + use libafl_core::Error; #[cfg(target_arch = "x86_64")] use libc::{ KERN_SUCCESS, THREAD_AFFINITY_POLICY, THREAD_AFFINITY_POLICY_COUNT, integer_t, @@ -604,7 +652,6 @@ mod apple { use libc::{pthread_set_qos_class_self_np, qos_class_t::QOS_CLASS_USER_INITIATED}; use super::CoreId; - use crate::Error; #[cfg(target_arch = "x86_64")] #[repr(C)] @@ -691,13 +738,13 @@ mod netbsd { use alloc::vec::Vec; use std::thread::available_parallelism; + use libafl_core::Error; use libc::{ _cpuset, _cpuset_create, _cpuset_destroy, _cpuset_set, _cpuset_size, pthread_self, pthread_setaffinity_np, }; use super::CoreId; - use crate::Error; #[expect(trivial_numeric_casts)] pub fn get_core_ids() -> Result, Error> { @@ -753,8 +800,9 @@ mod openbsd { use alloc::vec::Vec; use std::thread::available_parallelism; + use libafl_core::Error; + use super::CoreId; - use crate::Error; #[expect(trivial_numeric_casts)] pub fn get_core_ids() -> Result, Error> { @@ -781,8 +829,9 @@ mod solaris { use alloc::vec::Vec; use std::thread::available_parallelism; + use libafl_core::Error; + use super::CoreId; - use crate::Error; #[expect(clippy::unnecessary_wraps)] pub fn get_core_ids() -> Result, Error> { diff --git a/crates/exceptional/Cargo.toml b/crates/exceptional/Cargo.toml new file mode 100644 index 00000000000..74ff78b3b20 --- /dev/null +++ b/crates/exceptional/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "exceptional" +version.workspace = true +authors = [ + "Andrea Fioraldi ", + "Dominik Maier ", +] +description = "Everything for your exception and signal handling needs" +documentation = "https://docs.rs/libafl" +repository = "https://github.com/AFLplusplus/LibAFL/" +readme = "./README.md" +license = "MIT OR Apache-2.0" +keywords = ["os", "shmem", "no-std"] +edition = "2024" +rust-version = "1.87" +categories = ["embedded", "os", "no-std"] + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +[features] +default = ["std", "alloc"] +document-features = ["dep:document-features"] + +#! # Feature Flags +#! ### General Features + +## Enables features that need rust's `std` lib to work, like print, env, ... support +std = ["nix", "libafl_core/std"] + +## Enables all features that allocate in `no_std` +alloc = ["libafl_core/alloc"] + +[build-dependencies] +rustversion = { workspace = true } + +[dev-dependencies] + +[dependencies] +# Document all features of this crate (for `cargo doc`) +document-features = { workspace = true, optional = true } +libafl_core = { workspace = true } +nix = { workspace = true, optional = true, default-features = false, features = [ + "fs", + "signal", + "socket", + "poll", +] } +num_enum = { workspace = true, default-features = false } +[target.'cfg(unix)'.dependencies] +libc = { workspace = true } + +[lints] +workspace = true diff --git a/crates/exceptional/LICENSE-APACHE b/crates/exceptional/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/exceptional/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/exceptional/LICENSE-MIT b/crates/exceptional/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/exceptional/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/exceptional/README.md b/crates/exceptional/README.md new file mode 100644 index 00000000000..0a638ac1244 --- /dev/null +++ b/crates/exceptional/README.md @@ -0,0 +1,58 @@ +# LibAFL_bolts: OS and Fuzzer Dev's Libary Collection. + + LibAFL logo + +The `libafl_bolts` crate exposes a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just fuzzers completely different to LibAFL. +Some cross-platform things in bolts include (but are not limited to): + +* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable +* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation +* LLMP: A fast, lock-free IPC mechanism via SharedMap +* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores +* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/)) +* MiniBSOD: get and print information about the current process state including important registers. +* Tuples: Haskel-like compile-time tuple lists +* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork` + +LibAFL_bolts is written and maintained by + +* [Andrea Fioraldi](https://twitter.com/andreafioraldi) +* [Dominik Maier](https://twitter.com/domenuk) +* [s1341](https://twitter.com/srubenst1341) +* [Dongjia Zhang](https://github.com/tokatoka) +* [Addison Crump](https://github.com/addisoncrump) + +## Contributing + +For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 + +Even though we will gladly assist you in finishing up your PR, try to +- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26)) +- run `cargo nightly fmt` on your code before pushing +- check the output of `cargo clippy --all` or `./clippy.sh` +- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code. + +Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. + +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + + +
+ + +Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled +using the respective feature in each crate when it is present, such as the +'agpl' feature of the libafl crate. + diff --git a/crates/exceptional/src/lib.rs b/crates/exceptional/src/lib.rs new file mode 100644 index 00000000000..42200d19c6e --- /dev/null +++ b/crates/exceptional/src/lib.rs @@ -0,0 +1,60 @@ +/*! + * Everything for your exception and signal handling needs + */ +#![doc = include_str!("../../../README.md")] +/*! */ +#![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +#![no_std] +#![cfg_attr(not(test), warn( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr(test, deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results +))] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] + +#[cfg(feature = "std")] +#[macro_use] +extern crate std; +#[cfg(feature = "alloc")] +#[doc(hidden)] +pub extern crate alloc; + +pub mod unix_signals; + +#[cfg(all(windows, feature = "std"))] +#[expect(missing_docs, overflowing_literals)] +pub mod windows_exceptions; diff --git a/crates/libafl_bolts/src/os/unix_signals.rs b/crates/exceptional/src/unix_signals.rs similarity index 99% rename from crates/libafl_bolts/src/os/unix_signals.rs rename to crates/exceptional/src/unix_signals.rs index a2ed3ee2ba4..855f8471afb 100644 --- a/crates/libafl_bolts/src/os/unix_signals.rs +++ b/crates/exceptional/src/unix_signals.rs @@ -16,6 +16,7 @@ use core::{ mem, }; +use libafl_core::format; /// armv7 `libc` does not feature a `uncontext_t` implementation #[cfg(target_arch = "arm")] pub use libc::c_ulong; @@ -246,6 +247,7 @@ pub struct ucontext_t { pub mcontext_data: mcontext64, } +use libafl_core::Error; #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] use libc::ssize_t; #[cfg(not(any( @@ -265,8 +267,6 @@ use libc::{ pub use libc::{c_void, siginfo_t}; use num_enum::{IntoPrimitive, TryFromPrimitive}; -use crate::Error; - unsafe extern "C" { /// The `libc` `getcontext` /// For some reason, it's not available on `MacOS`. diff --git a/crates/libafl_bolts/src/os/windows_exceptions.rs b/crates/exceptional/src/windows_exceptions.rs similarity index 98% rename from crates/libafl_bolts/src/os/windows_exceptions.rs rename to crates/exceptional/src/windows_exceptions.rs index 68f1ee7865f..7be6e1102f6 100644 --- a/crates/libafl_bolts/src/os/windows_exceptions.rs +++ b/crates/exceptional/src/windows_exceptions.rs @@ -27,16 +27,15 @@ use crate::Error; /// The special exit code when the target exited through ctrl-c pub const CTRL_C_EXIT: i32 = -1073741510; -// For VEH +/// For VEH const EXCEPTION_CONTINUE_EXECUTION: c_long = -1; -// For VEH +/// For VEH const EXCEPTION_CONTINUE_SEARCH: c_long = 0; // For SEH // const EXCEPTION_EXECUTE_HANDLER: c_long = 1; -// From https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers/crt/signal.h pub const SIGINT: i32 = 2; pub const SIGILL: i32 = 4; pub const SIGABRT_COMPAT: i32 = 6; @@ -47,7 +46,6 @@ pub const SIGBREAK: i32 = 21; pub const SIGABRT: i32 = 22; pub const SIGABRT2: i32 = 22; -// From https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L611 pub const STATUS_WAIT_0: i32 = 0x00000000; pub const STATUS_ABANDONED_WAIT_0: i32 = 0x00000080; pub const STATUS_USER_APC: i32 = 0x000000C0; @@ -96,7 +94,6 @@ pub const STATUS_SXS_EARLY_DEACTIVATION: i32 = 0xC015000F; pub const STATUS_SXS_INVALID_DEACTIVATION: i32 = 0xC0150010; pub const STATUS_NOT_IMPLEMENTED: i32 = 0xC0000002; -// from https://github.com/x64dbg/x64dbg/blob/4d631707b89d97e199844c08f5b65d8ea5d5d3f3/bin/exceptiondb.txt pub const STATUS_WX86_UNSIMULATE: i32 = 0x4000001C; pub const STATUS_WX86_CONTINUE: i32 = 0x4000001D; pub const STATUS_WX86_SINGLE_STEP: i32 = 0x4000001E; @@ -132,7 +129,6 @@ pub const VCPP_EXCEPTION_ERROR_PROC_NOT_FOUND: i32 = 0xC06D007F; #[derive(Debug, FromPrimitive, Copy, Clone)] #[repr(i32)] pub enum ExceptionCode { - // From https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L611 WaitZero = STATUS_WAIT_0, AbandonedWaitZero = STATUS_ABANDONED_WAIT_0, UserApc = STATUS_USER_APC, @@ -180,7 +176,7 @@ pub enum ExceptionCode { SxsEarlyDeactivation = STATUS_SXS_EARLY_DEACTIVATION, SxsInvalidDeactivation = STATUS_SXS_INVALID_DEACTIVATION, NotImplemented = STATUS_NOT_IMPLEMENTED, - // from https://github.com/x64dbg/x64dbg/blob/4d631707b89d97e199844c08f5b65d8ea5d5d3f3/bin/exceptiondb.txt + Wx86Unsimulate = STATUS_WX86_UNSIMULATE, Wx86Continue = STATUS_WX86_CONTINUE, Wx86SingleStep = STATUS_WX86_SINGLE_STEP, @@ -534,6 +530,7 @@ unsafe extern "C" fn handle_signal(_signum: i32) { } /// Setup Win32 exception handlers in a somewhat rusty way. +/// /// # Safety /// Exception handlers are usually ugly, handle with care! #[cfg(feature = "alloc")] @@ -602,6 +599,7 @@ struct CtrlHandlerHolder { static mut CTRL_HANDLER: Option = None; /// Set `ConsoleCtrlHandler` to catch Ctrl-C +/// /// # Safety /// Same safety considerations as in `setup_exception_handler` pub(crate) unsafe fn setup_ctrl_handler( diff --git a/crates/fast_rands/Cargo.toml b/crates/fast_rands/Cargo.toml new file mode 100644 index 00000000000..d67c2c4398d --- /dev/null +++ b/crates/fast_rands/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "fast_rands" +version.workspace = true +authors = [ + "Andrea Fioraldi ", + "Dominik Maier ", +] +description = "Non-cryptographically, but quite fast, RNG implementations" +documentation = "https://docs.rs/libafl" +repository = "https://github.com/AFLplusplus/LibAFL/" +readme = "./README.md" +license = "MIT OR Apache-2.0" +keywords = ["os", "shmem", "no-std"] +edition = "2024" +rust-version = "1.87" +categories = ["embedded", "os", "no-std"] + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +[features] +default = ["std", "alloc"] +document-features = ["dep:document-features"] + +#! # Feature Flags +#! ### General Features + +## Enables features that need rust's `std` lib to work, like print, env, ... support +std = ["alloc"] + +## Enables all features that allocate in `no_std` +alloc = [] + +## If set, libafl_rand's `rand` implementations will implement `rand_core::CoreRng` +## and, inversely, all seedable `rand_core::RngCore` types can be used as Rng for LibAFL. +rand_trait = ["rand_core"] + +## Enable python support for Rand +python = ["pyo3"] + +[build-dependencies] +rustversion = { workspace = true } + +[dev-dependencies] + +[dependencies] +serde = { workspace = true, default-features = false, features = [ + "derive", +] } # serialization lib +# Document all features of this crate (for `cargo doc`) +document-features = { workspace = true, optional = true } +rand_core = { version = "0.9.0", optional = true } +pyo3 = { workspace = true, optional = true, features = ["serde", "macros"] } + +[lints] +workspace = true diff --git a/crates/fast_rands/LICENSE-APACHE b/crates/fast_rands/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/fast_rands/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/fast_rands/LICENSE-MIT b/crates/fast_rands/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/fast_rands/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/fast_rands/README.md b/crates/fast_rands/README.md new file mode 100644 index 00000000000..0a638ac1244 --- /dev/null +++ b/crates/fast_rands/README.md @@ -0,0 +1,58 @@ +# LibAFL_bolts: OS and Fuzzer Dev's Libary Collection. + + LibAFL logo + +The `libafl_bolts` crate exposes a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just fuzzers completely different to LibAFL. +Some cross-platform things in bolts include (but are not limited to): + +* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable +* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation +* LLMP: A fast, lock-free IPC mechanism via SharedMap +* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores +* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/)) +* MiniBSOD: get and print information about the current process state including important registers. +* Tuples: Haskel-like compile-time tuple lists +* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork` + +LibAFL_bolts is written and maintained by + +* [Andrea Fioraldi](https://twitter.com/andreafioraldi) +* [Dominik Maier](https://twitter.com/domenuk) +* [s1341](https://twitter.com/srubenst1341) +* [Dongjia Zhang](https://github.com/tokatoka) +* [Addison Crump](https://github.com/addisoncrump) + +## Contributing + +For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 + +Even though we will gladly assist you in finishing up your PR, try to +- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26)) +- run `cargo nightly fmt` on your code before pushing +- check the output of `cargo clippy --all` or `./clippy.sh` +- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code. + +Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. + +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + + +
+ + +Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled +using the respective feature in each crate when it is present, such as the +'agpl' feature of the libafl crate. + diff --git a/crates/libafl_bolts/src/rands/mod.rs b/crates/fast_rands/src/lib.rs similarity index 88% rename from crates/libafl_bolts/src/rands/mod.rs rename to crates/fast_rands/src/lib.rs index 14b5c50ce9c..1f2113fa465 100644 --- a/crates/libafl_bolts/src/rands/mod.rs +++ b/crates/fast_rands/src/lib.rs @@ -1,4 +1,54 @@ -//! The random number generators of `LibAFL` +//! The fast random number generators of `LibAFL` +#![doc = include_str!("../../../README.md")] +/*! */ +#![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +#![no_std] +#![cfg_attr(not(test), warn( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr(test, deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results +))] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] + +#[cfg(feature = "std")] +#[macro_use] +extern crate std; +#[doc(hidden)] +pub extern crate alloc; #[cfg(all(not(feature = "std"), target_has_atomic = "ptr"))] use core::sync::atomic::{AtomicUsize, Ordering}; @@ -559,6 +609,41 @@ impl XkcdRand { #[cfg(feature = "python")] /// `Rand` Python bindings pub mod pybind { + + /// Unwrap the mutable body of this wrapper + #[macro_export] + macro_rules! unwrap_me_mut_body { + ($wrapper:expr, $name:ident, $body:block, $wrapper_type:ident, { $($wrapper_option:tt),*}) => { + match &mut $wrapper { + $( + $wrapper_type::$wrapper_option(py_wrapper) => { + Python::with_gil(|py| -> PyResult<_> { + let mut borrowed = py_wrapper.borrow_mut(py); + let $name = &mut borrowed.inner; + Ok($body) + }) + .unwrap() + } + )* + } + }; + ($wrapper:expr, $name:ident, $body:block, $wrapper_type:ident, { $($wrapper_option:tt),*}, { $($wrapper_optional:tt($pw:ident) => $code_block:block)* }) => { + match &mut $wrapper { + $( + $wrapper_type::$wrapper_option(py_wrapper) => { + Python::with_gil(|py| -> PyResult<_> { + let mut borrowed = py_wrapper.borrow_mut(py); + let $name = &mut borrowed.inner; + Ok($body) + }) + .unwrap() + } + )* + $($wrapper_type::$wrapper_optional($pw) => { $code_block })* + } + }; + } + use pyo3::prelude::*; use serde::{Deserialize, Serialize}; @@ -567,9 +652,9 @@ pub mod pybind { #[pyclass(unsendable, name = "StdRand")] #[expect(clippy::unsafe_derive_deserialize)] #[derive(Serialize, Deserialize, Debug, Clone)] - /// Python class for StdRand + /// Python class for [`StdRand`] pub struct PythonStdRand { - /// Rust wrapped StdRand object + /// Rust wrapped [`StdRand`] object pub inner: StdRand, } @@ -644,18 +729,17 @@ pub mod pybind { #[cfg(test)] mod tests { + use core::num::NonZero; + use crate::{ - nonzero, - rands::{ - Rand, RomuDuoJrRand, RomuTrioRand, Sfc64Rand, StdRand, XorShift64Rand, - Xoshiro256PlusPlusRand, - }, + Rand, RomuDuoJrRand, RomuTrioRand, Sfc64Rand, StdRand, XorShift64Rand, + Xoshiro256PlusPlusRand, }; fn test_single_rand(rand: &mut R) { assert_ne!(rand.next(), rand.next()); - assert!(rand.below(nonzero!(100)) < 100); - assert_eq!(rand.below(nonzero!(1)), 0); + assert!(rand.below(NonZero::new(100).unwrap()) < 100); + assert_eq!(rand.below(NonZero::new(1).unwrap()), 0); assert_eq!(rand.between(10, 10), 10); assert!(rand.between(11, 20) > 10); } diff --git a/crates/libafl_bolts/src/rands/loaded_dice.rs b/crates/fast_rands/src/loaded_dice.rs similarity index 88% rename from crates/libafl_bolts/src/rands/loaded_dice.rs rename to crates/fast_rands/src/loaded_dice.rs index 321c653b2cf..93f8cdb8618 100644 --- a/crates/libafl_bolts/src/rands/loaded_dice.rs +++ b/crates/fast_rands/src/loaded_dice.rs @@ -7,8 +7,8 @@ A simple module that implements a random sampler implementing the [alias method] Assume we want to sample from the following distribution: `p(0)=0.5, p(1)=0.3, p(2)=0.1, p(3)=0.1`: ```rust -# extern crate libafl_bolts; -use libafl_bolts::rands::{StdRand, loaded_dice::LoadedDiceSampler}; +# extern crate fast_rands; +use fast_rands::{StdRand, loaded_dice::LoadedDiceSampler}; fn main() { let mut rand = StdRand::new(); let mut sampler = LoadedDiceSampler::new(&[0.5, 0.3, 0.1, 0.1]).unwrap(); @@ -22,10 +22,22 @@ fn main() { Original code by @eqv, see */ -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; +use core::fmt::{self, Debug, Formatter}; use super::Rand; -use crate::Error; + +/// An illegal argument got passed to a function. +pub struct IllegalArgumentError; + +impl Debug for IllegalArgumentError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "LoadedDiceError(Tried to construct LoadedDiceSampler with empty probs array)" + ) + } +} /// Helper struct for [`LoadedDiceSampler`] #[derive(Debug, Clone, PartialEq)] @@ -54,11 +66,9 @@ pub struct LoadedDiceSampler { impl LoadedDiceSampler { /// Create a new [`LoadedDiceSampler`] with the given probabilities - pub fn new(probs: &[f64]) -> Result { + pub fn new(probs: &[f64]) -> Result { if probs.is_empty() { - return Err(Error::illegal_argument( - "Tried to construct LoadedDiceSampler with empty probs array", - )); + return Err(IllegalArgumentError); } let entries = LoadedDiceSampler::construct_table(probs); Ok(Self { entries }) @@ -113,7 +123,7 @@ mod tests { use alloc::vec::Vec; use super::LoadedDiceSampler; - use crate::rands::{Rand, StdRand}; + use crate::{Rand, StdRand}; #[test] #[expect(clippy::cast_precision_loss)] diff --git a/crates/libafl/Cargo.toml b/crates/libafl/Cargo.toml index ecbd2b0d6fe..af18329c5d9 100644 --- a/crates/libafl/Cargo.toml +++ b/crates/libafl/Cargo.toml @@ -31,19 +31,19 @@ workspace = true [features] default = [ - "std", "derive", + "errors_backtrace", + "fork", + "gzip", + "libafl_bolts/xxh3", + "llmp_broker_timeouts", "llmp_compression", "llmp_small_maps", - "llmp_broker_timeouts", "rand_trait", - "fork", - "gzip", "regex", "serdeany_autoreg", - "libafl_bolts/xxh3", "simd", - "errors_backtrace", + "std", ] document-features = ["dep:document-features"] @@ -52,19 +52,20 @@ document-features = ["dep:document-features"] ## Enables features that need rust's `std` lib to work, like print, env, ... support std = [ + "backtrace", + "bincode", + "dep:nix", + "fastbloom", + "fs2", + "libafl_bolts/std", + "ll_mp/std", "serde_json", "serde_json/std", - "dep:nix", "serde/std", - "bincode", - "wait-timeout", - "uuid", - "backtrace", "serial_test", - "libafl_bolts/std", "typed-builder", - "fastbloom", - "fs2", + "uuid", + "wait-timeout", ] ## Tracks the Feedbacks and the Objectives that were interesting for a Testcase @@ -177,18 +178,16 @@ serdeany_autoreg = ["libafl_bolts/serdeany_autoreg"] llmp_broker_timeouts = ["std"] ## If set, llmp will bind to 0.0.0.0, allowing cross-device communication. Binds to localhost by default. -llmp_bind_public = ["libafl_bolts/llmp_bind_public"] +llmp_bind_public = ["ll_mp/llmp_bind_public"] ## Enables llmp compression using GZip -llmp_compression = ["libafl_bolts/llmp_compression"] +llmp_compression = ["ll_mp/llmp_compression"] ## Enables debug output for LLMP (also needs a `logger` installed) -llmp_debug = ["std", "libafl_bolts/llmp_debug"] +llmp_debug = ["std", "ll_mp/llmp_debug"] ## Reduces the initial map size for llmp -llmp_small_maps = [ - "libafl_bolts/llmp_small_maps", -] # reduces initial map size for llmp +llmp_small_maps = ["ll_mp/llmp_small_maps"] # reduces initial map size for llmp ## Grammar mutator. nautilus = ["std", "serde_json/std", "rand_trait", "regex-syntax", "regex"] @@ -220,50 +219,66 @@ static_assertions = { workspace = true } [dependencies] libafl_bolts = { workspace = true, features = ["alloc"] } +libafl_core = { workspace = true } libafl_derive = { workspace = true, default-features = true, optional = true } libafl_intelpt = { workspace = true, default-features = true, optional = true } +ll_mp = { workspace = true } -rustversion = { workspace = true } -tuple_list = { version = "0.1.3" } +ahash = { workspace = true } # The hash function already used in hashbrown +arbitrary-int = { workspace = true } +arrayvec = { version = "0.7.6", optional = true, default-features = false } # used for fixed-len collects +async-std = { version = "1.13.0", features = ["attributes"], optional = true } +backtrace = { workspace = true, optional = true } # Used to get the stacktrace in StacktraceObserver +bincode = { version = "2.0.1", optional = true, features = ["serde"] } +bitbybit = { workspace = true } +bitvec = { version = "1.0.1", optional = true, features = [ + "serde", +] } # used for string range storage +cadence = { version = "1.5.0", optional = true } # For the statsd monitor +clap = { workspace = true, optional = true } +const_format = "0.2.33" # used for providing helpful compiler output +const_panic = { version = "0.2.9", default-features = false } # similarly, for formatting const panic output +crossterm = { version = "0.29.0", optional = true } +enumflags2 = { version = "0.7.10", optional = true } +fastbloom = { workspace = true, optional = true } +fs2 = { workspace = true, optional = true } # used by OnDisk Corpus for file locking +futures = { version = "0.3.30", optional = true } hashbrown = { workspace = true, features = [ "serde", "ahash", ], default-features = false } # A faster hashmap, nostd compatible -num-traits = { workspace = true, default-features = false } -serde = { workspace = true, features = ["alloc"] } # serialization lib -postcard = { workspace = true } # no_std compatible serde serialization format -bincode = { version = "2.0.1", optional = true, features = ["serde"] } -bitbybit = { workspace = true } -arbitrary-int = { workspace = true } -ahash = { workspace = true } # The hash function already used in hashbrown +libcasr = { version = "2.12.1", optional = true } +libm = "0.2.8" +log = { workspace = true } meminterval = { workspace = true, features = ["serde"] } -backtrace = { workspace = true, optional = true } # Used to get the stacktrace in StacktraceObserver -typed-builder = { workspace = true, optional = true } # Implement the builder pattern at compiletime -send_wrapper = { version = "0.6.0", optional = true } # To move data between threads - -serde_json = { workspace = true, optional = true, default-features = false, features = [ - "alloc", -] } +mlua = { version = "0.10.3", features = [ + "lua54", + "vendored", + "macros", +], optional = true } # For Lua Mutators / TODO: macros is not needed/ a temporary fix for docsrs, see nix = { workspace = true, optional = true, features = [ "signal", "ptrace", "personality", "fs", ] } -regex = { workspace = true, optional = true } -uuid = { workspace = true, optional = true, features = ["serde", "v4"] } -libm = "0.2.8" +num_enum = { workspace = true, optional = true } +num-traits = { workspace = true, default-features = false } +postcard = { workspace = true } # no_std compatible serde serialization format +prometheus-client = { version = "0.23.0", optional = true } # For the prometheus monitor +pyo3 = { workspace = true, optional = true } ratatui = { version = "0.29.0", default-features = false, features = [ 'crossterm', ], optional = true } # Commandline rendering, for TUI Monitor -crossterm = { version = "0.29.0", optional = true } - -cadence = { version = "1.5.0", optional = true } # For the statsd monitor -prometheus-client = { version = "0.23.0", optional = true } # For the prometheus monitor +regex = { workspace = true, optional = true } +regex-syntax = { version = "0.8.4", optional = true } # For nautilus +rustversion = { workspace = true } +send_wrapper = { version = "0.6.0", optional = true } # To move data between threads +serde = { workspace = true, features = ["alloc"] } # serialization lib +serde_json = { workspace = true, optional = true, default-features = false, features = [ + "alloc", +] } tide = { version = "0.16.0", optional = true } -async-std = { version = "1.13.0", features = ["attributes"], optional = true } -futures = { version = "0.3.30", optional = true } -log = { workspace = true } tokio = { version = "1.40.0", optional = true, features = [ "sync", "net", @@ -273,26 +288,11 @@ tokio = { version = "1.40.0", optional = true, features = [ "rt-multi-thread", "time", ] } # used for TCP Event Manager and multi-machine -enumflags2 = { version = "0.7.10", optional = true } - +tuple_list = { workspace = true } +typed-builder = { workspace = true, optional = true } # Implement the builder pattern at compiletime +uuid = { workspace = true, optional = true, features = ["serde", "v4"] } wait-timeout = { version = "0.2.0", optional = true } # used by CommandExecutor to wait for child process -libcasr = { version = "2.12.1", optional = true } - -bitvec = { version = "1.0.1", optional = true, features = [ - "serde", -] } # used for string range storage - -arrayvec = { version = "0.7.6", optional = true, default-features = false } # used for fixed-len collects - -const_format = "0.2.33" # used for providing helpful compiler output -const_panic = { version = "0.2.9", default-features = false } # similarly, for formatting const panic output - -pyo3 = { workspace = true, optional = true } -regex-syntax = { version = "0.8.4", optional = true } # For nautilus - -fs2 = { workspace = true, optional = true } # used by OnDisk Corpus for file locking - # optional-dev deps (change when target.'cfg(accessible(::std))'.test-dependencies will be stable) serial_test = { workspace = true, optional = true, default-features = false, features = [ "logging", @@ -300,17 +300,6 @@ serial_test = { workspace = true, optional = true, default-features = false, fea # Document all features of this crate (for `cargo doc`) document-features = { workspace = true, optional = true } -# Optional -clap = { workspace = true, optional = true } -num_enum = { workspace = true, optional = true } -fastbloom = { workspace = true, optional = true } -# For Lua Mutators -# TODO: macros is not needed/ a temporary fix for docsrs, see -mlua = { version = "0.10.3", features = [ - "lua54", - "vendored", - "macros", -], optional = true } [target.'cfg(unix)'.dependencies] libc = { workspace = true } # For (*nix) libc diff --git a/crates/libafl/src/common/nautilus/grammartec/recursion_info.rs b/crates/libafl/src/common/nautilus/grammartec/recursion_info.rs index 549e1302ef1..db756dacc69 100644 --- a/crates/libafl/src/common/nautilus/grammartec/recursion_info.rs +++ b/crates/libafl/src/common/nautilus/grammartec/recursion_info.rs @@ -89,7 +89,11 @@ impl RecursionInfo { for v in &mut weights { *v /= norm; } - LoadedDiceSampler::new(&weights) + LoadedDiceSampler::new(&weights).map_err(|err| { + Error::illegal_argument(format!( + "Could not crate LoadedDiceSampler for depths {depths:?}: {err:?}" + )) + }) } pub fn get_random_recursion_pair(&mut self, rand: &mut R) -> (NodeId, NodeId) { diff --git a/crates/libafl/src/events/llmp/restarting.rs b/crates/libafl/src/events/llmp/restarting.rs index 154e779ca55..24220d1aae7 100644 --- a/crates/libafl/src/events/llmp/restarting.rs +++ b/crates/libafl/src/events/llmp/restarting.rs @@ -16,17 +16,14 @@ use core::{ #[cfg(feature = "std")] use std::net::TcpStream; +#[cfg(feature = "std")] +use libafl_bolts::llmp::{TcpRequest, TcpResponse, recv_tcp_msg, send_tcp_msg}; #[cfg(any(windows, not(feature = "fork")))] use libafl_bolts::os::startable_self; #[cfg(all(unix, not(miri)))] use libafl_bolts::os::unix_signals::setup_signal_handler; #[cfg(all(feature = "fork", unix))] use libafl_bolts::os::{ForkResult, fork}; -#[cfg(feature = "std")] -use libafl_bolts::{ - IP_LOCALHOST, - llmp::{TcpRequest, TcpResponse, recv_tcp_msg, send_tcp_msg}, -}; #[cfg(feature = "llmp_compression")] use libafl_bolts::{ compress::GzipCompressor, @@ -43,6 +40,8 @@ use libafl_bolts::{ staterestore::StateRestorer, tuples::tuple_list, }; +#[cfg(feature = "std")] +use libafl_core::IP_LOCALHOST; use serde::{Serialize, de::DeserializeOwned}; use typed_builder::TypedBuilder; diff --git a/crates/libafl/src/mutators/havoc_mutations.rs b/crates/libafl/src/mutators/havoc_mutations.rs index f03b5e903be..5edd85a2b56 100644 --- a/crates/libafl/src/mutators/havoc_mutations.rs +++ b/crates/libafl/src/mutators/havoc_mutations.rs @@ -1,8 +1,7 @@ //! [`crate::mutators::Mutator`] collection equivalent to AFL++'s havoc mutations -use libafl_bolts::{ - map_tuple_list_type, merge_tuple_list_type, - tuples::{Map, Merge, tuple_list, tuple_list_type}, +use libafl_bolts::tuples::{ + Map, Merge, map_tuple_list_type, merge_tuple_list_type, tuple_list, tuple_list_type, }; use crate::mutators::{ diff --git a/crates/libafl/src/observers/stacktrace.rs b/crates/libafl/src/observers/stacktrace.rs index 77ca29a8c8c..0046a171131 100644 --- a/crates/libafl/src/observers/stacktrace.rs +++ b/crates/libafl/src/observers/stacktrace.rs @@ -16,7 +16,7 @@ use std::{ }; use backtrace::Backtrace; -use libafl_bolts::{Named, ownedref::OwnedRefMut}; +use libafl_bolts::{Named, ownedref::OwnedRefMut, shmem::ShMem}; #[allow(unused_imports)] // expect breaks here for some reason #[cfg(feature = "casr")] use libcasr::{ @@ -156,6 +156,29 @@ impl<'a> BacktraceObserver<'a> { } } + /// [`BacktraceObserver`], with the `backtrace_hash` pointing to the given [`ShMem`]. + /// + /// # Panics + /// Panics if the given shared mem is smaller than `sizeof::()` + /// + /// # Safety + /// The shared memory needs to point to a valid u64 hash int. + /// Any use of this [`OwnedRefMut`] will dereference a pointer to the given shared memory accordingly + pub unsafe fn from_shmem( + observer_name: S, + shmem: &mut SHM, + harness_type: HarnessType, + ) -> Self + where + S: Into>, + { + Self::new( + observer_name, + unsafe { OwnedRefMut::from_mut_ptr(shmem.as_mut_ptr_of().unwrap()) }, + harness_type, + ) + } + /// Creates a new [`BacktraceObserver`] with the given name, owning a new `backtrace_hash` variable. #[must_use] pub fn owned(observer_name: S, harness_type: HarnessType) -> Self diff --git a/crates/libafl_bolts/Cargo.toml b/crates/libafl_bolts/Cargo.toml index a476ce03720..07a1af34c5d 100644 --- a/crates/libafl_bolts/Cargo.toml +++ b/crates/libafl_bolts/Cargo.toml @@ -27,14 +27,12 @@ all-features = true [features] default = [ - "std", + "alloc", "derive", - "llmp_compression", - "llmp_small_maps", - "rand_trait", "gzip", + "rand_trait", "serdeany_autoreg", - "alloc", + "std", "xxh3", ] document-features = ["dep:document-features"] @@ -44,29 +42,52 @@ document-features = ["dep:document-features"] ## Enables features that need rust's `std` lib to work, like print, env, ... support std = [ + "alloc", + "backtrace", + "build_id2", + "core_affinity2", + "fast_rands/std", "hostname", + "libafl_core/nix", + "libafl_core/nix", + "libafl_core/std", + "libafl_core/std", + "ll_mp/std", + "minibsod", "nix", + "no_std_time/std", "serde/std", - "uuid", - "backtrace", - "uds", "serial_test", - "alloc", + "shmem_providers/std", "simd", + "uds", ] ## Enables all features that allocate in `no_std` -alloc = ["serde/alloc", "hashbrown", "postcard", "erased-serde/alloc", "ahash"] +alloc = [ + "ahash", + "erased-serde/alloc", + "fast_rands/alloc", + "hashbrown", + "libafl_core/alloc", + "ll_mp/alloc", + "no_std_time/alloc", + "postcard", + "serde/alloc", + "serde_anymap", + "shmem_providers/alloc", + "tuple_list_ex/alloc", +] ## Provide the `#[derive(SerdeAny)]` macro. derive = ["libafl_derive"] ## If set, libafl_bolt's `rand` implementations will implement `rand_core::CoreRng` ## and, inversely, all seedable `rand_core::RngCore` types can be used as Rng for LibAFL. -rand_trait = ["rand_core"] +rand_trait = ["fast_rands/rand_trait"] ## Will build the `pyo3` bindings -python = ["pyo3", "std"] +python = ["pyo3", "std", "libafl_core/python"] ## Expose `libafl::prelude` for direct access to all types without additional `use` directives prelude = [] @@ -81,10 +102,10 @@ qemu_cli = ["cli"] frida_cli = ["cli"] ## Stores the backtraces of all generated `Error`s. Good for debugging, but may come with a slight performance hit. -errors_backtrace = ["backtrace"] +errors_backtrace = ["libafl_core/errors_backtrace"] ## Enables gzip compression in certain parts of the lib -gzip = ["miniz_oxide", "alloc"] +gzip = ["miniz_oxide", "alloc", "ll_mp/gzip"] ## Replaces `ahash` with the potentially faster [`xxh3`](https://github.com/Cyan4973/xxHash) in some parts of the lib. ## This yields a stable and fast hash, but may increase the resulting binary size slightly @@ -98,25 +119,24 @@ xxh3 = ["xxhash-rust"] ## With this feature, stored state remains deserializable across multiple compilations of LibAFL. ## The rust doc specifically states that "multiple types may map to the same type name", so it could potentially lead to bugs. ## However, we make sure that no two types with the same name ever exist. -stable_anymap = [] +stable_anymap = ["serde_anymap/stable_anymap"] ## Automatically register all `#[derive(SerdeAny)]` types at startup. -serdeany_autoreg = ["ctor"] - +serdeany_autoreg = ["serde_anymap/serdeany_autoreg"] #! ### LLMP features ## If set, llmp will bind to 0.0.0.0, allowing cross-device communication. Binds to localhost by default. -llmp_bind_public = ["alloc"] +llmp_bind_public = ["ll_mp/llmp_bind_public"] ## Enables llmp compression using GZip -llmp_compression = ["alloc", "gzip"] +llmp_compression = ["ll_mp/llmp_compression"] ## Enables debug output for LLMP (also needs a `logger` installed) -llmp_debug = ["alloc", "std"] +llmp_debug = ["ll_mp/llmp_debug"] ## Reduces the initial map size for llmp -llmp_small_maps = ["alloc"] +llmp_small_maps = ["ll_mp/llmp_small_maps"] #! ### Stable SIMD features @@ -133,60 +153,65 @@ chrono = "0.4.40" itertools = "0.14.0" [dependencies] +build_id2 = { workspace = true, optional = true } +core_affinity2 = { workspace = true, optional = true } +exceptional = { workspace = true } +fast_rands = { workspace = true } +libafl_core = { workspace = true } libafl_derive = { workspace = true, default-features = true, optional = true } +ll_mp = { workspace = true } +minibsod = { workspace = true, optional = true } +no_std_time = { workspace = true } +ownedref = { workspace = true } +serde_anymap = { workspace = true, optional = true } +shmem_providers = { workspace = true } static_assertions = { workspace = true } +tuple_list_ex = { workspace = true, features = ["serde"] } typeid = { workspace = true } -tuple_list = { version = "0.1.3" } +ahash = { workspace = true, optional = true } # The hash function already used in hashbrown +backtrace = { workspace = true, default-features = true, optional = true } # Used to get the stacktrace in StacktraceObserver +clap = { workspace = true, features = [ + "derive", + "wrap_help", +], optional = true } # CLI parsing, for libafl_bolts::cli / the `cli` feature +erased-serde = { version = "0.4.5", default-features = false, optional = true } # erased serde hashbrown = { workspace = true, features = [ "serde", "ahash", ], default-features = false, optional = true } # A faster hashmap, nostd compatible -xxhash-rust = { version = "0.8.12", features = [ - "xxh3", -], optional = true } # xxh3 hashing for rust -serde = { workspace = true, default-features = false, features = [ - "derive", -] } # serialization lib -erased-serde = { version = "0.4.5", default-features = false, optional = true } # erased serde -postcard = { workspace = true, optional = true } # no_std compatible serde serialization format -num_enum = { workspace = true, default-features = false } -ahash = { workspace = true, optional = true } # The hash function already used in hashbrown -backtrace = { workspace = true, default-features = true, optional = true } # Used to get the stacktrace in StacktraceObserver - -ctor = { optional = true, version = "0.4.0" } -miniz_oxide = { version = "0.8.0", optional = true } hostname = { version = "0.4.0", optional = true } # Is there really no gethostname in the stdlib? -rand_core = { version = "0.9.0", optional = true } +log = { workspace = true } +miniz_oxide = { version = "0.8.0", optional = true } nix = { workspace = true, optional = true, default-features = false, features = [ "fs", "signal", "socket", "poll", ] } -uuid = { workspace = true, optional = true, features = ["serde", "v4"] } -clap = { workspace = true, features = [ - "derive", - "wrap_help", -], optional = true } # CLI parsing, for libafl_bolts::cli / the `cli` feature -log = { workspace = true } +num_enum = { workspace = true, default-features = false } +postcard = { workspace = true, optional = true } # no_std compatible serde serialization format pyo3 = { workspace = true, optional = true, features = ["serde", "macros"] } +serde = { workspace = true, default-features = false, features = [ + "derive", +] } # serialization lib +tuple_list = { workspace = true } +xxhash-rust = { version = "0.8.12", features = [ + "xxh3", +], optional = true } # xxh3 hashing for rust # optional-dev deps (change when target.'cfg(accessible(::std))'.test-dependencies will be stable) serial_test = { workspace = true, optional = true, default-features = false, features = [ "logging", ] } +rustversion = { workspace = true } # optional stable simd, pin to a commit due to `u8x32` not released yet. Switch to `wide` as long as next release is out! wide = { version = "0.7.33", optional = true, package = "libafl_wide" } -rustversion = { workspace = true } # Document all features of this crate (for `cargo doc`) document-features = { workspace = true, optional = true } -[lints] -workspace = true - [target.'cfg(unix)'.dependencies] libc = { workspace = true } # For (*nix) libc uds = { version = "0.4.2", optional = true, default-features = false } @@ -194,15 +219,14 @@ uds = { version = "0.4.2", optional = true, default-features = false } [target.'cfg(windows)'.dependencies] windows = { workspace = true, features = [ "Win32_Foundation", - "Win32_System_Threading", + "Win32_Security", + "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_Kernel", "Win32_System_Memory", - "Win32_Security", "Win32_System_SystemInformation", - "Win32_System_Console", + "Win32_System_Threading", ] } -once_cell = "1.10.0" winapi = { version = "0.3", features = [ "fileapi", "handleapi", @@ -224,11 +248,6 @@ mach2 = "0.4.2" #opt-level = 3 #debug = true -[[example]] -name = "llmp_test" -path = "./examples/llmp_test/main.rs" -required-features = ["std"] - [[example]] name = "simd" @@ -236,3 +255,6 @@ path = "./examples/simd/simd.rs" bench = true harness = false required-features = ["std", "simd"] + +[lints] +workspace = true diff --git a/crates/libafl_bolts/src/compress.rs b/crates/libafl_bolts/src/compress.rs index 3cf189638e7..c62d8b5d09e 100644 --- a/crates/libafl_bolts/src/compress.rs +++ b/crates/libafl_bolts/src/compress.rs @@ -62,12 +62,8 @@ impl GzipCompressor { /// Decompression. pub fn decompress(&self, buf: &[u8]) -> Result, Error> { - let decompressed = decompress_to_vec(buf); - - match decompressed { - Ok(buf) => Ok(buf), - Err(_) => Err(Error::compression()), - } + decompress_to_vec(buf) + .map_err(|err| Error::illegal_state(format!("Failed to decompress: {err:?}"))) } } diff --git a/crates/libafl_bolts/src/lib.rs b/crates/libafl_bolts/src/lib.rs index 27268dac104..93d897a2d83 100644 --- a/crates/libafl_bolts/src/lib.rs +++ b/crates/libafl_bolts/src/lib.rs @@ -46,21 +46,6 @@ ) )] -/// We need some sort of "[`String`]" for errors in `no_alloc`... -/// We can only support `'static` without allocator, so let's do that. -#[cfg(not(feature = "alloc"))] -type String = &'static str; - -/// A simple non-allocating "format" string wrapper for no-std. -/// -/// Problem is that we really need a non-allocating format... -/// This one simply returns the `fmt` string. -/// Good enough for simple errors, for anything else, use the `alloc` feature. -#[cfg(not(feature = "alloc"))] -macro_rules! format { - ($fmt:literal) => {{ $fmt }}; -} - #[cfg(feature = "std")] #[macro_use] extern crate std; @@ -69,13 +54,10 @@ extern crate std; #[doc(hidden)] pub extern crate alloc; -#[cfg(feature = "ctor")] -#[doc(hidden)] -pub use ctor; -#[cfg(feature = "alloc")] -pub mod anymap; #[cfg(feature = "std")] -pub mod build_id; +pub use build_id2 as build_id; +#[cfg(feature = "alloc")] +pub use serde_anymap::anymap; #[cfg(all( any(feature = "cli", feature = "frida_cli", feature = "qemu_cli"), feature = "std" @@ -84,29 +66,21 @@ pub mod cli; #[cfg(feature = "gzip")] pub mod compress; #[cfg(feature = "std")] -pub mod core_affinity; -pub mod cpu; +pub use core_affinity2 as core_affinity; #[cfg(feature = "std")] pub mod fs; #[cfg(feature = "alloc")] -pub mod llmp; +pub use ll_mp as llmp; pub mod math; #[cfg(feature = "std")] -pub mod minibsod; +pub use minibsod; pub mod os; #[cfg(feature = "alloc")] -pub mod ownedref; -pub mod rands; -#[cfg(feature = "alloc")] -pub mod serdeany; -pub mod shmem; +pub use serde_anymap::serdeany; #[cfg(feature = "std")] pub mod staterestore; -#[cfg(feature = "alloc")] -pub mod subrange; -// TODO: reenable once ahash works in no-alloc #[cfg(any(feature = "xxh3", feature = "alloc"))] -pub mod tuples; +pub use tuple_list_ex as tuples; #[cfg(all(feature = "std", unix))] pub mod argparse; @@ -115,11 +89,18 @@ pub use argparse::*; #[cfg(feature = "std")] pub mod target_args; +pub use fast_rands as rands; +pub use libafl_core::{ + AsIter, AsIterMut, AsSlice, AsSliceMut, ClientId, Error, HasLen, HasRefCnt, Named, Truncate, +}; +pub use no_std_time::{current_milliseconds, current_nanos, current_time, format_duration}; +pub use ownedref::{self, subrange}; +#[cfg(feature = "alloc")] +pub use serde_anymap::impl_serdeany; +pub use shmem_providers as shmem; #[cfg(feature = "std")] pub use target_args::*; -pub mod simd; - /// The purpose of this module is to alleviate imports of the bolts by adding a glob import. #[cfg(feature = "prelude")] pub mod bolts_prelude { @@ -148,15 +129,15 @@ pub mod bolts_prelude { #[cfg(all(unix, feature = "std"))] use alloc::boxed::Box; #[cfg(feature = "alloc")] -use alloc::{borrow::Cow, string::ToString, vec::Vec}; +use alloc::string::String; +#[cfg(feature = "alloc")] +use alloc::string::ToString; #[cfg(all(not(feature = "xxh3"), feature = "alloc"))] use core::hash::BuildHasher; #[cfg(any(feature = "xxh3", feature = "alloc"))] use core::hash::{Hash, Hasher}; #[cfg(all(unix, feature = "std"))] use core::mem; -#[cfg(feature = "std")] -use std::time::{SystemTime, UNIX_EPOCH}; #[cfg(all(unix, feature = "std"))] use std::{ fs::File, @@ -169,76 +150,12 @@ use std::{ // TODO: re-enable once is resolved. #[cfg(all(not(feature = "xxh3"), feature = "alloc"))] use ahash::RandomState; -use log::SetLoggerError; -use serde::{Deserialize, Serialize}; -#[cfg(feature = "xxh3")] -use xxhash_rust::xxh3::xxh3_64; - -/// The client ID == the sender id. -#[repr(transparent)] -#[derive( - Debug, Default, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, -)] -pub struct ClientId(pub u32); - -use core::{ - array::TryFromSliceError, - fmt::{self, Display}, - num::{ParseIntError, TryFromIntError}, - ops::{Deref, DerefMut}, - time, -}; -#[cfg(feature = "std")] -use std::{env::VarError, io}; - #[cfg(feature = "libafl_derive")] pub use libafl_derive::SerdeAny; #[cfg(feature = "std")] use log::{Metadata, Record}; -#[cfg(feature = "alloc")] -use { - alloc::string::{FromUtf8Error, String}, - core::cell::{BorrowError, BorrowMutError}, - core::str::Utf8Error, -}; - -/// Localhost addr, this is used, for example, for LLMP Client, which connects to this address -pub const IP_LOCALHOST: &str = "127.0.0.1"; - -/// We need fixed names for many parts of this lib. -#[cfg(feature = "alloc")] -pub trait Named { - /// Provide the name of this element. - fn name(&self) -> &Cow<'static, str>; -} - -#[cfg(feature = "errors_backtrace")] -/// Error Backtrace type when `errors_backtrace` feature is enabled (== [`backtrace::Backtrace`]) -pub type ErrorBacktrace = backtrace::Backtrace; - -#[cfg(not(feature = "errors_backtrace"))] -#[derive(Debug, Default)] -/// ZST to use when `errors_backtrace` is disabled -pub struct ErrorBacktrace; - -#[cfg(not(feature = "errors_backtrace"))] -impl ErrorBacktrace { - /// Nop - #[must_use] - pub fn new() -> Self { - Self - } -} - -#[cfg(feature = "errors_backtrace")] -fn display_error_backtrace(f: &mut fmt::Formatter, err: &ErrorBacktrace) -> fmt::Result { - write!(f, "\nBacktrace: {err:?}") -} -#[cfg(not(feature = "errors_backtrace"))] -#[expect(clippy::unnecessary_wraps)] -fn display_error_backtrace(_f: &mut fmt::Formatter, _err: &ErrorBacktrace) -> fmt::Result { - fmt::Result::Ok(()) -} +#[cfg(feature = "xxh3")] +use xxhash_rust::xxh3::xxh3_64; /// Returns the standard input [`Hasher`] /// @@ -300,418 +217,6 @@ pub fn generic_hash_std(input: &I) -> u64 { hasher.finish() } -/// Main error struct for `LibAFL` -#[derive(Debug)] -pub enum Error { - /// Serialization error - Serialize(String, ErrorBacktrace), - /// Compression error - #[cfg(feature = "gzip")] - Compression(ErrorBacktrace), - /// Optional val was supposed to be set, but isn't. - EmptyOptional(String, ErrorBacktrace), - /// Key not in Map - KeyNotFound(String, ErrorBacktrace), - /// Key already exists and should not overwrite - KeyExists(String, ErrorBacktrace), - /// No elements in the current item - Empty(String, ErrorBacktrace), - /// End of iteration - IteratorEnd(String, ErrorBacktrace), - /// This is not supported (yet) - NotImplemented(String, ErrorBacktrace), - /// You're holding it wrong - IllegalState(String, ErrorBacktrace), - /// The argument passed to this method or function is not valid - IllegalArgument(String, ErrorBacktrace), - /// The performed action is not supported on the current platform - Unsupported(String, ErrorBacktrace), - /// Shutting down, not really an error. - ShuttingDown, - /// OS error, wrapping a [`io::Error`] - #[cfg(feature = "std")] - OsError(io::Error, String, ErrorBacktrace), - /// Something else happened - Unknown(String, ErrorBacktrace), - /// Error with the corpora - InvalidCorpus(String, ErrorBacktrace), - /// Error specific to a runtime like QEMU or Frida - Runtime(String, ErrorBacktrace), - /// The `Input` was invalid. - InvalidInput(String, ErrorBacktrace), -} - -impl Error { - /// Serialization error - #[must_use] - pub fn serialize(arg: S) -> Self - where - S: Into, - { - Error::Serialize(arg.into(), ErrorBacktrace::new()) - } - - #[cfg(feature = "gzip")] - /// Compression error - #[must_use] - pub fn compression() -> Self { - Error::Compression(ErrorBacktrace::new()) - } - - /// Optional val was supposed to be set, but isn't. - #[must_use] - pub fn empty_optional(arg: S) -> Self - where - S: Into, - { - Error::EmptyOptional(arg.into(), ErrorBacktrace::new()) - } - - /// The `Input` was invalid - #[must_use] - pub fn invalid_input(reason: S) -> Self - where - S: Into, - { - Error::InvalidInput(reason.into(), ErrorBacktrace::new()) - } - - /// Key not in Map - #[must_use] - pub fn key_not_found(arg: S) -> Self - where - S: Into, - { - Error::KeyNotFound(arg.into(), ErrorBacktrace::new()) - } - - /// Key already exists in Map - #[must_use] - pub fn key_exists(arg: S) -> Self - where - S: Into, - { - Error::KeyExists(arg.into(), ErrorBacktrace::new()) - } - - /// No elements in the current item - #[must_use] - pub fn empty(arg: S) -> Self - where - S: Into, - { - Error::Empty(arg.into(), ErrorBacktrace::new()) - } - - /// End of iteration - #[must_use] - pub fn iterator_end(arg: S) -> Self - where - S: Into, - { - Error::IteratorEnd(arg.into(), ErrorBacktrace::new()) - } - - /// This is not supported (yet) - #[must_use] - pub fn not_implemented(arg: S) -> Self - where - S: Into, - { - Error::NotImplemented(arg.into(), ErrorBacktrace::new()) - } - - /// You're holding it wrong - #[must_use] - pub fn illegal_state(arg: S) -> Self - where - S: Into, - { - Error::IllegalState(arg.into(), ErrorBacktrace::new()) - } - - /// The argument passed to this method or function is not valid - #[must_use] - pub fn illegal_argument(arg: S) -> Self - where - S: Into, - { - Error::IllegalArgument(arg.into(), ErrorBacktrace::new()) - } - - /// Shutting down, not really an error. - #[must_use] - pub fn shutting_down() -> Self { - Error::ShuttingDown - } - - /// This operation is not supported on the current architecture or platform - #[must_use] - pub fn unsupported(arg: S) -> Self - where - S: Into, - { - Error::Unsupported(arg.into(), ErrorBacktrace::new()) - } - - /// OS error with additional message - #[cfg(feature = "std")] - #[must_use] - pub fn os_error(err: io::Error, msg: S) -> Self - where - S: Into, - { - Error::OsError(err, msg.into(), ErrorBacktrace::new()) - } - - /// OS error from [`io::Error::last_os_error`] with additional message - #[cfg(feature = "std")] - #[must_use] - pub fn last_os_error(msg: S) -> Self - where - S: Into, - { - Error::OsError( - io::Error::last_os_error(), - msg.into(), - ErrorBacktrace::new(), - ) - } - - /// Something else happened - #[must_use] - pub fn unknown(arg: S) -> Self - where - S: Into, - { - Error::Unknown(arg.into(), ErrorBacktrace::new()) - } - - /// Error with corpora - #[must_use] - pub fn invalid_corpus(arg: S) -> Self - where - S: Into, - { - Error::InvalidCorpus(arg.into(), ErrorBacktrace::new()) - } - - /// Error specific to some runtime, like QEMU or Frida - #[must_use] - pub fn runtime(arg: S) -> Self - where - S: Into, - { - Error::Runtime(arg.into(), ErrorBacktrace::new()) - } -} - -impl core::error::Error for Error { - #[cfg(feature = "std")] - fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { - if let Self::OsError(err, _, _) = self { - Some(err) - } else { - None - } - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Serialize(s, b) => { - write!(f, "Error in Serialization: `{0}`", &s)?; - display_error_backtrace(f, b) - } - #[cfg(feature = "gzip")] - Self::Compression(b) => { - write!(f, "Error in decompression")?; - display_error_backtrace(f, b) - } - Self::EmptyOptional(s, b) => { - write!(f, "Optional value `{0}` was not set", &s)?; - display_error_backtrace(f, b) - } - Self::KeyNotFound(s, b) => { - write!(f, "Key: `{0}` - not found", &s)?; - display_error_backtrace(f, b) - } - Self::KeyExists(s, b) => { - write!(f, "Key: `{0}` - already exists", &s)?; - display_error_backtrace(f, b) - } - Self::Empty(s, b) => { - write!(f, "No items in {0}", &s)?; - display_error_backtrace(f, b) - } - Self::IteratorEnd(s, b) => { - write!(f, "All elements have been processed in {0} iterator", &s)?; - display_error_backtrace(f, b) - } - Self::NotImplemented(s, b) => { - write!(f, "Not implemented: {0}", &s)?; - display_error_backtrace(f, b) - } - Self::IllegalState(s, b) => { - write!(f, "Illegal state: {0}", &s)?; - display_error_backtrace(f, b) - } - Self::IllegalArgument(s, b) => { - write!(f, "Illegal argument: {0}", &s)?; - display_error_backtrace(f, b) - } - Self::Unsupported(s, b) => { - write!( - f, - "The operation is not supported on the current platform: {0}", - &s - )?; - display_error_backtrace(f, b) - } - Self::ShuttingDown => write!(f, "Shutting down!"), - #[cfg(feature = "std")] - Self::OsError(err, s, b) => { - write!(f, "OS error: {0}: {1}", &s, err)?; - display_error_backtrace(f, b) - } - Self::Unknown(s, b) => { - write!(f, "Unknown error: {0}", &s)?; - display_error_backtrace(f, b) - } - Self::InvalidCorpus(s, b) => { - write!(f, "Invalid corpus: {0}", &s)?; - display_error_backtrace(f, b) - } - Self::Runtime(s, b) => { - write!(f, "Runtime error: {0}", &s)?; - display_error_backtrace(f, b) - } - Self::InvalidInput(s, b) => { - write!(f, "Encountered an invalid input: {0}", &s)?; - display_error_backtrace(f, b) - } - } - } -} - -#[cfg(feature = "alloc")] -impl From for Error { - fn from(err: BorrowError) -> Self { - Self::illegal_state(format!( - "Couldn't borrow from a RefCell as immutable: {err:?}" - )) - } -} - -#[cfg(feature = "alloc")] -impl From for Error { - fn from(err: BorrowMutError) -> Self { - Self::illegal_state(format!( - "Couldn't borrow from a RefCell as mutable: {err:?}" - )) - } -} - -/// Stringify the postcard serializer error -#[cfg(feature = "alloc")] -impl From for Error { - fn from(err: postcard::Error) -> Self { - Self::serialize(format!("{err:?}")) - } -} - -#[cfg(all(unix, feature = "std"))] -impl From for Error { - fn from(err: nix::Error) -> Self { - Self::unknown(format!("Unix error: {err:?}")) - } -} - -/// Create an AFL Error from io Error -#[cfg(feature = "std")] -impl From for Error { - fn from(err: io::Error) -> Self { - Self::os_error(err, "io::Error ocurred") - } -} - -#[cfg(feature = "alloc")] -impl From for Error { - fn from(err: FromUtf8Error) -> Self { - Self::unknown(format!("Could not convert byte / utf-8: {err:?}")) - } -} - -#[cfg(feature = "alloc")] -impl From for Error { - fn from(err: Utf8Error) -> Self { - Self::unknown(format!("Could not convert byte / utf-8: {err:?}")) - } -} - -#[cfg(feature = "std")] -impl From for Error { - fn from(err: VarError) -> Self { - Self::empty(format!("Could not get env var: {err:?}")) - } -} - -impl From for Error { - #[allow(unused_variables)] // err is unused without std - fn from(err: ParseIntError) -> Self { - Self::unknown(format!("Failed to parse Int: {err:?}")) - } -} - -impl From for Error { - #[allow(unused_variables)] // err is unused without std - fn from(err: TryFromIntError) -> Self { - Self::illegal_state(format!("Expected conversion failed: {err:?}")) - } -} - -impl From for Error { - #[allow(unused_variables)] // err is unused without std - fn from(err: TryFromSliceError) -> Self { - Self::illegal_argument(format!("Could not convert slice: {err:?}")) - } -} - -impl From for Error { - #[allow(unused_variables)] // err is unused without std - fn from(err: SetLoggerError) -> Self { - Self::illegal_state(format!("Failed to register logger: {err:?}")) - } -} - -#[cfg(windows)] -impl From for Error { - #[allow(unused_variables)] // err is unused without std - fn from(err: windows_result::Error) -> Self { - Self::unknown(format!("Windows API error: {err:?}")) - } -} - -#[cfg(feature = "python")] -impl From for Error { - fn from(err: pyo3::PyErr) -> Self { - pyo3::Python::with_gil(|py| { - if err - .matches( - py, - pyo3::types::PyType::new::(py), - ) - .unwrap() - { - Self::shutting_down() - } else { - Self::illegal_state(format!("Python exception: {err:?}")) - } - }) - } -} - /// The purpose of this module is to alleviate imports of many components by adding a glob import. #[cfg(feature = "prelude")] pub mod prelude { @@ -720,278 +225,6 @@ pub mod prelude { pub use super::{bolts_prelude::*, *}; } -#[cfg(all(any(doctest, test), not(feature = "std")))] -/// Provide custom time in `no_std` tests. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn external_current_millis() -> u64 { - // TODO: use "real" time here - 1000 -} - -/// Trait to convert into an Owned type -pub trait IntoOwned { - /// Returns if the current type is an owned type. - #[must_use] - fn is_owned(&self) -> bool; - - /// Transfer the current type into an owned type. - #[must_use] - fn into_owned(self) -> Self; -} - -/// Can be converted to a slice -pub trait AsSlice<'a> { - /// Type of the entries of this slice - type Entry: 'a; - /// Type of the reference to this slice - type SliceRef: Deref; - - /// Convert to a slice - fn as_slice(&'a self) -> Self::SliceRef; -} - -/// Can be converted to a slice -pub trait AsSizedSlice<'a, const N: usize> { - /// Type of the entries of this slice - type Entry: 'a; - /// Type of the reference to this slice - type SliceRef: Deref; - - /// Convert to a slice - fn as_sized_slice(&'a self) -> Self::SliceRef; -} - -impl<'a, T, R: ?Sized> AsSlice<'a> for R -where - T: 'a, - R: Deref, -{ - type Entry = T; - type SliceRef = &'a [T]; - - fn as_slice(&'a self) -> Self::SliceRef { - self - } -} - -impl<'a, T, const N: usize, R: ?Sized> AsSizedSlice<'a, N> for R -where - T: 'a, - R: Deref, -{ - type Entry = T; - type SliceRef = &'a [T; N]; - - fn as_sized_slice(&'a self) -> Self::SliceRef { - self - } -} - -/// Can be converted to a mutable slice -pub trait AsSliceMut<'a>: AsSlice<'a> { - /// Type of the mutable reference to this slice - type SliceRefMut: DerefMut; - - /// Convert to a slice - fn as_slice_mut(&'a mut self) -> Self::SliceRefMut; -} - -/// Can be converted to a mutable slice -pub trait AsSizedSliceMut<'a, const N: usize>: AsSizedSlice<'a, N> { - /// Type of the mutable reference to this slice - type SliceRefMut: DerefMut; - - /// Convert to a slice - fn as_sized_slice_mut(&'a mut self) -> Self::SliceRefMut; -} - -impl<'a, T, R: ?Sized> AsSliceMut<'a> for R -where - T: 'a, - R: DerefMut, -{ - type SliceRefMut = &'a mut [T]; - - fn as_slice_mut(&'a mut self) -> Self::SliceRefMut { - &mut *self - } -} - -impl<'a, T, const N: usize, R: ?Sized> AsSizedSliceMut<'a, N> for R -where - T: 'a, - R: DerefMut, -{ - type SliceRefMut = &'a mut [T; N]; - - fn as_sized_slice_mut(&'a mut self) -> Self::SliceRefMut { - &mut *self - } -} - -/// Create an `Iterator` from a reference -pub trait AsIter<'it> { - /// The item type - type Item: 'it; - /// The ref type - type Ref: Deref; - /// The iterator type - type IntoIter: Iterator; - - /// Create an iterator from &self - fn as_iter(&'it self) -> Self::IntoIter; -} - -impl<'it, S, T> AsIter<'it> for S -where - S: AsSlice<'it, Entry = T, SliceRef = &'it [T]>, - T: 'it, -{ - type Item = S::Entry; - type Ref = &'it Self::Item; - type IntoIter = core::slice::Iter<'it, Self::Item>; - - fn as_iter(&'it self) -> Self::IntoIter { - self.as_slice().iter() - } -} - -/// Create an `Iterator` from a mutable reference -pub trait AsIterMut<'it>: AsIter<'it> { - /// The ref type - type RefMut: DerefMut; - /// The iterator type - type IntoIterMut: Iterator; - - /// Create an iterator from &mut self - fn as_iter_mut(&'it mut self) -> Self::IntoIterMut; -} - -impl<'it, S, T> AsIterMut<'it> for S -where - S: AsSliceMut<'it, Entry = T, SliceRef = &'it [T], SliceRefMut = &'it mut [T]>, - T: 'it, -{ - type RefMut = &'it mut Self::Item; - type IntoIterMut = core::slice::IterMut<'it, Self::Item>; - - fn as_iter_mut(&'it mut self) -> Self::IntoIterMut { - self.as_slice_mut().iter_mut() - } -} - -/// Has a length field -pub trait HasLen { - /// The length - fn len(&self) -> usize; - - /// Returns `true` if it has no elements. - fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -#[cfg(feature = "alloc")] -impl HasLen for Vec { - #[inline] - fn len(&self) -> usize { - Vec::::len(self) - } -} - -impl HasLen for &mut T { - fn len(&self) -> usize { - self.deref().len() - } -} - -/// Has a ref count -pub trait HasRefCnt { - /// The ref count - fn refcnt(&self) -> isize; - /// The ref count, mutable - fn refcnt_mut(&mut self) -> &mut isize; -} - -/// Trait to truncate slices and maps to a new size -pub trait Truncate { - /// Reduce the size of the slice - fn truncate(&mut self, len: usize); -} - -/// Current time -#[cfg(feature = "std")] -#[must_use] -#[inline] -pub fn current_time() -> time::Duration { - SystemTime::now().duration_since(UNIX_EPOCH).unwrap() -} - -// external defined function in case of `no_std` -// -// Define your own `external_current_millis()` function via `extern "C"` -// which is linked into the binary and called from here. -#[cfg(all(not(any(doctest, test)), not(feature = "std")))] -unsafe extern "C" { - //#[unsafe(no_mangle)] - fn external_current_millis() -> u64; -} - -/// Current time (fixed fallback for `no_std`) -#[cfg(not(feature = "std"))] -#[inline] -#[must_use] -pub fn current_time() -> time::Duration { - let millis = unsafe { external_current_millis() }; - time::Duration::from_millis(millis) -} - -/// Gets current nanoseconds since [`UNIX_EPOCH`] -#[must_use] -#[inline] -pub fn current_nanos() -> u64 { - current_time().as_nanos() as u64 -} - -/// Gets current milliseconds since [`UNIX_EPOCH`] -#[must_use] -#[inline] -pub fn current_milliseconds() -> u64 { - current_time().as_millis() as u64 -} - -/// Format a `Duration` into a HMS string -#[cfg(feature = "alloc")] -#[must_use] -pub fn format_duration(duration: &time::Duration) -> String { - const MINS_PER_HOUR: u64 = 60; - const HOURS_PER_DAY: u64 = 24; - - const SECS_PER_MINUTE: u64 = 60; - const SECS_PER_HOUR: u64 = SECS_PER_MINUTE * MINS_PER_HOUR; - const SECS_PER_DAY: u64 = SECS_PER_HOUR * HOURS_PER_DAY; - - let total_secs = duration.as_secs(); - let secs = total_secs % SECS_PER_MINUTE; - - if total_secs < SECS_PER_MINUTE { - format!("{secs}s") - } else { - let mins = (total_secs / SECS_PER_MINUTE) % MINS_PER_HOUR; - if total_secs < SECS_PER_HOUR { - format!("{mins}m-{secs}s") - } else { - let hours = (total_secs / SECS_PER_HOUR) % HOURS_PER_DAY; - if total_secs < SECS_PER_DAY { - format!("{hours}h-{mins}m-{secs}s") - } else { - let days = total_secs / SECS_PER_DAY; - format!("{days}days {hours}h-{mins}m-{secs}s") - } - } - } -} - /// Format a number with thousands separators #[cfg(feature = "alloc")] #[must_use] @@ -1065,8 +298,8 @@ impl SimpleStdoutLogger { /// register stdout logger pub fn set_logger() -> Result<(), Error> { - log::set_logger(&LIBAFL_STDOUT_LOGGER)?; - Ok(()) + log::set_logger(&LIBAFL_STDOUT_LOGGER) + .map_err(|err| Error::illegal_state(format!("Failed to set logger: {err:?}"))) } } @@ -1118,8 +351,8 @@ pub fn get_thread_id() -> u64 { #[cfg(target_os = "windows")] mod windows_logging { use core::ptr; + use std::cell::OnceCell; - use once_cell::sync::OnceCell; use winapi::um::{ fileapi::WriteFile, handleapi::INVALID_HANDLE_VALUE, processenv::GetStdHandle, winbase::STD_OUTPUT_HANDLE, winnt::HANDLE, @@ -1231,8 +464,8 @@ impl SimpleStderrLogger { /// register stderr logger pub fn set_logger() -> Result<(), Error> { - log::set_logger(&LIBAFL_STDERR_LOGGER)?; - Ok(()) + log::set_logger(&LIBAFL_STDERR_LOGGER) + .map_err(|err| Error::illegal_state(format!("Could not set logger: {err:?}"))) } } @@ -1295,9 +528,9 @@ impl SimpleFdLogger { unsafe { let logger = &mut *logger; logger.set_fd(log_fd); - log::set_logger(logger)?; + log::set_logger(logger) + .map_err(|err| Error::illegal_state(format!("Could not set logger: {err:?}"))) } - Ok(()) } } @@ -1466,39 +699,6 @@ pub mod pybind { }; } - #[macro_export] - macro_rules! unwrap_me_mut_body { - ($wrapper:expr, $name:ident, $body:block, $wrapper_type:ident, { $($wrapper_option:tt),*}) => { - match &mut $wrapper { - $( - $wrapper_type::$wrapper_option(py_wrapper) => { - Python::with_gil(|py| -> PyResult<_> { - let mut borrowed = py_wrapper.borrow_mut(py); - let $name = &mut borrowed.inner; - Ok($body) - }) - .unwrap() - } - )* - } - }; - ($wrapper:expr, $name:ident, $body:block, $wrapper_type:ident, { $($wrapper_option:tt),*}, { $($wrapper_optional:tt($pw:ident) => $code_block:block)* }) => { - match &mut $wrapper { - $( - $wrapper_type::$wrapper_option(py_wrapper) => { - Python::with_gil(|py| -> PyResult<_> { - let mut borrowed = py_wrapper.borrow_mut(py); - let $name = &mut borrowed.inner; - Ok($body) - }) - .unwrap() - } - )* - $($wrapper_type::$wrapper_optional($pw) => { $code_block })* - } - }; - } - #[macro_export] macro_rules! impl_serde_pyobjectwrapper { ($struct_name:ident, $inner:tt) => { @@ -1569,29 +769,6 @@ pub mod pybind { } } -/// Create a [`Vec`] of the given type with `nb_elts` elements, initialized in place. -/// The closure must initialize [`Vec`] (of size `nb_elts` * `sizeo_of::()`). -/// -/// # Safety -/// -/// The input closure should fully initialize the new [`Vec`], not leaving any uninitialized bytes. -// TODO: Use MaybeUninit API at some point. -#[cfg(feature = "alloc")] -#[expect(clippy::uninit_vec)] -pub unsafe fn vec_init(nb_elts: usize, init_fn: F) -> Result, E> -where - F: FnOnce(&mut Vec) -> Result<(), E>, -{ - unsafe { - let mut new_vec: Vec = Vec::with_capacity(nb_elts); - new_vec.set_len(nb_elts); - - init_fn(&mut new_vec)?; - - Ok(new_vec) - } -} - #[cfg(test)] mod tests { diff --git a/crates/libafl_bolts/src/math.rs b/crates/libafl_bolts/src/math.rs index f9ac732a132..2755335d127 100644 --- a/crates/libafl_bolts/src/math.rs +++ b/crates/libafl_bolts/src/math.rs @@ -2,7 +2,7 @@ use core::ops::AddAssign; -use crate::Error; +use libafl_core::{Error, format}; /// Returns the cumulative distribution function for a discrete distribution. pub fn calculate_cumulative_distribution_in_place(probabilities: &mut [f32]) -> Result<(), Error> { diff --git a/crates/libafl_bolts/src/os/mod.rs b/crates/libafl_bolts/src/os.rs similarity index 96% rename from crates/libafl_bolts/src/os/mod.rs rename to crates/libafl_bolts/src/os.rs index 0bbdd9f0357..ad09f465811 100644 --- a/crates/libafl_bolts/src/os/mod.rs +++ b/crates/libafl_bolts/src/os.rs @@ -1,19 +1,5 @@ //! Operating System specific abstractions -#[cfg(any(unix, all(windows, feature = "std")))] -use crate::Error; - -#[cfg(all(unix, feature = "std"))] -pub mod unix_shmem_server; - -#[cfg(unix)] -pub mod unix_signals; -#[cfg(unix)] -pub use unix_signals::CTRL_C_EXIT; - -#[cfg(all(unix, feature = "alloc"))] -pub mod pipes; - #[cfg(all(unix, feature = "std"))] use alloc::{borrow::Cow, ffi::CString}; #[cfg(all(unix, feature = "std"))] @@ -29,15 +15,24 @@ use std::{ sync::OnceLock, }; +#[cfg(unix)] +pub use exceptional::unix_signals; +#[cfg(unix)] +pub use exceptional::unix_signals::CTRL_C_EXIT; +#[cfg(windows)] +pub use exceptional::windows_exceptions; +use libafl_core::format; // Allow a few extra features we need for the whole module -#[cfg(all(windows, feature = "std"))] -#[expect(missing_docs, overflowing_literals)] -pub mod windows_exceptions; #[cfg(unix)] use libc::pid_t; +#[cfg(all(unix, feature = "std"))] +pub use shmem_providers::pipes; #[cfg(all(windows, feature = "std"))] pub use windows_exceptions::CTRL_C_EXIT; +#[cfg(any(unix, all(windows, feature = "std")))] +use crate::Error; + /// A file that we keep open, pointing to /dev/null #[cfg(all(feature = "std", unix))] static NULL_FILE: OnceLock = OnceLock::new(); diff --git a/crates/libafl_bolts/src/simd.rs b/crates/libafl_bolts/src/simd.rs index 0be9430d765..e3944ea69d3 100644 --- a/crates/libafl_bolts/src/simd.rs +++ b/crates/libafl_bolts/src/simd.rs @@ -131,7 +131,7 @@ where } } -/// Unforunately we have to keep this type due to [`wide`] might not `PartialOrd` +/// Unfortunately we have to keep this type due to [`wide`] might not `PartialOrd` #[cfg(feature = "wide")] #[derive(Debug)] pub struct SimdMinReducer; diff --git a/crates/libafl_bolts/src/staterestore.rs b/crates/libafl_bolts/src/staterestore.rs index c75b2772eb6..76e71d22d98 100644 --- a/crates/libafl_bolts/src/staterestore.rs +++ b/crates/libafl_bolts/src/staterestore.rs @@ -17,12 +17,10 @@ use std::{ }; use ahash::RandomState; +use libafl_core::{AsSlice, Error}; use serde::{Serialize, de::DeserializeOwned}; -use crate::{ - AsSlice, Error, - shmem::{ShMem, ShMemProvider}, -}; +use crate::shmem::{ShMem, ShMemProvider}; /// If the saved page content equals exactly this buf, the restarted child wants to exit cleanly. const EXITING_MAGIC: &[u8; 16] = b"LIBAFL_EXIT_NOW\0"; diff --git a/crates/libafl_core/Cargo.toml b/crates/libafl_core/Cargo.toml new file mode 100644 index 00000000000..84f13b03b71 --- /dev/null +++ b/crates/libafl_core/Cargo.toml @@ -0,0 +1,63 @@ +[package] +name = "libafl_core" +version.workspace = true +authors = [ + "Andrea Fioraldi ", + "Dominik Maier ", +] +description = "A platform-independent shared memory library for Rust" +documentation = "https://docs.rs/libafl" +repository = "https://github.com/AFLplusplus/LibAFL/" +readme = "./README.md" +license = "MIT OR Apache-2.0" +keywords = ["os", "shmem", "no-std"] +edition = "2024" +rust-version = "1.87" +categories = ["embedded", "os", "no-std"] + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +[features] +default = [] +document-features = ["dep:document-features"] +#! # Feature Flags +#! ### General Features + +## Enables features that need rust's `std` lib to work, like print, env, ... support +std = ["alloc"] + +## Enables all features that allocate in `no_std` +alloc = [] + +## Stores the backtraces of all generated `Error`s. Good for debugging, but may come with a slight performance hit. +errors_backtrace = ["dep:backtrace"] + +## Enable python error conversion +python = ["dep:pyo3"] + +## Enables nix error conversion +nix = ["dep:nix"] + +## Enables postcard error conversion +postcard = ["dep:postcard"] + +## Enables Serde support for some types +serde = ["dep:serde", "alloc"] + +[build-dependencies] +rustversion = { workspace = true } + +[dev-dependencies] + +[dependencies] +document-features = { workspace = true, optional = true } +backtrace = { workspace = true, default-features = true, optional = true } +postcard = { workspace = true, optional = true } +nix = { workspace = true, optional = true } +pyo3 = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive", "alloc"], optional = true } + +[lints] +workspace = true diff --git a/crates/libafl_core/LICENSE-APACHE b/crates/libafl_core/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/libafl_core/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/libafl_core/LICENSE-MIT b/crates/libafl_core/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/libafl_core/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/libafl_core/README.md b/crates/libafl_core/README.md new file mode 100644 index 00000000000..0a638ac1244 --- /dev/null +++ b/crates/libafl_core/README.md @@ -0,0 +1,58 @@ +# LibAFL_bolts: OS and Fuzzer Dev's Libary Collection. + + LibAFL logo + +The `libafl_bolts` crate exposes a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just fuzzers completely different to LibAFL. +Some cross-platform things in bolts include (but are not limited to): + +* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable +* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation +* LLMP: A fast, lock-free IPC mechanism via SharedMap +* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores +* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/)) +* MiniBSOD: get and print information about the current process state including important registers. +* Tuples: Haskel-like compile-time tuple lists +* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork` + +LibAFL_bolts is written and maintained by + +* [Andrea Fioraldi](https://twitter.com/andreafioraldi) +* [Dominik Maier](https://twitter.com/domenuk) +* [s1341](https://twitter.com/srubenst1341) +* [Dongjia Zhang](https://github.com/tokatoka) +* [Addison Crump](https://github.com/addisoncrump) + +## Contributing + +For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 + +Even though we will gladly assist you in finishing up your PR, try to +- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26)) +- run `cargo nightly fmt` on your code before pushing +- check the output of `cargo clippy --all` or `./clippy.sh` +- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code. + +Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. + +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + + +
+ + +Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled +using the respective feature in each crate when it is present, such as the +'agpl' feature of the libafl crate. + diff --git a/crates/libafl_core/src/lib.rs b/crates/libafl_core/src/lib.rs new file mode 100644 index 00000000000..a2dfd3396a8 --- /dev/null +++ b/crates/libafl_core/src/lib.rs @@ -0,0 +1,821 @@ +/*! + * `LibAFL_core` contains core traits used across all crates, including the [`Error`] enum and various traits. + */ +#![doc = include_str!("../../../README.md")] +/*! */ +#![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +#![no_std] +#![cfg_attr(not(test), warn( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr(test, deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results +))] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] + +/// We need some sort of "[`String`]" for errors in `no_alloc`... +/// We can only support `'static` without allocator, so let's do that. +#[cfg(not(feature = "alloc"))] +type String = &'static str; + +/// A simple non-allocating "format" string wrapper for no-std. +/// +/// Problem is that we really need a non-allocating format... +/// This one simply returns the `fmt` string. +/// Good enough for simple errors, for anything else, use the `alloc` feature. +#[macro_export] +#[cfg(not(feature = "alloc"))] +macro_rules! format { + ($fmt:literal) => {{ $fmt }}; +} +/// Re-export of the "format" macro +#[cfg(feature = "alloc")] +pub use alloc::{borrow::Cow, format}; + +#[cfg(feature = "std")] +#[macro_use] +extern crate std; +#[cfg(feature = "alloc")] +#[doc(hidden)] +pub extern crate alloc; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +use core::{ + array::TryFromSliceError, + fmt::{self, Display}, + num::{ParseIntError, TryFromIntError}, + ops::{Deref, DerefMut}, +}; +#[cfg(feature = "std")] +use std::{env::VarError, io}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +#[cfg(feature = "alloc")] +use { + alloc::string::{FromUtf8Error, String}, + core::cell::{BorrowError, BorrowMutError}, + core::str::Utf8Error, +}; + +/// Localhost addr, this is used, for example, for LLMP Client, which connects to this address +pub const IP_LOCALHOST: &str = "127.0.0.1"; + +/// The client ID for various use cases across `LibAFL` +#[repr(transparent)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ClientId(pub u32); + +#[cfg(feature = "errors_backtrace")] +/// Error Backtrace type when `errors_backtrace` feature is enabled (== [`Backtrace`](std::backtrace::Backtrace`)) +pub type ErrorBacktrace = std::backtrace::Backtrace; + +#[cfg(not(feature = "errors_backtrace"))] +#[derive(Debug, Default)] +/// ZST to use when `errors_backtrace` is disabled +pub struct ErrorBacktrace; + +#[cfg(not(feature = "errors_backtrace"))] +impl ErrorBacktrace { + /// Nop + #[must_use] + pub fn capture() -> Self { + Self + } +} + +#[cfg(feature = "errors_backtrace")] +fn display_error_backtrace(f: &mut fmt::Formatter, err: &ErrorBacktrace) -> fmt::Result { + write!(f, "\nBacktrace: {err:?}") +} +#[cfg(not(feature = "errors_backtrace"))] +#[expect(clippy::unnecessary_wraps)] +fn display_error_backtrace(_f: &mut fmt::Formatter, _err: &ErrorBacktrace) -> fmt::Result { + fmt::Result::Ok(()) +} + +/// Main error enum for `LibAFL` +#[derive(Debug)] +pub enum Error { + /// Serialization error + Serialize(String, ErrorBacktrace), + /// Optional val was supposed to be set, but isn't. + EmptyOptional(String, ErrorBacktrace), + /// Key not in Map + KeyNotFound(String, ErrorBacktrace), + /// Key already exists and should not overwrite + KeyExists(String, ErrorBacktrace), + /// No elements in the current item + Empty(String, ErrorBacktrace), + /// End of iteration + IteratorEnd(String, ErrorBacktrace), + /// This is not supported (yet) + NotImplemented(String, ErrorBacktrace), + /// You're holding it wrong + IllegalState(String, ErrorBacktrace), + /// The argument passed to this method or function is not valid + IllegalArgument(String, ErrorBacktrace), + /// The performed action is not supported on the current platform + Unsupported(String, ErrorBacktrace), + /// Shutting down, not really an error. + ShuttingDown, + /// OS error, wrapping a [`io::Error`] + #[cfg(feature = "std")] + OsError(io::Error, String, ErrorBacktrace), + /// Something else happened + Unknown(String, ErrorBacktrace), + /// Error with the corpora + InvalidCorpus(String, ErrorBacktrace), + /// Error specific to a runtime like QEMU or Frida + Runtime(String, ErrorBacktrace), + /// The `Input` was invalid. + InvalidInput(String, ErrorBacktrace), +} + +impl Error { + /// Serialization error + #[must_use] + pub fn serialize(arg: S) -> Self + where + S: Into, + { + Error::Serialize(arg.into(), ErrorBacktrace::capture()) + } + + /// Optional val was supposed to be set, but isn't. + #[must_use] + pub fn empty_optional(arg: S) -> Self + where + S: Into, + { + Error::EmptyOptional(arg.into(), ErrorBacktrace::capture()) + } + + /// The `Input` was invalid + #[must_use] + pub fn invalid_input(reason: S) -> Self + where + S: Into, + { + Error::InvalidInput(reason.into(), ErrorBacktrace::capture()) + } + + /// Key not in Map + #[must_use] + pub fn key_not_found(arg: S) -> Self + where + S: Into, + { + Error::KeyNotFound(arg.into(), ErrorBacktrace::capture()) + } + + /// Key already exists in Map + #[must_use] + pub fn key_exists(arg: S) -> Self + where + S: Into, + { + Error::KeyExists(arg.into(), ErrorBacktrace::capture()) + } + + /// No elements in the current item + #[must_use] + pub fn empty(arg: S) -> Self + where + S: Into, + { + Error::Empty(arg.into(), ErrorBacktrace::capture()) + } + + /// End of iteration + #[must_use] + pub fn iterator_end(arg: S) -> Self + where + S: Into, + { + Error::IteratorEnd(arg.into(), ErrorBacktrace::capture()) + } + + /// This is not supported (yet) + #[must_use] + pub fn not_implemented(arg: S) -> Self + where + S: Into, + { + Error::NotImplemented(arg.into(), ErrorBacktrace::capture()) + } + + /// You're holding it wrong + #[must_use] + pub fn illegal_state(arg: S) -> Self + where + S: Into, + { + Error::IllegalState(arg.into(), ErrorBacktrace::capture()) + } + + /// The argument passed to this method or function is not valid + #[must_use] + pub fn illegal_argument(arg: S) -> Self + where + S: Into, + { + Error::IllegalArgument(arg.into(), ErrorBacktrace::capture()) + } + + /// Shutting down, not really an error. + #[must_use] + pub fn shutting_down() -> Self { + Error::ShuttingDown + } + + /// This operation is not supported on the current architecture or platform + #[must_use] + pub fn unsupported(arg: S) -> Self + where + S: Into, + { + Error::Unsupported(arg.into(), ErrorBacktrace::capture()) + } + + /// OS error with additional message + #[cfg(feature = "std")] + #[must_use] + pub fn os_error(err: io::Error, msg: S) -> Self + where + S: Into, + { + Error::OsError(err, msg.into(), ErrorBacktrace::capture()) + } + + /// OS error from [`io::Error::last_os_error`] with additional message + #[cfg(feature = "std")] + #[must_use] + pub fn last_os_error(msg: S) -> Self + where + S: Into, + { + Error::OsError( + io::Error::last_os_error(), + msg.into(), + ErrorBacktrace::capture(), + ) + } + + /// Something else happened + #[must_use] + pub fn unknown(arg: S) -> Self + where + S: Into, + { + Error::Unknown(arg.into(), ErrorBacktrace::capture()) + } + + /// Error with corpora + #[must_use] + pub fn invalid_corpus(arg: S) -> Self + where + S: Into, + { + Error::InvalidCorpus(arg.into(), ErrorBacktrace::capture()) + } + + /// Error specific to some runtime, like QEMU or Frida + #[must_use] + pub fn runtime(arg: S) -> Self + where + S: Into, + { + Error::Runtime(arg.into(), ErrorBacktrace::capture()) + } +} + +impl core::error::Error for Error { + #[cfg(feature = "std")] + fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { + if let Self::OsError(err, _, _) = self { + Some(err) + } else { + None + } + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Serialize(s, b) => { + write!(f, "Error in Serialization: `{0}`", &s)?; + display_error_backtrace(f, b) + } + Self::EmptyOptional(s, b) => { + write!(f, "Optional value `{0}` was not set", &s)?; + display_error_backtrace(f, b) + } + Self::KeyNotFound(s, b) => { + write!(f, "Key: `{0}` - not found", &s)?; + display_error_backtrace(f, b) + } + Self::KeyExists(s, b) => { + write!(f, "Key: `{0}` - already exists", &s)?; + display_error_backtrace(f, b) + } + Self::Empty(s, b) => { + write!(f, "No items in {0}", &s)?; + display_error_backtrace(f, b) + } + Self::IteratorEnd(s, b) => { + write!(f, "All elements have been processed in {0} iterator", &s)?; + display_error_backtrace(f, b) + } + Self::NotImplemented(s, b) => { + write!(f, "Not implemented: {0}", &s)?; + display_error_backtrace(f, b) + } + Self::IllegalState(s, b) => { + write!(f, "Illegal state: {0}", &s)?; + display_error_backtrace(f, b) + } + Self::IllegalArgument(s, b) => { + write!(f, "Illegal argument: {0}", &s)?; + display_error_backtrace(f, b) + } + Self::Unsupported(s, b) => { + write!( + f, + "The operation is not supported on the current platform: {0}", + &s + )?; + display_error_backtrace(f, b) + } + Self::ShuttingDown => write!(f, "Shutting down!"), + #[cfg(feature = "std")] + Self::OsError(err, s, b) => { + write!(f, "OS error: {0}: {1}", &s, err)?; + display_error_backtrace(f, b) + } + Self::Unknown(s, b) => { + write!(f, "Unknown error: {0}", &s)?; + display_error_backtrace(f, b) + } + Self::InvalidCorpus(s, b) => { + write!(f, "Invalid corpus: {0}", &s)?; + display_error_backtrace(f, b) + } + Self::Runtime(s, b) => { + write!(f, "Runtime error: {0}", &s)?; + display_error_backtrace(f, b) + } + Self::InvalidInput(s, b) => { + write!(f, "Encountered an invalid input: {0}", &s)?; + display_error_backtrace(f, b) + } + } + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + fn from(err: BorrowError) -> Self { + Self::illegal_state(format!( + "Couldn't borrow from a RefCell as immutable: {err:?}" + )) + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + fn from(err: BorrowMutError) -> Self { + Self::illegal_state(format!( + "Couldn't borrow from a RefCell as mutable: {err:?}" + )) + } +} + +/// Stringify the postcard serializer error +#[cfg(all(feature = "alloc", feature = "postcard"))] +impl From for Error { + fn from(err: postcard::Error) -> Self { + Self::serialize(format!("{err:?}")) + } +} + +#[cfg(all(unix, feature = "nix"))] +impl From for Error { + fn from(err: nix::Error) -> Self { + Self::unknown(format!("Unix error: {err:?}")) + } +} + +/// Create an AFL Error from io Error +#[cfg(feature = "std")] +impl From for Error { + fn from(err: io::Error) -> Self { + Self::os_error(err, "io::Error ocurred") + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + fn from(err: FromUtf8Error) -> Self { + Self::unknown(format!("Could not convert byte / utf-8: {err:?}")) + } +} + +#[cfg(feature = "alloc")] +impl From for Error { + fn from(err: Utf8Error) -> Self { + Self::unknown(format!("Could not convert byte / utf-8: {err:?}")) + } +} + +#[cfg(feature = "std")] +impl From for Error { + fn from(_err: VarError) -> Self { + Self::illegal_state(format!("Could not get env var: {_err:?}")) + } +} + +impl From for Error { + #[allow(unused_variables)] // err is unused without std + fn from(err: ParseIntError) -> Self { + Self::unknown(format!("Failed to parse Int: {err:?}")) + } +} + +impl From for Error { + #[allow(unused_variables)] // err is unused without std + fn from(err: TryFromIntError) -> Self { + Self::illegal_state(format!("Expected conversion failed: {err:?}")) + } +} + +impl From for Error { + #[allow(unused_variables)] // err is unused without std + fn from(err: TryFromSliceError) -> Self { + Self::illegal_argument(format!("Could not convert slice: {err:?}")) + } +} + +#[cfg(windows)] +impl From for Error { + #[allow(unused_variables)] // err is unused without std + fn from(err: windows_result::Error) -> Self { + Self::unknown(format!("Windows API error: {err:?}")) + } +} + +#[cfg(feature = "python")] +impl From for Error { + fn from(err: pyo3::PyErr) -> Self { + pyo3::Python::with_gil(|py| { + if err + .matches( + py, + pyo3::types::PyType::new::(py), + ) + .unwrap() + { + Self::shutting_down() + } else { + Self::illegal_state(format!("Python exception: {err:?}")) + } + }) + } +} + +/// Trait to convert into an Owned type +pub trait IntoOwned { + /// Returns if the current type is an owned type. + #[must_use] + fn is_owned(&self) -> bool; + + /// Transfer the current type into an owned type. + #[must_use] + fn into_owned(self) -> Self; +} + +/// Can be converted to a slice +pub trait AsSlice<'a> { + /// Type of the entries of this slice + type Entry: 'a; + /// Type of the reference to this slice + type SliceRef: Deref; + + /// Convert to a slice + fn as_slice(&'a self) -> Self::SliceRef; +} + +/// Can be converted to a slice +pub trait AsSizedSlice<'a, const N: usize> { + /// Type of the entries of this slice + type Entry: 'a; + /// Type of the reference to this slice + type SliceRef: Deref; + + /// Convert to a slice + fn as_sized_slice(&'a self) -> Self::SliceRef; +} + +impl<'a, T, R: ?Sized> AsSlice<'a> for R +where + T: 'a, + R: Deref, +{ + type Entry = T; + type SliceRef = &'a [T]; + + fn as_slice(&'a self) -> Self::SliceRef { + self + } +} + +impl<'a, T, const N: usize, R: ?Sized> AsSizedSlice<'a, N> for R +where + T: 'a, + R: Deref, +{ + type Entry = T; + type SliceRef = &'a [T; N]; + + fn as_sized_slice(&'a self) -> Self::SliceRef { + self + } +} + +/// Can be converted to a mutable slice +pub trait AsSliceMut<'a>: AsSlice<'a> { + /// Type of the mutable reference to this slice + type SliceRefMut: DerefMut; + + /// Convert to a slice + fn as_slice_mut(&'a mut self) -> Self::SliceRefMut; +} + +/// Can be converted to a mutable slice +pub trait AsSizedSliceMut<'a, const N: usize>: AsSizedSlice<'a, N> { + /// Type of the mutable reference to this slice + type SliceRefMut: DerefMut; + + /// Convert to a slice + fn as_sized_slice_mut(&'a mut self) -> Self::SliceRefMut; +} + +impl<'a, T, R: ?Sized> AsSliceMut<'a> for R +where + T: 'a, + R: DerefMut, +{ + type SliceRefMut = &'a mut [T]; + + fn as_slice_mut(&'a mut self) -> Self::SliceRefMut { + &mut *self + } +} + +impl<'a, T, const N: usize, R: ?Sized> AsSizedSliceMut<'a, N> for R +where + T: 'a, + R: DerefMut, +{ + type SliceRefMut = &'a mut [T; N]; + + fn as_sized_slice_mut(&'a mut self) -> Self::SliceRefMut { + &mut *self + } +} + +/// Create an `Iterator` from a reference +pub trait AsIter<'it> { + /// The item type + type Item: 'it; + /// The ref type + type Ref: Deref; + /// The iterator type + type IntoIter: Iterator; + + /// Create an iterator from &self + fn as_iter(&'it self) -> Self::IntoIter; +} + +impl<'it, S, T> AsIter<'it> for S +where + S: AsSlice<'it, Entry = T, SliceRef = &'it [T]>, + T: 'it, +{ + type Item = S::Entry; + type Ref = &'it Self::Item; + type IntoIter = core::slice::Iter<'it, Self::Item>; + + fn as_iter(&'it self) -> Self::IntoIter { + self.as_slice().iter() + } +} + +/// Create an `Iterator` from a mutable reference +pub trait AsIterMut<'it>: AsIter<'it> { + /// The ref type + type RefMut: DerefMut; + /// The iterator type + type IntoIterMut: Iterator; + + /// Create an iterator from &mut self + fn as_iter_mut(&'it mut self) -> Self::IntoIterMut; +} + +impl<'it, S, T> AsIterMut<'it> for S +where + S: AsSliceMut<'it, Entry = T, SliceRef = &'it [T], SliceRefMut = &'it mut [T]>, + T: 'it, +{ + type RefMut = &'it mut Self::Item; + type IntoIterMut = core::slice::IterMut<'it, Self::Item>; + + fn as_iter_mut(&'it mut self) -> Self::IntoIterMut { + self.as_slice_mut().iter_mut() + } +} + +/// Has a ref count +pub trait HasRefCnt { + /// The ref count + fn refcnt(&self) -> isize; + /// The ref count, mutable + fn refcnt_mut(&mut self) -> &mut isize; +} + +/// Zero-cost way to construct [`core::num::NonZeroUsize`] at compile-time. +#[macro_export] +macro_rules! nonzero { + // TODO: Further simplify with `unwrap`/`expect` once MSRV includes + // https://github.com/rust-lang/rust/issues/67441 + ($val:expr) => { + const { + match core::num::NonZero::new($val) { + Some(x) => x, + None => panic!("Value passed to `nonzero!` was zero"), + } + } + }; +} + +/// Get a [`core::ptr::NonNull`] to a global static mut (or similar). +/// +/// The same as [`core::ptr::addr_of_mut`] or `&raw mut`, but wrapped in said [`NonNull`](core::ptr::NonNull). +#[macro_export] +macro_rules! nonnull_raw_mut { + ($val:expr) => { + // # Safety + // The pointer to a value will never be null (unless we're on an archaic OS in a CTF challenge). + unsafe { core::ptr::NonNull::new(&raw mut $val).unwrap_unchecked() } + }; +} + +/// Create a [`Vec`] of the given type with `nb_elts` elements, initialized in place. +/// The closure must initialize [`Vec`] (of size `nb_elts` * `sizeo_of::()`). +/// +/// # Safety +/// +/// The input closure should fully initialize the new [`Vec`], not leaving any uninitialized bytes. +// TODO: Use MaybeUninit API at some point. +#[cfg(feature = "alloc")] +#[expect(clippy::uninit_vec)] +pub unsafe fn vec_init(nb_elts: usize, init_fn: F) -> Result, E> +where + F: FnOnce(&mut Vec) -> Result<(), E>, +{ + unsafe { + let mut new_vec: Vec = Vec::with_capacity(nb_elts); + new_vec.set_len(nb_elts); + + init_fn(&mut new_vec)?; + + Ok(new_vec) + } +} + +/// We need fixed names for many parts of this lib. +#[cfg(feature = "alloc")] +pub trait Named { + /// Provide the name of this element. + fn name(&self) -> &Cow<'static, str>; +} + +#[cfg(feature = "alloc")] +impl Named for () { + #[inline] + fn name(&self) -> &Cow<'static, str> { + static NAME: Cow<'static, str> = Cow::Borrowed("()"); + &NAME + } +} + +/// Has a length field +pub trait HasLen { + /// The length + fn len(&self) -> usize; + + /// Returns `true` if it has no elements. + fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +#[cfg(feature = "alloc")] +impl HasLen for Vec { + #[inline] + fn len(&self) -> usize { + Vec::::len(self) + } +} + +impl HasLen for &mut T { + fn len(&self) -> usize { + self.deref().len() + } +} + +impl HasLen for (Head, Tail) +where + Tail: HasLen, +{ + #[inline] + fn len(&self) -> usize { + self.1.len() + 1 + } +} + +impl HasLen for (Tail,) +where + Tail: HasLen, +{ + #[inline] + fn len(&self) -> usize { + self.0.len() + } +} + +impl HasLen for () { + #[inline] + fn len(&self) -> usize { + 0 + } +} + +/// Trait to truncate slices and maps to a new size +pub trait Truncate { + /// Reduce the size of the slice + fn truncate(&mut self, len: usize); +} + +impl Truncate for &[T] { + fn truncate(&mut self, len: usize) { + *self = &self[..len]; + } +} + +impl Truncate for &mut [T] { + fn truncate(&mut self, len: usize) { + let value = core::mem::take(self); + let len = value.len().min(len); + let truncated = value + .get_mut(..len) + .expect("Truncate with len <= len() should always work"); + let _: &mut [T] = core::mem::replace(self, truncated); + } +} diff --git a/crates/libafl_sugar/src/forkserver.rs b/crates/libafl_sugar/src/forkserver.rs index aacbf55d985..e32fdd7ba7f 100644 --- a/crates/libafl_sugar/src/forkserver.rs +++ b/crates/libafl_sugar/src/forkserver.rs @@ -34,7 +34,6 @@ use libafl_bolts::{ AsSliceMut, StdTargetArgs, core_affinity::Cores, nonzero, - ownedref::OwnedRefMut, rands::StdRand, shmem::{ShMem, ShMemProvider, UnixShMemProvider}, tuples::{Merge, tuple_list}, @@ -271,8 +270,7 @@ impl ForkserverBytesCoverageSugar<'_> { unsafe { cmplog_shmem.write_to_env(SHM_CMPLOG_ENV_VAR).unwrap(); } - let cmpmap = - unsafe { OwnedRefMut::::from_shmem(&mut cmplog_shmem) }; + let cmpmap = unsafe { AflppCmpLogMap::from_shmem(&mut cmplog_shmem) }; let cmplog_observer = StdCmpObserver::new("cmplog", cmpmap, true); diff --git a/crates/libafl_targets/Cargo.toml b/crates/libafl_targets/Cargo.toml index 4fec14199dd..69a431fcb41 100644 --- a/crates/libafl_targets/Cargo.toml +++ b/crates/libafl_targets/Cargo.toml @@ -79,10 +79,12 @@ rustversion = "1.0.17" [dependencies] libafl = { workspace = true, features = [], default-features = false } libafl_bolts = { workspace = true, features = [] } +fast_rands = { workspace = true } +ownedref = { workspace = true } +shmem_providers = { workspace = true } libc = { workspace = true } nix = { workspace = true, optional = true } hashbrown = { workspace = true, default-features = true } -once_cell = "1.19.0" log = { workspace = true } rustversion = { workspace = true } diff --git a/crates/libafl_targets/src/call.rs b/crates/libafl_targets/src/call.rs index d0581e18e09..22a95a459a0 100644 --- a/crates/libafl_targets/src/call.rs +++ b/crates/libafl_targets/src/call.rs @@ -1,10 +1,11 @@ use core::marker::PhantomData; +use std::cell::LazyCell; use hashbrown::HashMap; use libafl::executors::hooks::ExecutorHook; -use once_cell::sync::Lazy; + /// The list of functions that this execution has observed -pub static mut FUNCTION_LIST: Lazy> = Lazy::new(HashMap::new); +pub static mut FUNCTION_LIST: LazyCell> = LazyCell::new(HashMap::new); #[unsafe(no_mangle)] /// The runtime code inserted at every callinst invokation (if you used the function-logging.cc) diff --git a/crates/libafl_targets/src/cmps/mod.rs b/crates/libafl_targets/src/cmps/mod.rs index f80491e0df3..b52eec3a685 100644 --- a/crates/libafl_targets/src/cmps/mod.rs +++ b/crates/libafl_targets/src/cmps/mod.rs @@ -12,12 +12,10 @@ use core::{ ptr, slice, }; -use libafl::{ - Error, - observers::{CmpMap, CmpValues, CmplogBytes, cmp::AflppCmpLogHeader}, -}; -use libafl_bolts::HasLen; +use libafl::observers::{CmpMap, CmpValues, CmplogBytes, cmp::AflppCmpLogHeader}; +use libafl_bolts::{Error, HasLen, ownedref::OwnedRefMut}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use shmem_providers::ShMem; pub use stages::*; use crate::{CMPLOG_MAP_H, CMPLOG_MAP_W}; @@ -510,6 +508,18 @@ impl HasLen for AflppCmpLogMap { } impl AflppCmpLogMap { + /// Returns a new [`OwnedRefMut`] to an [`AflppCmpLogMap`], pointing to the given [`ShMem`]. + /// + /// # Panics + /// Panics if the given shared mem is too small + /// + /// # Safety + /// The shared memory needs to start with a valid [`AflppCmpLogMap`] object. + /// Any use of this [`OwnedRefMut`] will dereference a pointer to the given shared memory accordingly + pub unsafe fn from_shmem(shmem: &mut SHM) -> OwnedRefMut<'_, Self> { + unsafe { OwnedRefMut::from_mut_ptr(shmem.as_mut_ptr_of().unwrap()) } + } + #[must_use] #[expect(clippy::cast_ptr_alignment)] /// Instantiate a new boxed zeroed `AflppCmpLogMap`. This should be used to create a new diff --git a/crates/libafl_targets/src/coverage.rs b/crates/libafl_targets/src/coverage.rs index 032b09794a4..03e7ac7bfc1 100644 --- a/crates/libafl_targets/src/coverage.rs +++ b/crates/libafl_targets/src/coverage.rs @@ -10,7 +10,9 @@ use alloc::borrow::Cow; #[cfg(any(target_os = "linux", target_vendor = "apple"))] -use libafl::{Error, mutators::Tokens}; +use libafl::mutators::Tokens; +#[cfg(any(target_os = "linux", target_vendor = "apple"))] +use libafl_bolts::Error; use crate::{ACCOUNTING_MAP_SIZE, EDGES_MAP_ALLOCATED_SIZE, EDGES_MAP_DEFAULT_SIZE}; diff --git a/crates/libafl_targets/src/drcov.rs b/crates/libafl_targets/src/drcov.rs index c26b202e0de..d51825fc5a9 100644 --- a/crates/libafl_targets/src/drcov.rs +++ b/crates/libafl_targets/src/drcov.rs @@ -12,7 +12,7 @@ use std::{ }; use hashbrown::HashSet; -use libafl::Error; +use libafl_bolts::Error; use rangemap::RangeMap; /// A basic block struct diff --git a/crates/libafl_targets/src/forkserver.rs b/crates/libafl_targets/src/forkserver.rs index 0620ff081f6..0c9465fef7c 100644 --- a/crates/libafl_targets/src/forkserver.rs +++ b/crates/libafl_targets/src/forkserver.rs @@ -10,22 +10,20 @@ use std::{ use libafl::executors::forkserver::FS_NEW_OPT_AUTODTCT; #[cfg(feature = "cmplog")] use libafl::executors::forkserver::SHM_CMPLOG_ENV_VAR; -use libafl::{ - Error, - executors::forkserver::{ - AFL_MAP_SIZE_ENV_VAR, FORKSRV_FD, FS_ERROR_SHM_OPEN, FS_NEW_OPT_MAPSIZE, - FS_NEW_OPT_SHDMEM_FUZZ, FS_NEW_VERSION_MAX, FS_OPT_ERROR, MAX_INPUT_SIZE_DEFAULT, - SHM_ENV_VAR, SHM_FUZZ_ENV_VAR, SHM_FUZZ_MAP_SIZE_ENV_VAR, SHMEM_FUZZ_HDR_SIZE, - }, +use libafl::executors::forkserver::{ + AFL_MAP_SIZE_ENV_VAR, FORKSRV_FD, FS_ERROR_SHM_OPEN, FS_NEW_OPT_MAPSIZE, + FS_NEW_OPT_SHDMEM_FUZZ, FS_NEW_VERSION_MAX, FS_OPT_ERROR, MAX_INPUT_SIZE_DEFAULT, SHM_ENV_VAR, + SHM_FUZZ_ENV_VAR, SHM_FUZZ_MAP_SIZE_ENV_VAR, SHMEM_FUZZ_HDR_SIZE, }; use libafl_bolts::{ + Error, os::{ChildHandle, ForkResult}, - shmem::{ShMem, ShMemId, ShMemProvider}, }; use nix::{ sys::signal::{SigHandler, Signal}, unistd::Pid, }; +use shmem_providers::{ShMem, ShMemId, ShMemProvider}; #[cfg(feature = "cmplog_extended_instrumentation")] use crate::cmps::EXTENDED_CMPLOG_MAP_PTR; diff --git a/crates/libafl_targets/src/libfuzzer/mutators.rs b/crates/libafl_targets/src/libfuzzer/mutators.rs index 52bfb634ee2..bfa804c66d9 100644 --- a/crates/libafl_targets/src/libfuzzer/mutators.rs +++ b/crates/libafl_targets/src/libfuzzer/mutators.rs @@ -6,8 +6,8 @@ use alloc::{ }; use core::{cell::RefCell, marker::PhantomData, ops::Deref}; +use fast_rands::Rand; use libafl::{ - Error, corpus::{Corpus, CorpusId}, inputs::{BytesInput, HasMutatorBytes, ResizableMutator}, mutators::{ @@ -16,7 +16,7 @@ use libafl::{ random_corpus_id_with_disabled, state::{HasCorpus, HasMaxSize, HasRand}, }; -use libafl_bolts::{AsSlice, HasLen, Named, rands::Rand}; +use libafl_core::{AsSlice, Error, HasLen, Named}; unsafe extern "C" { fn libafl_targets_has_libfuzzer_custom_mutator() -> bool; diff --git a/crates/libafl_targets/src/libfuzzer/observers/oom.rs b/crates/libafl_targets/src/libfuzzer/observers/oom.rs index bc8f3b1522e..4bdfe2676c0 100644 --- a/crates/libafl_targets/src/libfuzzer/observers/oom.rs +++ b/crates/libafl_targets/src/libfuzzer/observers/oom.rs @@ -6,12 +6,11 @@ use core::{ }; use libafl::{ - Error, executors::ExitKind, feedbacks::{Feedback, StateInitializer}, observers::Observer, }; -use libafl_bolts::Named; +use libafl_core::{Error, Named}; use libc::SIGABRT; use serde::{Deserialize, Serialize}; diff --git a/crates/libafl_targets/src/sancov_8bit.rs b/crates/libafl_targets/src/sancov_8bit.rs index 9ce2fa4ab6a..33d3f6b9c7c 100644 --- a/crates/libafl_targets/src/sancov_8bit.rs +++ b/crates/libafl_targets/src/sancov_8bit.rs @@ -1,7 +1,8 @@ //! [`LLVM` `8-bit-counters`](https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards) runtime for `LibAFL`. use alloc::vec::Vec; -use libafl_bolts::{AsSlice, AsSliceMut, ownedref::OwnedMutSlice}; +use libafl_core::{AsSlice, AsSliceMut}; +use ownedref::OwnedMutSlice; /// A [`Vec`] of `8-bit-counters` maps for multiple modules. /// They are initialized by calling [`__sanitizer_cov_8bit_counters_init`]( diff --git a/crates/ll_mp/Cargo.toml b/crates/ll_mp/Cargo.toml new file mode 100644 index 00000000000..4ccdbe91f5a --- /dev/null +++ b/crates/ll_mp/Cargo.toml @@ -0,0 +1,93 @@ +[package] +name = "ll_mp" +version.workspace = true +authors = [ + "Andrea Fioraldi ", + "Dominik Maier ", +] +description = "A library for low level message passing" +documentation = "https://docs.rs/libafl" +repository = "https://github.com/AFLplusplus/LibAFL/" +readme = "./README.md" +license = "MIT OR Apache-2.0" +keywords = ["os", "ipc", "no-std"] +edition = "2024" +rust-version = "1.87" +categories = ["os", "no-std"] + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +[features] +default = ["std", "alloc", "gzip", "llmp_compression", "llmp_small_maps"] +document-features = ["dep:document-features"] + +#! # Feature Flags +#! ### General Features + +## Enables features that need rust's `std` lib to work, like print, env, ... support +std = [ + "libafl_core/std", + "shmem_providers/std", + "alloc", + "nix", + "serial_test", + "hostname", +] + +## Enables all features that allocate in `no_std` +alloc = [ + "exceptional/alloc", + "libafl_core/alloc", + "libafl_core/serde", + "postcard", + "shmem_providers/alloc", +] + +## Enables gzip compression in certain parts of the lib +gzip = ["miniz_oxide", "alloc"] + +#! ### LLMP features + +## If set, llmp will bind to 0.0.0.0, allowing cross-device communication. Binds to localhost by default. +llmp_bind_public = ["alloc"] + +## Enables llmp compression using GZip +llmp_compression = ["alloc", "gzip"] + +## Enables debug output for LLMP (also needs a `logger` installed) +llmp_debug = ["alloc", "std"] + +## Reduces the initial map size for llmp +llmp_small_maps = ["alloc"] + + +[build-dependencies] +rustversion = { workspace = true } + +[dev-dependencies] + +[dependencies] +# Document all features of this crate (for `cargo doc`) +document-features = { workspace = true, optional = true } +exceptional = { workspace = true } +hostname = { version = "0.4.0", optional = true } # Is there really no gethostname in the stdlib? +libafl_core = { workspace = true } +log = { workspace = true } +miniz_oxide = { version = "0.8.0", optional = true } +nix = { workspace = true, features = ["socket"], optional = true } +no_std_time = { workspace = true } +postcard = { workspace = true, optional = true } # no_std compatible serde serialization format +serde = { workspace = true, features = ["alloc", "derive"] } +serial_test = { workspace = true, optional = true } +shmem_providers = { workspace = true } +tuple_list = { workspace = true } + +[[example]] +name = "llmp_test" +path = "./examples/llmp_test/main.rs" +required-features = ["std"] + +[lints] +workspace = true diff --git a/crates/ll_mp/LICENSE-APACHE b/crates/ll_mp/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/ll_mp/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/ll_mp/LICENSE-MIT b/crates/ll_mp/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/ll_mp/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/ll_mp/README.md b/crates/ll_mp/README.md new file mode 100644 index 00000000000..0a638ac1244 --- /dev/null +++ b/crates/ll_mp/README.md @@ -0,0 +1,58 @@ +# LibAFL_bolts: OS and Fuzzer Dev's Libary Collection. + + LibAFL logo + +The `libafl_bolts` crate exposes a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just fuzzers completely different to LibAFL. +Some cross-platform things in bolts include (but are not limited to): + +* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable +* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation +* LLMP: A fast, lock-free IPC mechanism via SharedMap +* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores +* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/)) +* MiniBSOD: get and print information about the current process state including important registers. +* Tuples: Haskel-like compile-time tuple lists +* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork` + +LibAFL_bolts is written and maintained by + +* [Andrea Fioraldi](https://twitter.com/andreafioraldi) +* [Dominik Maier](https://twitter.com/domenuk) +* [s1341](https://twitter.com/srubenst1341) +* [Dongjia Zhang](https://github.com/tokatoka) +* [Addison Crump](https://github.com/addisoncrump) + +## Contributing + +For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 + +Even though we will gladly assist you in finishing up your PR, try to +- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26)) +- run `cargo nightly fmt` on your code before pushing +- check the output of `cargo clippy --all` or `./clippy.sh` +- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code. + +Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. + +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + + +
+ + +Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled +using the respective feature in each crate when it is present, such as the +'agpl' feature of the libafl crate. + diff --git a/crates/libafl_bolts/examples/llmp_test/main.rs b/crates/ll_mp/examples/llmp_test/main.rs similarity index 91% rename from crates/libafl_bolts/examples/llmp_test/main.rs rename to crates/ll_mp/examples/llmp_test/main.rs index 2ac4d09413b..8cdf3e3aaf2 100644 --- a/crates/libafl_bolts/examples/llmp_test/main.rs +++ b/crates/ll_mp/examples/llmp_test/main.rs @@ -11,13 +11,13 @@ use core::time::Duration; #[cfg(all(feature = "std", not(target_os = "haiku")))] use std::{thread, time}; -use libafl_bolts::llmp::{LlmpBrokerInner, LlmpMsgHookResult}; #[cfg(all(feature = "std", not(target_os = "haiku")))] -use libafl_bolts::{ - ClientId, Error, SimpleStderrLogger, - llmp::{self, Flags, LlmpHook, Tag}, - shmem::{ShMemProvider, StdShMemProvider}, -}; +use libafl_core::{ClientId, Error}; +#[cfg(all(feature = "std", not(target_os = "haiku")))] +use ll_mp::{self, Flags, LlmpHook, Tag}; +use ll_mp::{LlmpBrokerInner, LlmpMsgHookResult}; +#[cfg(all(feature = "std", not(target_os = "haiku")))] +use shmem_providers::{ShMemProvider, StdShMemProvider}; use tuple_list::tuple_list; #[cfg(all(feature = "std", not(target_os = "haiku")))] @@ -35,13 +35,10 @@ const BROKER_TIMEOUT: Duration = Duration::from_secs(10); #[cfg(all(feature = "std", not(target_os = "haiku")))] const SLEEP_BETWEEN_FORWARDS: Duration = Duration::from_millis(5); -#[cfg(all(feature = "std", not(target_os = "haiku")))] -static LOGGER: SimpleStderrLogger = SimpleStderrLogger::new(); - #[cfg(all(feature = "std", not(target_os = "haiku")))] fn adder_loop(port: u16) -> Result<(), Box> { let shmem_provider = StdShMemProvider::new()?; - let mut client = llmp::LlmpClient::create_attach_to_tcp(shmem_provider, port)?; + let mut client = ll_mp::LlmpClient::create_attach_to_tcp(shmem_provider, port)?; let mut last_result: u32 = 0; let mut current_result: u32 = 0; loop { @@ -78,7 +75,7 @@ fn adder_loop(port: u16) -> Result<(), Box> { #[cfg(all(feature = "std", not(target_os = "haiku")))] fn large_msg_loop(port: u16) -> Result<(), Box> { - let mut client = llmp::LlmpClient::create_attach_to_tcp(StdShMemProvider::new()?, port)?; + let mut client = ll_mp::LlmpClient::create_attach_to_tcp(StdShMemProvider::new()?, port)?; #[cfg(not(target_vendor = "apple"))] let meg_buf = vec![1u8; 1 << 20]; @@ -170,7 +167,7 @@ fn main() { fn main() -> Result<(), Box> { /* The main node has a broker, and a few worker threads */ - use libafl_bolts::llmp::Broker; + use ll_mp::Broker; let mode = std::env::args() .nth(1) @@ -185,13 +182,13 @@ fn main() -> Result<(), Box> { .unwrap_or_else(|| "4242".into()) .parse::()?; - log::set_logger(&LOGGER).unwrap(); + // log::set_logger(..) log::set_max_level(log::LevelFilter::Trace); println!("Launching in mode {mode} on port {port}"); match mode.as_str() { "broker" => { - let mut broker = llmp::LlmpBroker::new( + let mut broker = ll_mp::LlmpBroker::new( StdShMemProvider::new()?, tuple_list!(LlmpExampleHook::new()), )?; @@ -201,7 +198,7 @@ fn main() -> Result<(), Box> { broker.loop_with_timeouts(BROKER_TIMEOUT, Some(SLEEP_BETWEEN_FORWARDS)); } "b2b" => { - let mut broker = llmp::LlmpBroker::new( + let mut broker = ll_mp::LlmpBroker::new( StdShMemProvider::new()?, tuple_list!(LlmpExampleHook::new()), )?; @@ -212,7 +209,7 @@ fn main() -> Result<(), Box> { } "ctr" => { let mut client = - llmp::LlmpClient::create_attach_to_tcp(StdShMemProvider::new()?, port)?; + ll_mp::LlmpClient::create_attach_to_tcp(StdShMemProvider::new()?, port)?; let mut counter: u32 = 0; loop { counter = counter.wrapping_add(1); @@ -229,7 +226,7 @@ fn main() -> Result<(), Box> { } "exiting" => { let mut client = - llmp::LlmpClient::create_attach_to_tcp(StdShMemProvider::new()?, port)?; + ll_mp::LlmpClient::create_attach_to_tcp(StdShMemProvider::new()?, port)?; for i in 0..10_u32 { client.send_buf(_TAG_SIMPLE_U32_V1, &i.to_le_bytes())?; println!("Exiting Client writing {i}"); diff --git a/crates/libafl_bolts/src/llmp.rs b/crates/ll_mp/src/lib.rs similarity index 98% rename from crates/libafl_bolts/src/llmp.rs rename to crates/ll_mp/src/lib.rs index 2b55ca5a6e1..7470d5ee9e4 100644 --- a/crates/libafl_bolts/src/llmp.rs +++ b/crates/ll_mp/src/lib.rs @@ -56,12 +56,60 @@ For broker2broker communication, all messages are forwarded via network sockets. Check out the `llmp_test` example in ./examples, or build it with `cargo run --example llmp_test`. */ +#![doc = include_str!("../../../README.md")] +/*! */ +#![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +#![no_std] +#![cfg_attr(not(test), warn( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr(test, deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results +))] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] + +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; #[cfg(feature = "std")] use alloc::boxed::Box; #[cfg(feature = "std")] use alloc::string::ToString; -use alloc::{string::String, vec::Vec}; +use alloc::{string::String, vec, vec::Vec}; #[cfg(feature = "std")] use core::net::SocketAddr; #[cfg(not(target_pointer_width = "64"))] @@ -79,6 +127,8 @@ use core::{ sync::atomic::{AtomicU16, Ordering, fence}, time::Duration, }; +#[cfg(all(debug_assertions, feature = "llmp_debug", feature = "std"))] +use std::backtrace::Backtrace; #[cfg(feature = "std")] use std::{ env, @@ -88,28 +138,25 @@ use std::{ thread, }; -#[cfg(all(debug_assertions, feature = "llmp_debug", feature = "std"))] -use backtrace::Backtrace; +#[cfg(all(unix, not(miri)))] +use exceptional::unix_signals::setup_signal_handler; +#[cfg(unix)] +use exceptional::unix_signals::{Signal, SignalHandler, siginfo_t, ucontext_t}; +#[cfg(all(windows, feature = "std"))] +use exceptional::windows_exceptions::{CtrlHandler, setup_ctrl_handler}; +#[cfg(feature = "std")] +use libafl_core::IP_LOCALHOST; +use libafl_core::{ClientId, Error, format}; #[cfg(all(unix, feature = "std"))] #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] use nix::sys::socket::{self, sockopt::ReusePort}; +#[cfg(feature = "std")] +use no_std_time::current_time; use serde::{Deserialize, Serialize}; +use shmem_providers::{ShMem, ShMemDescription, ShMemId, ShMemProvider}; #[cfg(feature = "std")] use tuple_list::tuple_list; -#[cfg(all(unix, not(miri)))] -use crate::os::unix_signals::setup_signal_handler; -#[cfg(unix)] -use crate::os::unix_signals::{Signal, SignalHandler, siginfo_t, ucontext_t}; -#[cfg(all(windows, feature = "std"))] -use crate::os::windows_exceptions::{CtrlHandler, setup_ctrl_handler}; -use crate::{ - ClientId, Error, - shmem::{ShMem, ShMemDescription, ShMemId, ShMemProvider}, -}; -#[cfg(feature = "std")] -use crate::{IP_LOCALHOST, current_time}; - /// The max number of pages a [`client`] may have mapped that were not yet read by the [`broker`] /// Usually, this value should not exceed `1`, else the broker cannot keep up with the amount of incoming messages. /// Instead of increasing this value, you may consider sending new messages at a lower rate, else your Sender will eventually `OOM`. @@ -276,6 +323,7 @@ pub enum TcpRequest { }, } +#[cfg(feature = "std")] impl TryFrom<&Vec> for TcpRequest { type Error = Error; @@ -284,6 +332,7 @@ impl TryFrom<&Vec> for TcpRequest { } } +#[cfg(feature = "std")] impl TryFrom> for TcpRequest { type Error = Error; @@ -305,6 +354,7 @@ pub struct TcpRemoteNewMessage { payload: Vec, } +#[cfg(feature = "std")] impl TryFrom<&Vec> for TcpRemoteNewMessage { type Error = Error; @@ -313,6 +363,7 @@ impl TryFrom<&Vec> for TcpRemoteNewMessage { } } +#[cfg(feature = "std")] impl TryFrom> for TcpRemoteNewMessage { type Error = Error; @@ -349,6 +400,7 @@ pub enum TcpResponse { }, } +#[cfg(feature = "std")] impl TryFrom<&Vec> for TcpResponse { type Error = Error; @@ -357,6 +409,7 @@ impl TryFrom<&Vec> for TcpResponse { } } +#[cfg(feature = "std")] impl TryFrom> for TcpResponse { type Error = Error; @@ -1130,7 +1183,7 @@ where #[cfg(all(feature = "llmp_debug", feature = "std"))] { #[cfg(debug_assertions)] - let bt = Backtrace::new(); + let bt = Backtrace::capture(); #[cfg(not(debug_assertions))] let bt = ""; let shm = self.out_shmems.last().unwrap(); @@ -2006,7 +2059,7 @@ where #[cfg(feature = "llmp_debug")] //{ //#[cfg(debug_assertions)] - //let bt = Backtrace::new(); + //let bt = Backtrace::capture(); //#[cfg(not(debug_assertions))] //let bt = ""; log::info!( @@ -2437,7 +2490,7 @@ impl Brokers { /// 5 millis of sleep can't hurt to keep busywait not at 100% #[cfg(feature = "std")] pub fn loop_with_timeouts(&mut self, timeout: Duration, sleep_time: Option) { - use super::current_milliseconds; + use no_std_time::current_milliseconds; #[cfg(any(all(unix, not(miri)), all(windows, feature = "std")))] Self::setup_handlers(); @@ -2613,7 +2666,7 @@ where /// 5 millis of sleep can't hurt to keep busywait not at 100% #[cfg(feature = "std")] pub fn loop_with_timeouts(&mut self, timeout: Duration, sleep_time: Option) { - use super::current_milliseconds; + use no_std_time::current_milliseconds; #[cfg(any(all(unix, not(miri)), all(windows, feature = "std")))] Self::setup_handlers(); @@ -3871,16 +3924,16 @@ impl LlmpClient { mod tests { use core::time::Duration; - use std::thread::sleep; + use std::{thread::sleep, vec}; use serial_test::serial; + use shmem_providers::{ShMemProvider, StdShMemProvider}; use super::{ LlmpClient, LlmpConnection::{self, IsBroker, IsClient}, Tag, }; - use crate::shmem::{ShMemProvider, StdShMemProvider}; #[test] #[serial] diff --git a/crates/minibsod/Cargo.toml b/crates/minibsod/Cargo.toml new file mode 100644 index 00000000000..80091363a2a --- /dev/null +++ b/crates/minibsod/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "minibsod" +version.workspace = true +authors = [ + "Andrea Fioraldi ", + "Dominik Maier ", +] +description = "A library to dump current register states, etc., on crash" +documentation = "https://docs.rs/libafl" +repository = "https://github.com/AFLplusplus/LibAFL/" +readme = "./README.md" +license = "MIT OR Apache-2.0" +keywords = ["os", "bsod"] +edition = "2024" +rust-version = "1.87" +categories = ["os"] + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +[features] +document-features = ["dep:document-features"] + +[build-dependencies] +rustversion = { workspace = true } + +[dev-dependencies] + +[dependencies] +# Document all features of this crate (for `cargo doc`) +document-features = { workspace = true, optional = true } +libc = { workspace = true } +exceptional = { workspace = true, features = ["alloc"] } + +[target.'cfg(target_vendor = "apple")'.dependencies] +mach2 = "0.4.2" + +[lints] +workspace = true diff --git a/crates/minibsod/LICENSE-APACHE b/crates/minibsod/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/minibsod/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/minibsod/LICENSE-MIT b/crates/minibsod/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/minibsod/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/minibsod/README.md b/crates/minibsod/README.md new file mode 100644 index 00000000000..0a638ac1244 --- /dev/null +++ b/crates/minibsod/README.md @@ -0,0 +1,58 @@ +# LibAFL_bolts: OS and Fuzzer Dev's Libary Collection. + + LibAFL logo + +The `libafl_bolts` crate exposes a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just fuzzers completely different to LibAFL. +Some cross-platform things in bolts include (but are not limited to): + +* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable +* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation +* LLMP: A fast, lock-free IPC mechanism via SharedMap +* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores +* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/)) +* MiniBSOD: get and print information about the current process state including important registers. +* Tuples: Haskel-like compile-time tuple lists +* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork` + +LibAFL_bolts is written and maintained by + +* [Andrea Fioraldi](https://twitter.com/andreafioraldi) +* [Dominik Maier](https://twitter.com/domenuk) +* [s1341](https://twitter.com/srubenst1341) +* [Dongjia Zhang](https://github.com/tokatoka) +* [Addison Crump](https://github.com/addisoncrump) + +## Contributing + +For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 + +Even though we will gladly assist you in finishing up your PR, try to +- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26)) +- run `cargo nightly fmt` on your code before pushing +- check the output of `cargo clippy --all` or `./clippy.sh` +- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code. + +Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. + +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + + +
+ + +Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled +using the respective feature in each crate when it is present, such as the +'agpl' feature of the libafl crate. + diff --git a/crates/libafl_bolts/src/minibsod.rs b/crates/minibsod/src/lib.rs similarity index 97% rename from crates/libafl_bolts/src/minibsod.rs rename to crates/minibsod/src/lib.rs index b837b70306c..ac39b6183f5 100644 --- a/crates/libafl_bolts/src/minibsod.rs +++ b/crates/minibsod/src/lib.rs @@ -1,5 +1,53 @@ //! Implements a mini-bsod generator. //! It dumps all important registers and prints a stacktrace. +#![doc = include_str!("../../../README.md")] +/*! */ +#![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +#![cfg_attr(not(test), warn( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr(test, deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results +))] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] + +#[macro_use] +extern crate std; +#[doc(hidden)] +pub extern crate alloc; #[cfg(unix)] use alloc::vec::Vec; @@ -9,6 +57,8 @@ use std::io::{BufWriter, Write}; #[cfg(any(target_os = "solaris", target_os = "illumos"))] use std::process::Command; +#[cfg(unix)] +use exceptional::unix_signals::{Signal, ucontext_t}; #[cfg(unix)] use libc::siginfo_t; #[cfg(target_vendor = "apple")] @@ -23,9 +73,6 @@ use mach2::{ #[cfg(windows)] use windows::Win32::System::Diagnostics::Debug::{CONTEXT, EXCEPTION_POINTERS}; -#[cfg(unix)] -use crate::os::unix_signals::{Signal, ucontext_t}; - /// Necessary info to print a mini-BSOD. #[derive(Debug)] #[cfg(unix)] @@ -1119,7 +1166,7 @@ pub fn generate_minibsod( writeln!(writer, "Received signal {signal}")?; } writeln!(writer, "{:━^100}", " BACKTRACE ")?; - writeln!(writer, "{:?}", backtrace::Backtrace::new())?; + writeln!(writer, "{:?}", std::backtrace::Backtrace::force_capture())?; writeln!(writer, "{:━^100}", " MAPS ")?; write_minibsod(writer) } diff --git a/crates/no_std_time/Cargo.toml b/crates/no_std_time/Cargo.toml new file mode 100644 index 00000000000..514d7de4069 --- /dev/null +++ b/crates/no_std_time/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "no_std_time" +version.workspace = true +authors = [ + "Andrea Fioraldi ", + "Dominik Maier ", +] +description = "Time measurments that work in no_std environments" +documentation = "https://docs.rs/libafl" +repository = "https://github.com/AFLplusplus/LibAFL/" +readme = "./README.md" +license = "MIT OR Apache-2.0" +keywords = ["os", "shmem", "no-std"] +edition = "2024" +rust-version = "1.87" +categories = ["embedded", "os", "no-std"] + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +[features] +default = ["std", "alloc"] +document-features = ["dep:document-features"] + +#! # Feature Flags +#! ### General Features + +## Enables features that need rust's `std` lib to work, like print, env, ... support +std = ["alloc"] + +## Enables all features that allocate in `no_std` +alloc = [] + +[build-dependencies] +rustversion = { workspace = true } + +[dev-dependencies] + +[dependencies] +# Document all features of this crate (for `cargo doc`) +document-features = { workspace = true, optional = true } + +[lints] +workspace = true diff --git a/crates/no_std_time/LICENSE-APACHE b/crates/no_std_time/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/no_std_time/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/no_std_time/LICENSE-MIT b/crates/no_std_time/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/no_std_time/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/no_std_time/README.md b/crates/no_std_time/README.md new file mode 100644 index 00000000000..0a638ac1244 --- /dev/null +++ b/crates/no_std_time/README.md @@ -0,0 +1,58 @@ +# LibAFL_bolts: OS and Fuzzer Dev's Libary Collection. + + LibAFL logo + +The `libafl_bolts` crate exposes a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just fuzzers completely different to LibAFL. +Some cross-platform things in bolts include (but are not limited to): + +* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable +* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation +* LLMP: A fast, lock-free IPC mechanism via SharedMap +* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores +* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/)) +* MiniBSOD: get and print information about the current process state including important registers. +* Tuples: Haskel-like compile-time tuple lists +* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork` + +LibAFL_bolts is written and maintained by + +* [Andrea Fioraldi](https://twitter.com/andreafioraldi) +* [Dominik Maier](https://twitter.com/domenuk) +* [s1341](https://twitter.com/srubenst1341) +* [Dongjia Zhang](https://github.com/tokatoka) +* [Addison Crump](https://github.com/addisoncrump) + +## Contributing + +For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 + +Even though we will gladly assist you in finishing up your PR, try to +- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26)) +- run `cargo nightly fmt` on your code before pushing +- check the output of `cargo clippy --all` or `./clippy.sh` +- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code. + +Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. + +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + + +
+ + +Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled +using the respective feature in each crate when it is present, such as the +'agpl' feature of the libafl crate. + diff --git a/crates/no_std_time/src/lib.rs b/crates/no_std_time/src/lib.rs new file mode 100644 index 00000000000..339a9c71560 --- /dev/null +++ b/crates/no_std_time/src/lib.rs @@ -0,0 +1,143 @@ +//! Time functions that can be used on `no_std` +#![doc = include_str!("../../../README.md")] +/*! */ +#![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +#![no_std] +#![cfg_attr(not(test), warn( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr(test, deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results +))] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] + +#[cfg(feature = "std")] +#[macro_use] +extern crate std; +#[cfg(feature = "alloc")] +#[macro_use] +#[doc(hidden)] +pub extern crate alloc; + +use core::time; + +pub mod time_counters; + +#[cfg(feature = "alloc")] +use alloc::string::String; +#[cfg(feature = "std")] +use std::time::{SystemTime, UNIX_EPOCH}; + +/// Format a `Duration` into a HMS string +#[cfg(feature = "alloc")] +#[must_use] +pub fn format_duration(duration: &time::Duration) -> String { + const MINS_PER_HOUR: u64 = 60; + const HOURS_PER_DAY: u64 = 24; + + const SECS_PER_MINUTE: u64 = 60; + const SECS_PER_HOUR: u64 = SECS_PER_MINUTE * MINS_PER_HOUR; + const SECS_PER_DAY: u64 = SECS_PER_HOUR * HOURS_PER_DAY; + + let total_secs = duration.as_secs(); + let secs = total_secs % SECS_PER_MINUTE; + + if total_secs < SECS_PER_MINUTE { + format!("{secs}s") + } else { + let mins = (total_secs / SECS_PER_MINUTE) % MINS_PER_HOUR; + if total_secs < SECS_PER_HOUR { + format!("{mins}m-{secs}s") + } else { + let hours = (total_secs / SECS_PER_HOUR) % HOURS_PER_DAY; + if total_secs < SECS_PER_DAY { + format!("{hours}h-{mins}m-{secs}s") + } else { + let days = total_secs / SECS_PER_DAY; + format!("{days}days {hours}h-{mins}m-{secs}s") + } + } + } +} + +#[cfg(all(any(doctest, test), not(feature = "std")))] +/// Provide custom time in `no_std` tests. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn external_current_millis() -> u64 { + // TODO: use "real" time here + 1000 +} + +/// Current time +#[cfg(feature = "std")] +#[must_use] +#[inline] +pub fn current_time() -> time::Duration { + SystemTime::now().duration_since(UNIX_EPOCH).unwrap() +} + +// external defined function in case of `no_std` +// +// Define your own `external_current_millis()` function via `extern "C"` +// which is linked into the binary and called from here. +#[cfg(all(not(any(doctest, test)), not(feature = "std")))] +unsafe extern "C" { + //#[unsafe(no_mangle)] + fn external_current_millis() -> u64; +} + +/// Current time (fixed fallback for `no_std`) +#[cfg(not(feature = "std"))] +#[inline] +#[must_use] +pub fn current_time() -> time::Duration { + let millis = unsafe { external_current_millis() }; + time::Duration::from_millis(millis) +} + +/// Gets current nanoseconds since [`UNIX_EPOCH`] +#[must_use] +#[inline] +pub fn current_nanos() -> u64 { + current_time().as_nanos() as u64 +} + +/// Gets current milliseconds since [`UNIX_EPOCH`] +#[must_use] +#[inline] +pub fn current_milliseconds() -> u64 { + current_time().as_millis() as u64 +} diff --git a/crates/libafl_bolts/src/cpu.rs b/crates/no_std_time/src/time_counters.rs similarity index 100% rename from crates/libafl_bolts/src/cpu.rs rename to crates/no_std_time/src/time_counters.rs diff --git a/crates/ownedref/Cargo.toml b/crates/ownedref/Cargo.toml new file mode 100644 index 00000000000..d6b564f5f24 --- /dev/null +++ b/crates/ownedref/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "ownedref" +version.workspace = true +authors = [ + "Andrea Fioraldi ", + "Dominik Maier ", +] +description = "Library to pass around references that will be owned types on deserialization" +documentation = "https://docs.rs/libafl" +repository = "https://github.com/AFLplusplus/LibAFL/" +readme = "./README.md" +license = "MIT OR Apache-2.0" +keywords = ["os", "shmem", "no-std"] +edition = "2024" +rust-version = "1.87" +categories = ["embedded", "os", "no-std"] + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +[features] +default = ["std", "alloc"] +document-features = ["dep:document-features"] + +#! # Feature Flags +#! ### General Features + +## Enables features that need rust's `std` lib to work, like print, env, ... support +std = ["alloc"] + +## Enables all features that allocate in `no_std` +alloc = [] + +[build-dependencies] +rustversion = { workspace = true } + +[dev-dependencies] + +[dependencies] +# Document all features of this crate (for `cargo doc`) +document-features = { workspace = true, optional = true } + +libafl_core = { workspace = true } +serde = { workspace = true } + +[lints] +workspace = true diff --git a/crates/ownedref/LICENSE-APACHE b/crates/ownedref/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/ownedref/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/ownedref/LICENSE-MIT b/crates/ownedref/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/ownedref/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/ownedref/README.md b/crates/ownedref/README.md new file mode 100644 index 00000000000..0a638ac1244 --- /dev/null +++ b/crates/ownedref/README.md @@ -0,0 +1,58 @@ +# LibAFL_bolts: OS and Fuzzer Dev's Libary Collection. + + LibAFL logo + +The `libafl_bolts` crate exposes a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just fuzzers completely different to LibAFL. +Some cross-platform things in bolts include (but are not limited to): + +* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable +* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation +* LLMP: A fast, lock-free IPC mechanism via SharedMap +* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores +* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/)) +* MiniBSOD: get and print information about the current process state including important registers. +* Tuples: Haskel-like compile-time tuple lists +* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork` + +LibAFL_bolts is written and maintained by + +* [Andrea Fioraldi](https://twitter.com/andreafioraldi) +* [Dominik Maier](https://twitter.com/domenuk) +* [s1341](https://twitter.com/srubenst1341) +* [Dongjia Zhang](https://github.com/tokatoka) +* [Addison Crump](https://github.com/addisoncrump) + +## Contributing + +For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 + +Even though we will gladly assist you in finishing up your PR, try to +- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26)) +- run `cargo nightly fmt` on your code before pushing +- check the output of `cargo clippy --all` or `./clippy.sh` +- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code. + +Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. + +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + + +
+ + +Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled +using the respective feature in each crate when it is present, such as the +'agpl' feature of the libafl crate. + diff --git a/crates/libafl_bolts/src/ownedref.rs b/crates/ownedref/src/lib.rs similarity index 95% rename from crates/libafl_bolts/src/ownedref.rs rename to crates/ownedref/src/lib.rs index 10a3f7de857..13d70ace810 100644 --- a/crates/libafl_bolts/src/ownedref.rs +++ b/crates/ownedref/src/lib.rs @@ -1,5 +1,57 @@ //! Wrappers that abstracts references (or pointers) and owned data accesses. -// The serialization is towards owned, allowing to serialize pointers without troubles. +//! The serialization is towards owned, allowing to serialize pointers without troubles. +#![doc = include_str!("../../../README.md")] +/*! */ +#![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +#![no_std] +#![cfg_attr(not(test), warn( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr(test, deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results +))] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] + +#[cfg(feature = "std")] +#[macro_use] +extern crate std; +#[doc(hidden)] +pub extern crate alloc; + +pub mod subrange; use alloc::{boxed::Box, vec::Vec}; use core::{ @@ -11,18 +63,16 @@ use core::{ slice::{Iter, IterMut, SliceIndex}, }; +use libafl_core::{AsSizedSlice, AsSizedSliceMut, AsSlice, AsSliceMut, IntoOwned, Truncate}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::{ - AsSizedSlice, AsSizedSliceMut, AsSlice, AsSliceMut, IntoOwned, Truncate, shmem::ShMem, -}; - /// Constant size array visitor for serde deserialization. /// Mostly taken from mod arrays { use alloc::{boxed::Box, fmt, vec::Vec}; use core::{convert::TryInto, marker::PhantomData}; + use libafl_core::format; use serde::{ Deserialize, Deserializer, de::{SeqAccess, Visitor}, @@ -89,23 +139,6 @@ impl UnsafeMarker { } } -impl Truncate for &[T] { - fn truncate(&mut self, len: usize) { - *self = &self[..len]; - } -} - -impl Truncate for &mut [T] { - fn truncate(&mut self, len: usize) { - let value = core::mem::take(self); - let len = value.len().min(len); - let truncated = value - .get_mut(..len) - .expect("Truncate with len <= len() should always work"); - let _: &mut [T] = core::mem::replace(self, truncated); - } -} - /// Wrap a reference and convert to a [`Box`] on serialize #[derive(Debug)] pub enum OwnedRef<'a, T> @@ -183,18 +216,6 @@ impl OwnedRef<'_, T> where T: Sized + 'static, { - /// Returns a new [`OwnedRef`], pointing to the given [`ShMem`]. - /// - /// # Panics - /// Panics if the given shared mem is too small - /// - /// # Safety - /// The shared memory needs to start with a valid object of type `T`. - /// Any use of this [`OwnedRef`] will dereference a pointer to the shared memory accordingly. - pub unsafe fn from_shmem(shmem: &mut SHM) -> Self { - unsafe { Self::from_ptr(shmem.as_mut_ptr_of().unwrap()) } - } - /// Returns a new [`OwnedRef`], owning the given value. pub fn owned(val: T) -> Self { Self::Owned(Box::new(val)) @@ -316,18 +337,6 @@ impl OwnedRefMut<'_, T> where T: Sized + 'static, { - /// Returns a new [`OwnedRefMut`], pointing to the given [`ShMem`]. - /// - /// # Panics - /// Panics if the given shared mem is too small - /// - /// # Safety - /// The shared memory needs to start with a valid object of type `T`. - /// Any use of this [`OwnedRefMut`] will dereference a pointer to the shared memory accordingly. - pub unsafe fn from_shmem(shmem: &mut SHM) -> Self { - unsafe { Self::from_mut_ptr(shmem.as_mut_ptr_of().unwrap()) } - } - /// Returns a new [`OwnedRefMut`], owning the given value. pub fn owned(val: T) -> Self { Self::Owned(Box::new(val)) diff --git a/crates/libafl_bolts/src/subrange.rs b/crates/ownedref/src/subrange.rs similarity index 99% rename from crates/libafl_bolts/src/subrange.rs rename to crates/ownedref/src/subrange.rs index 1228723a3a6..65d787fe970 100644 --- a/crates/libafl_bolts/src/subrange.rs +++ b/crates/ownedref/src/subrange.rs @@ -6,10 +6,9 @@ use core::{ ops::{Bound, Range, RangeBounds}, }; -use crate::{ - HasLen, - ownedref::{OwnedMutSlice, OwnedSlice}, -}; +use libafl_core::HasLen; + +use crate::{OwnedMutSlice, OwnedSlice}; /// An immutable contiguous subslice of a byte slice. /// It is mostly useful to cheaply wrap a subslice of a given input. @@ -35,6 +34,20 @@ pub struct SubRangeMutSlice<'a, T> { range: Range, } +impl HasLen for SubRangeSlice<'_, T> { + #[inline] + fn len(&self) -> usize { + self.range.len() + } +} + +impl HasLen for SubRangeMutSlice<'_, T> { + #[inline] + fn len(&self) -> usize { + self.range.len() + } +} + /// Slice wrapper keeping track of the current read position. /// Convenient wrapper when the slice must be split in multiple sub-slices and read sequentially. #[derive(Debug)] @@ -96,20 +109,6 @@ impl<'a, T> From<&'a [T]> for SliceReader<'a, T> { } } -impl HasLen for SubRangeSlice<'_, T> { - #[inline] - fn len(&self) -> usize { - self.range.len() - } -} - -impl HasLen for SubRangeMutSlice<'_, T> { - #[inline] - fn len(&self) -> usize { - self.range.len() - } -} - /// Gets the relevant concrete start index from [`RangeBounds`] (inclusive) pub fn start_index(range: &R) -> usize where diff --git a/crates/serde_anymap/Cargo.toml b/crates/serde_anymap/Cargo.toml new file mode 100644 index 00000000000..7a476bd94e9 --- /dev/null +++ b/crates/serde_anymap/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "serde_anymap" +description = "A map that can retrieve values by type - and is SerDe serializable" +version.workspace = true +authors = [ + "Andrea Fioraldi ", + "Dominik Maier ", +] +documentation = "https://docs.rs/libafl" +repository = "https://github.com/AFLplusplus/LibAFL/" +readme = "./README.md" +license = "MIT OR Apache-2.0" +keywords = ["os", "shmem", "no-std"] +edition = "2024" +rust-version = "1.87" +categories = ["embedded", "os", "no-std"] + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +[features] +default = ["std", "alloc", "stable_anymap"] +document-features = ["dep:document-features"] + +#! # Feature Flags +#! ### General Features + +## Enables features that need rust's `std` lib to work, like print, env, ... support +std = ["alloc", "libafl_core/std"] + +## Enables all features that allocate in `no_std` +alloc = ["erased-serde/alloc", "libafl_core/alloc"] + +## Create keys that are more stable between compilations +stable_anymap = [] +serdeany_autoreg = ["ctor"] + +[build-dependencies] +rustversion = { workspace = true } + +[dev-dependencies] + +[dependencies] +# Document all features of this crate (for `cargo doc`) +document-features = { workspace = true, optional = true } + +ahash = { workspace = true } +ctor = { workspace = true, optional = true } +erased-serde = { workspace = true, default-features = false } +hashbrown = { workspace = true } +libafl_core = { workspace = true } +postcard = { workspace = true } +serde = { workspace = true } +static_assertions = { workspace = true } + +[lints] +workspace = true diff --git a/crates/serde_anymap/LICENSE-APACHE b/crates/serde_anymap/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/serde_anymap/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/serde_anymap/LICENSE-MIT b/crates/serde_anymap/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/serde_anymap/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/serde_anymap/README.md b/crates/serde_anymap/README.md new file mode 100644 index 00000000000..0a638ac1244 --- /dev/null +++ b/crates/serde_anymap/README.md @@ -0,0 +1,58 @@ +# LibAFL_bolts: OS and Fuzzer Dev's Libary Collection. + + LibAFL logo + +The `libafl_bolts` crate exposes a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just fuzzers completely different to LibAFL. +Some cross-platform things in bolts include (but are not limited to): + +* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable +* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation +* LLMP: A fast, lock-free IPC mechanism via SharedMap +* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores +* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/)) +* MiniBSOD: get and print information about the current process state including important registers. +* Tuples: Haskel-like compile-time tuple lists +* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork` + +LibAFL_bolts is written and maintained by + +* [Andrea Fioraldi](https://twitter.com/andreafioraldi) +* [Dominik Maier](https://twitter.com/domenuk) +* [s1341](https://twitter.com/srubenst1341) +* [Dongjia Zhang](https://github.com/tokatoka) +* [Addison Crump](https://github.com/addisoncrump) + +## Contributing + +For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 + +Even though we will gladly assist you in finishing up your PR, try to +- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26)) +- run `cargo nightly fmt` on your code before pushing +- check the output of `cargo clippy --all` or `./clippy.sh` +- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code. + +Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. + +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + + +
+ + +Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled +using the respective feature in each crate when it is present, such as the +'agpl' feature of the libafl crate. + diff --git a/crates/libafl_bolts/src/anymap.rs b/crates/serde_anymap/src/anymap.rs similarity index 100% rename from crates/libafl_bolts/src/anymap.rs rename to crates/serde_anymap/src/anymap.rs diff --git a/crates/serde_anymap/src/lib.rs b/crates/serde_anymap/src/lib.rs new file mode 100644 index 00000000000..d2138e79050 --- /dev/null +++ b/crates/serde_anymap/src/lib.rs @@ -0,0 +1,58 @@ +//! A map that can retrieve values by type - and is `Serde` serializable. +#![doc = include_str!("../../../README.md")] +/*! */ +#![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +#![no_std] +#![cfg_attr(not(test), warn( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr(test, deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results +))] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] + +#[cfg(feature = "std")] +#[macro_use] +extern crate std; +#[doc(hidden)] +pub extern crate alloc; + +#[cfg(feature = "serdeany_autoreg")] +#[doc(hidden)] +pub use ctor; + +pub mod anymap; +pub mod serdeany; diff --git a/crates/libafl_bolts/src/serdeany.rs b/crates/serde_anymap/src/serdeany.rs similarity index 99% rename from crates/libafl_bolts/src/serdeany.rs rename to crates/serde_anymap/src/serdeany.rs index 0c712f54fd4..bbed4ac0b75 100644 --- a/crates/libafl_bolts/src/serdeany.rs +++ b/crates/serde_anymap/src/serdeany.rs @@ -24,7 +24,7 @@ pub type TypeRepr = u128; pub type TypeRepr = Cow<'static, str>; /// Error string when no types at all have been registered yet. -pub(crate) const ERR_EMPTY_TYPES_REGISTER: &str = "Empty types registry. Please enable the `serdeany_autoreg` feature in libafl_bolts or register all required types manually using RegistryBuilder::register()."; +pub(crate) const ERR_EMPTY_TYPES_REGISTER: &str = "Empty types registry. Please enable the `serdeany_autoreg` feature for serde_anymap or register all required types manually using RegistryBuilder::register()."; #[cfg(not(feature = "stable_anymap"))] fn type_repr() -> TypeRepr @@ -124,14 +124,12 @@ pub mod serdeany_registry { HashMap, hash_map::{Values, ValuesMut}, }; + use libafl_core::{Error, format}; use serde::{Deserialize, Serialize, de}; - use crate::{ - Error, - serdeany::{ - DeserializeCallback, DeserializeCallbackSeed, SerdeAny, TypeRepr, type_repr, - type_repr_owned, - }, + use crate::serdeany::{ + DeserializeCallback, DeserializeCallbackSeed, SerdeAny, TypeRepr, type_repr, + type_repr_owned, }; /// A [`HashMap`] that maps from [`TypeRepr`] to a deserializer and its [`TypeRepr`]. diff --git a/crates/shmem_providers/Cargo.toml b/crates/shmem_providers/Cargo.toml new file mode 100644 index 00000000000..c4422c08b08 --- /dev/null +++ b/crates/shmem_providers/Cargo.toml @@ -0,0 +1,69 @@ +[package] +name = "shmem_providers" +version.workspace = true +authors = [ + "Andrea Fioraldi ", + "Dominik Maier ", +] +description = "Platform independent shared memory providers for Windows, Linux, Android, iOS, ..." +documentation = "https://docs.rs/libafl" +repository = "https://github.com/AFLplusplus/LibAFL/" +readme = "./README.md" +license = "MIT OR Apache-2.0" +keywords = ["os", "shmem", "no-std"] +edition = "2024" +rust-version = "1.87" +categories = ["embedded", "os", "no-std"] + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +[features] +default = ["std", "alloc"] +document-features = ["dep:document-features"] + +#! # Feature Flags +#! ### General Features + +## Enables features that need rust's `std` lib to work, like print, env, ... support +std = ["libafl_core/std", "uds", "nix", "libafl_core/nix", "serial_test"] + +## Enables all features that allocate in `no_std` +alloc = ["libafl_core/alloc", "hashbrown", "postcard", "libafl_core/postcard"] + +[build-dependencies] +rustversion = { workspace = true } + +[dev-dependencies] + +[dependencies] +# Document all features of this crate (for `cargo doc`) +document-features = { workspace = true, optional = true } +fast_rands = { workspace = true } +libafl_core = { workspace = true } +log = { workspace = true } +postcard = { workspace = true, optional = true } # no_std compatible serde serialization format +serde = { workspace = true, features = ["derive"] } +hashbrown = { workspace = true, features = [ + "serde", + "ahash", +], default-features = false, optional = true } # A faster hashmap, nostd compatible + +# optional-dev deps (change when target.'cfg(accessible(::std))'.test-dependencies will be stable) +serial_test = { workspace = true, optional = true, default-features = false, features = [ + "logging", +] } + +[target.'cfg(unix)'.dependencies] +libc = { workspace = true } +uds = { version = "0.4.2", optional = true, default-features = false } +nix = { workspace = true, optional = true, default-features = false, features = [ + "fs", + "signal", + "socket", + "poll", +] } + +[lints] +workspace = true diff --git a/crates/shmem_providers/LICENSE-APACHE b/crates/shmem_providers/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/shmem_providers/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/shmem_providers/LICENSE-MIT b/crates/shmem_providers/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/shmem_providers/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/shmem_providers/README.md b/crates/shmem_providers/README.md new file mode 100644 index 00000000000..0a638ac1244 --- /dev/null +++ b/crates/shmem_providers/README.md @@ -0,0 +1,58 @@ +# LibAFL_bolts: OS and Fuzzer Dev's Libary Collection. + + LibAFL logo + +The `libafl_bolts` crate exposes a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just fuzzers completely different to LibAFL. +Some cross-platform things in bolts include (but are not limited to): + +* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable +* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation +* LLMP: A fast, lock-free IPC mechanism via SharedMap +* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores +* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/)) +* MiniBSOD: get and print information about the current process state including important registers. +* Tuples: Haskel-like compile-time tuple lists +* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork` + +LibAFL_bolts is written and maintained by + +* [Andrea Fioraldi](https://twitter.com/andreafioraldi) +* [Dominik Maier](https://twitter.com/domenuk) +* [s1341](https://twitter.com/srubenst1341) +* [Dongjia Zhang](https://github.com/tokatoka) +* [Addison Crump](https://github.com/addisoncrump) + +## Contributing + +For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 + +Even though we will gladly assist you in finishing up your PR, try to +- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26)) +- run `cargo nightly fmt` on your code before pushing +- check the output of `cargo clippy --all` or `./clippy.sh` +- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code. + +Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. + +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + + +
+ + +Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled +using the respective feature in each crate when it is present, such as the +'agpl' feature of the libafl crate. + diff --git a/crates/libafl_bolts/src/shmem.rs b/crates/shmem_providers/src/lib.rs similarity index 97% rename from crates/libafl_bolts/src/shmem.rs rename to crates/shmem_providers/src/lib.rs index c4dda359427..5eff4766362 100644 --- a/crates/libafl_bolts/src/shmem.rs +++ b/crates/shmem_providers/src/lib.rs @@ -1,5 +1,65 @@ //! A generic shared memory region to be used by any functions (queues or feedbacks //! too.) +#![doc = include_str!("../../../README.md")] +/*! */ +#![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +#![no_std] +#![cfg_attr(not(test), warn( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr(test, deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results +))] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] + +#[cfg(feature = "std")] +#[macro_use] +extern crate std; +#[doc(hidden)] +pub extern crate alloc; + +#[cfg(feature = "alloc")] +use alloc::vec; + +pub use libafl_core::Error; + +#[cfg(all(unix, feature = "std"))] +pub mod pipes; +#[cfg(all(feature = "std", unix, not(target_os = "haiku")))] +pub mod unix_shmem_server; #[cfg(feature = "alloc")] use alloc::{rc::Rc, string::ToString, vec::Vec}; @@ -29,11 +89,10 @@ pub use unix_shmem::{UnixShMem, UnixShMemProvider}; #[cfg(all(windows, feature = "std"))] pub use win32_shmem::{Win32ShMem, Win32ShMemProvider}; -use crate::Error; #[cfg(all(unix, feature = "std", not(target_os = "haiku")))] -use crate::os::pipes::Pipe; +use crate::pipes::Pipe; #[cfg(all(feature = "std", unix, not(target_os = "haiku")))] -pub use crate::os::unix_shmem_server::{ServedShMem, ServedShMemProvider, ShMemService}; +pub use crate::unix_shmem_server::{ServedShMem, ServedShMemProvider, ShMemService}; /// The standard sharedmem provider #[cfg(all(windows, feature = "std"))] @@ -683,16 +742,14 @@ pub mod unix_shmem { }; use std::{io, path::Path, process}; + use fast_rands::{Rand, StdRand}; + use libafl_core::Error; use libc::{ c_int, c_uchar, close, fcntl, ftruncate, mmap, munmap, shm_open, shm_unlink, shmat, shmctl, shmdt, shmget, }; - use crate::{ - Error, - rands::{Rand, StdRand}, - shmem::{ShMem, ShMemId, ShMemProvider}, - }; + use crate::{ShMem, ShMemId, ShMemProvider}; /// The max number of bytes used when generating names for [`MmapShMem`]s. pub const MAX_MMAP_FILENAME_LEN: usize = 20; @@ -1003,7 +1060,7 @@ pub mod unix_shmem { } fn new_shmem(&mut self, map_size: usize) -> Result { - let mut rand = StdRand::with_seed(crate::rands::random_seed()); + let mut rand = StdRand::with_seed(fast_rands::random_seed()); let id = rand.next() as u32; let mut full_file_name = format!("libafl_{}_{}", process::id(), id); // leave one byte space for the null byte. @@ -1180,10 +1237,7 @@ pub mod unix_shmem { open, }; - use crate::{ - Error, - shmem::{ShMem, ShMemId, ShMemProvider}, - }; + use crate::{Error, ShMem, ShMemId, ShMemProvider}; /// An ashmem based impl for linux/android #[derive(Debug, Clone)] @@ -1401,10 +1455,7 @@ pub mod unix_shmem { use libc::{MAP_SHARED, PROT_READ, PROT_WRITE, close, fstat, ftruncate, mmap, munmap}; use nix::sys::memfd::{MFdFlags, memfd_create}; - use crate::{ - Error, - shmem::{ShMem, ShMemId, ShMemProvider}, - }; + use crate::{Error, ShMem, ShMemId, ShMemProvider}; /// An memfd based impl for linux/android #[cfg(unix)] @@ -1798,7 +1849,7 @@ impl ShMemCursor { where SHM: DerefMut, { - use crate::AsSliceMut; + use libafl_core::AsSliceMut; &mut (self.inner.as_slice_mut()[self.pos..]) } } @@ -1852,7 +1903,7 @@ where let effective_new_pos = match pos { std::io::SeekFrom::Start(s) => s, std::io::SeekFrom::End(offset) => { - use crate::AsSlice; + use libafl_core::AsSlice; let map_len = self.inner.as_slice().len(); let signed_pos = i64::try_from(map_len).unwrap(); let effective = signed_pos.checked_add(offset).unwrap(); @@ -1877,12 +1928,10 @@ where #[cfg(all(feature = "std", not(target_os = "haiku")))] #[cfg(test)] mod tests { + use libafl_core::{AsSlice, AsSliceMut, Error}; use serial_test::serial; - use crate::{ - AsSlice, AsSliceMut, Error, - shmem::{ShMemProvider, StdShMemProvider}, - }; + use crate::{ShMemProvider, StdShMemProvider}; #[test] #[serial] @@ -1906,7 +1955,7 @@ mod tests { process::{Command, Stdio}, }; - use crate::shmem::{MmapShMemProvider, ShMem as _, ShMemId}; + use crate::{MmapShMemProvider, ShMem as _, ShMemId}; // relies on the fact that the ID in a ShMemDescription is always a string for MmapShMem match env::var("SHMEM_SIZE") { diff --git a/crates/libafl_bolts/src/os/pipes.rs b/crates/shmem_providers/src/pipes.rs similarity index 100% rename from crates/libafl_bolts/src/os/pipes.rs rename to crates/shmem_providers/src/pipes.rs diff --git a/crates/libafl_bolts/src/os/unix_shmem_server.rs b/crates/shmem_providers/src/unix_shmem_server.rs similarity index 99% rename from crates/libafl_bolts/src/os/unix_shmem_server.rs rename to crates/shmem_providers/src/unix_shmem_server.rs index 04fcb145bf8..98cb6114c42 100644 --- a/crates/libafl_bolts/src/os/unix_shmem_server.rs +++ b/crates/shmem_providers/src/unix_shmem_server.rs @@ -47,10 +47,7 @@ use serde::{Deserialize, Serialize}; #[cfg(all(unix, feature = "std"))] use uds::{UnixListenerExt, UnixSocketAddr, UnixStreamExt}; -use crate::{ - Error, - shmem::{ShMem, ShMemDescription, ShMemId, ShMemProvider}, -}; +use crate::{Error, ShMem, ShMemDescription, ShMemId, ShMemProvider}; /// The default server name for our abstract shmem server #[cfg(all(unix, not(target_vendor = "apple")))] diff --git a/crates/tuple_list_ex/Cargo.toml b/crates/tuple_list_ex/Cargo.toml new file mode 100644 index 00000000000..d13ef86759b --- /dev/null +++ b/crates/tuple_list_ex/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "tuple_list_ex" +version.workspace = true +authors = [ + "Andrea Fioraldi ", + "Dominik Maier ", +] +description = "Useful Haskel-like extensions for the tuple_list crate" +documentation = "https://docs.rs/libafl" +repository = "https://github.com/AFLplusplus/LibAFL/" +readme = "./README.md" +license = "MIT OR Apache-2.0" +keywords = ["os", "no-std"] +edition = "2024" +rust-version = "1.87" +categories = ["embedded", "os", "no-std"] + +[package.metadata.docs.rs] +features = ["document-features"] +all-features = true + +[features] +default = ["std", "alloc"] +document-features = ["dep:document-features"] + +#! # Feature Flags +#! ### General Features + +## Enables features that need rust's `std` lib to work, like print, env, ... support +std = ["alloc"] + +## Enables all features that allocate in `no_std` +alloc = ["ownedref"] + +## Enables serde support for tuple lists +serde = ["alloc", "dep:serde"] + +[build-dependencies] +rustversion = { workspace = true } + +[dev-dependencies] + +[dependencies] +# Document all features of this crate (for `cargo doc`) +document-features = { workspace = true, optional = true } + +libafl_core = { workspace = true } + +serde = { workspace = true, default-features = false, features = [ + "derive", + "alloc", +], optional = true } +tuple_list = { workspace = true } +typeid = { workspace = true } +ownedref = { workspace = true, optional = true } + +[lints] +workspace = true diff --git a/crates/tuple_list_ex/LICENSE-APACHE b/crates/tuple_list_ex/LICENSE-APACHE new file mode 120000 index 00000000000..965b606f331 --- /dev/null +++ b/crates/tuple_list_ex/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/crates/tuple_list_ex/LICENSE-MIT b/crates/tuple_list_ex/LICENSE-MIT new file mode 120000 index 00000000000..76219eb72e8 --- /dev/null +++ b/crates/tuple_list_ex/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/crates/tuple_list_ex/README.md b/crates/tuple_list_ex/README.md new file mode 100644 index 00000000000..0a638ac1244 --- /dev/null +++ b/crates/tuple_list_ex/README.md @@ -0,0 +1,58 @@ +# LibAFL_bolts: OS and Fuzzer Dev's Libary Collection. + + LibAFL logo + +The `libafl_bolts` crate exposes a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just fuzzers completely different to LibAFL. +Some cross-platform things in bolts include (but are not limited to): + +* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable +* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation +* LLMP: A fast, lock-free IPC mechanism via SharedMap +* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores +* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/)) +* MiniBSOD: get and print information about the current process state including important registers. +* Tuples: Haskel-like compile-time tuple lists +* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork` + +LibAFL_bolts is written and maintained by + +* [Andrea Fioraldi](https://twitter.com/andreafioraldi) +* [Dominik Maier](https://twitter.com/domenuk) +* [s1341](https://twitter.com/srubenst1341) +* [Dongjia Zhang](https://github.com/tokatoka) +* [Addison Crump](https://github.com/addisoncrump) + +## Contributing + +For bugs, feel free to open issues or contact us directly. Thank you for your support. <3 + +Even though we will gladly assist you in finishing up your PR, try to +- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26)) +- run `cargo nightly fmt` on your code before pushing +- check the output of `cargo clippy --all` or `./clippy.sh` +- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code. + +Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help. + +#### License + + +Licensed under either of Apache License, Version +2.0 or MIT license at your option. + + +
+ + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this crate by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. + + +
+ + +Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled +using the respective feature in each crate when it is present, such as the +'agpl' feature of the libafl crate. + diff --git a/crates/libafl_bolts/src/tuples.rs b/crates/tuple_list_ex/src/lib.rs similarity index 91% rename from crates/libafl_bolts/src/tuples.rs rename to crates/tuple_list_ex/src/lib.rs index 87d8840f967..7ff1a8f63f7 100644 --- a/crates/libafl_bolts/src/tuples.rs +++ b/crates/tuple_list_ex/src/lib.rs @@ -1,4 +1,55 @@ //! Compiletime lists/tuples used throughout the `LibAFL` universe +#![doc = include_str!("../../../README.md")] +/*! */ +#![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +#![no_std] +#![cfg_attr(not(test), warn( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + //unused_results +))] +#![cfg_attr(test, deny( + missing_debug_implementations, + missing_docs, + //trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_must_use, + //unused_results +))] +#![cfg_attr( + test, + deny( + bad_style, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true + ) +)] + +#[cfg(feature = "std")] +#[macro_use] +extern crate std; +#[cfg(feature = "alloc")] +#[doc(hidden)] +pub extern crate alloc; #[cfg(feature = "alloc")] use alloc::{borrow::Cow, vec::Vec}; @@ -11,16 +62,11 @@ use core::{ ops::{Deref, DerefMut, Index, IndexMut}, }; -#[cfg(feature = "alloc")] +use libafl_core::Named; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; pub use tuple_list::{TupleList, tuple_list, tuple_list_type}; -use crate::HasLen; -#[cfg(feature = "alloc")] -use crate::Named; -#[cfg(any(feature = "xxh3", feature = "alloc"))] -use crate::hash_std; - /// Returns if the type `T` is equal to `U`, ignoring lifetimes. #[must_use] pub fn type_eq() -> bool { @@ -110,85 +156,6 @@ where const LEN: usize = 1 + Tail::LEN; } -impl HasLen for (Head, Tail) -where - Tail: HasLen, -{ - #[inline] - fn len(&self) -> usize { - self.1.len() + 1 - } -} - -impl HasLen for (Tail,) -where - Tail: HasLen, -{ - #[inline] - fn len(&self) -> usize { - self.0.len() - } -} - -impl HasLen for () { - #[inline] - fn len(&self) -> usize { - 0 - } -} - -/// Finds the `const_name` and `name_id` -pub trait HasNameId { - /// Gets the `const_name` for this entry - fn const_name(&self) -> &'static str; - - /// Gets the `name_id` for this entry - fn name_id(&self) -> u64 { - hash_std(self.const_name().as_bytes()) - } -} - -/// Gets the id and `const_name` for the given index in a tuple -pub trait HasNameIdTuple: HasConstLen { - /// Gets the `const_name` for the entry at the given index - fn const_name_for(&self, index: usize) -> Option<&'static str>; - - /// Gets the `name_id` for the entry at the given index - fn name_id_for(&self, index: usize) -> Option; -} - -impl HasNameIdTuple for () { - fn const_name_for(&self, _index: usize) -> Option<&'static str> { - None - } - - fn name_id_for(&self, _index: usize) -> Option { - None - } -} - -impl HasNameIdTuple for (Head, Tail) -where - Head: HasNameId, - Tail: HasNameIdTuple, -{ - fn const_name_for(&self, index: usize) -> Option<&'static str> { - if index == 0 { - Some(self.0.const_name()) - } else { - self.1.const_name_for(index - 1) - } - } - - fn name_id_for(&self, index: usize) -> Option { - if index == 0 { - Some(self.0.name_id()) - } else { - self.1.name_id_for(index - 1) - } - } -} - /// Returns the first element with the given type pub trait MatchFirstType { /// Returns the first element with the given type as borrow, or [`None`] @@ -403,15 +370,6 @@ impl NamedTuple for () { } } -#[cfg(feature = "alloc")] -impl Named for () { - #[inline] - fn name(&self) -> &Cow<'static, str> { - static NAME: Cow<'static, str> = Cow::Borrowed("Empty"); - &NAME - } -} - #[cfg(feature = "alloc")] impl NamedTuple for (Head, Tail) where @@ -496,11 +454,11 @@ pub trait Handled: Named { impl Handled for N where N: Named {} /// Object with the type T and the name associated with its concrete value -#[derive(Serialize, Deserialize)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg(feature = "alloc")] pub struct Handle { name: Cow<'static, str>, - #[serde(skip)] + #[cfg_attr(feature = "serde", serde(skip))] phantom: PhantomData, } @@ -835,7 +793,7 @@ macro_rules! tuple_for_each_mut { #[macro_export] macro_rules! map_tuple_list_type { ($Tuple:ty, $Mapper:ty) => { - <$Tuple as $crate::tuples::Map<$Mapper>>::MapResult + <$Tuple as $crate::Map<$Mapper>>::MapResult }; } @@ -870,13 +828,13 @@ macro_rules! merge_tuple_list_type { // Base case: when only two types are provided, apply the Merge trait directly ($Type1:ty, $Type2:ty) => { - <$Type1 as $crate::tuples::Merge<$Type2>>::MergeResult + <$Type1 as $crate::Merge<$Type2>>::MergeResult }; // Recursive case: when more than two types are provided ($Type1:ty, $Type2:ty, $( $rest:ty ),+) => { merge_tuple_list_type!( - <$Type1 as $crate::tuples::Merge<$Type2>>::MergeResult, + <$Type1 as $crate::Merge<$Type2>>::MergeResult, $( $rest ),+ ) }; @@ -915,11 +873,11 @@ impl PlusOne for (Head, Tail) where mod test { use core::marker::PhantomData; + #[cfg(feature = "alloc")] + use ownedref::OwnedMutSlice; use tuple_list::{tuple_list, tuple_list_type}; - #[cfg(feature = "alloc")] - use crate::ownedref::OwnedMutSlice; - use crate::tuples::{Map, MappingFunctor, Merge, type_eq}; + use crate::{Map, MappingFunctor, Merge, type_eq}; #[test] // for type name tests diff --git a/fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs b/fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs index f840803134e..5173ed5259d 100644 --- a/fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs +++ b/fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor/src/main.rs @@ -55,11 +55,15 @@ pub fn main() { }; // Create a stacktrace observer let mut bt = shmem_provider.new_on_shmem::>(None).unwrap(); - let bt_observer = BacktraceObserver::new( - "BacktraceObserver", - unsafe { OwnedRefMut::from_shmem(&mut bt) }, - libafl::observers::HarnessType::Child, - ); + // # Safety + // We just created the shmem and it's large enough. + let bt_observer = unsafe { + BacktraceObserver::from_shmem( + "BacktraceObserver", + &mut bt, + libafl::observers::HarnessType::Child, + ) + }; // Feedback to rate the interestingness of an input, obtained by ANDing the interestingness of both feedbacks let mut feedback = MaxMapFeedback::new(&observer); diff --git a/fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_fork_executor/src/main.rs b/fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_fork_executor/src/main.rs index 43c3c9a5019..dae8d591f46 100644 --- a/fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_fork_executor/src/main.rs +++ b/fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_fork_executor/src/main.rs @@ -67,12 +67,15 @@ pub fn main() { // Create an observation channel using the signals map let observer = unsafe { StdMapObserver::from_mut_ptr("signals", signals_ptr, signals_len) }; - // Create a stacktrace observer - let bt_observer = BacktraceObserver::new( - "BacktraceObserver", - unsafe { OwnedRefMut::from_shmem(&mut bt) }, - libafl::observers::HarnessType::Child, - ); + // # Safety + // We just created the shmem and it's large enough. + let bt_observer = unsafe { + BacktraceObserver::from_shmem( + "BacktraceObserver", + &mut bt, + libafl::observers::HarnessType::Child, + ) + }; // Feedback to rate the interestingness of an input, obtained by ANDing the interestingness of both feedbacks let mut feedback = MaxMapFeedback::new(&observer); diff --git a/fuzzers/forkserver/fuzzbench_forkserver/src/main.rs b/fuzzers/forkserver/fuzzbench_forkserver/src/main.rs index 1e547fd16d1..99fc78698d9 100644 --- a/fuzzers/forkserver/fuzzbench_forkserver/src/main.rs +++ b/fuzzers/forkserver/fuzzbench_forkserver/src/main.rs @@ -356,7 +356,7 @@ fn fuzz( unsafe { cmplog_shmem.write_to_env(SHM_CMPLOG_ENV_VAR).unwrap(); } - let cmpmap = unsafe { OwnedRefMut::::from_shmem(&mut cmplog_shmem) }; + let cmpmap = unsafe { AflppCmpLogMap::from_shmem(&mut cmplog_shmem) }; let cmplog_observer = StdCmpObserver::new("cmplog", cmpmap, true); diff --git a/fuzzers/forkserver/fuzzbench_forkserver_cmplog/src/main.rs b/fuzzers/forkserver/fuzzbench_forkserver_cmplog/src/main.rs index 3775a3bc335..2e6d5661093 100644 --- a/fuzzers/forkserver/fuzzbench_forkserver_cmplog/src/main.rs +++ b/fuzzers/forkserver/fuzzbench_forkserver_cmplog/src/main.rs @@ -359,7 +359,7 @@ fn fuzz( unsafe { cmplog_shmem.write_to_env(SHM_CMPLOG_ENV_VAR).unwrap(); } - let cmpmap = unsafe { OwnedRefMut::from_shmem(&mut cmplog_shmem) }; + let cmpmap = unsafe { AflppCmpLogMap::from_shmem(&mut cmplog_shmem) }; let cmplog_observer = AflppCmpLogObserver::new("cmplog", cmpmap, true); let cmplog_ref = cmplog_observer.handle(); diff --git a/fuzzers/forkserver/fuzzbench_forkserver_sand/src/main.rs b/fuzzers/forkserver/fuzzbench_forkserver_sand/src/main.rs index a3b06f7ff09..6d42988324c 100644 --- a/fuzzers/forkserver/fuzzbench_forkserver_sand/src/main.rs +++ b/fuzzers/forkserver/fuzzbench_forkserver_sand/src/main.rs @@ -405,7 +405,7 @@ fn fuzz( unsafe { cmplog_shmem.write_to_env(SHM_CMPLOG_ENV_VAR).unwrap(); } - let cmpmap = unsafe { OwnedRefMut::::from_shmem(&mut cmplog_shmem) }; + let cmpmap = unsafe { AflppCmpLogMap::from_shmem(&mut cmplog_shmem) }; let cmplog_observer = StdCmpObserver::new("cmplog", cmpmap, true); diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index 8e31f621943..e3aa8394d6c 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -481,7 +481,7 @@ define_run_client!(state, mgr, fuzzer_dir, core_id, opt, is_main_node, { unsafe { cmplog_shmem.write_to_env(SHM_CMPLOG_ENV_VAR).unwrap(); } - let cmpmap = unsafe { OwnedRefMut::from_shmem(&mut cmplog_shmem) }; + let cmpmap = unsafe { AflppCmpLogMap::from_shmem(&mut cmplog_shmem) }; // Create the CmpLog observer. let cmplog_observer = AflppCmpLogObserver::new("cmplog", cmpmap, true); diff --git a/fuzzers/fuzz_anything/baby_no_std/Cargo.lock b/fuzzers/fuzz_anything/baby_no_std/Cargo.lock index 526ce7b47e0..7c26be0e911 100644 --- a/fuzzers/fuzz_anything/baby_no_std/Cargo.lock +++ b/fuzzers/fuzz_anything/baby_no_std/Cargo.lock @@ -118,6 +118,24 @@ dependencies = [ "typeid", ] +[[package]] +name = "exceptional" +version = "0.15.3" +dependencies = [ + "libafl_core", + "libc", + "num_enum", + "rustversion", +] + +[[package]] +name = "fast_rands" +version = "0.15.3" +dependencies = [ + "rustversion", + "serde", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -139,8 +157,10 @@ dependencies = [ "const_panic", "hashbrown", "libafl_bolts", + "libafl_core", "libc", "libm", + "ll_mp", "log", "meminterval", "num-traits", @@ -158,23 +178,40 @@ version = "0.15.3" dependencies = [ "ahash", "erased-serde", + "exceptional", + "fast_rands", "hashbrown", + "libafl_core", "libc", + "ll_mp", "log", "mach2", + "no_std_time", "num_enum", - "once_cell", + "ownedref", "postcard", "rustversion", "serde", + "serde_anymap", + "shmem_providers", "static_assertions", "tuple_list", + "tuple_list_ex", "typeid", "winapi", "windows", "windows-result", ] +[[package]] +name = "libafl_core" +version = "0.15.3" +dependencies = [ + "postcard", + "rustversion", + "serde", +] + [[package]] name = "libc" version = "0.2.174" @@ -187,6 +224,21 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +[[package]] +name = "ll_mp" +version = "0.15.3" +dependencies = [ + "exceptional", + "libafl_core", + "log", + "no_std_time", + "postcard", + "rustversion", + "serde", + "shmem_providers", + "tuple_list", +] + [[package]] name = "log" version = "0.4.27" @@ -212,6 +264,13 @@ dependencies = [ "serde", ] +[[package]] +name = "no_std_time" +version = "0.15.3" +dependencies = [ + "rustversion", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -248,6 +307,15 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "ownedref" +version = "0.15.3" +dependencies = [ + "libafl_core", + "rustversion", + "serde", +] + [[package]] name = "postcard" version = "1.1.2" @@ -293,6 +361,20 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_anymap" +version = "0.15.3" +dependencies = [ + "ahash", + "erased-serde", + "hashbrown", + "libafl_core", + "postcard", + "rustversion", + "serde", + "static_assertions", +] + [[package]] name = "serde_derive" version = "1.0.219" @@ -304,6 +386,20 @@ dependencies = [ "syn", ] +[[package]] +name = "shmem_providers" +version = "0.15.3" +dependencies = [ + "fast_rands", + "hashbrown", + "libafl_core", + "libc", + "log", + "postcard", + "rustversion", + "serde", +] + [[package]] name = "static-alloc" version = "0.2.6" @@ -356,6 +452,18 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "141fb9f71ee586d956d7d6e4d5a9ef8e946061188520140f7591b668841d502e" +[[package]] +name = "tuple_list_ex" +version = "0.15.3" +dependencies = [ + "libafl_core", + "ownedref", + "rustversion", + "serde", + "tuple_list", + "typeid", +] + [[package]] name = "typeid" version = "1.0.3" diff --git a/fuzzers/structure_aware/baby_fuzzer_nautilus/Cargo.lock b/fuzzers/structure_aware/baby_fuzzer_nautilus/Cargo.lock index 24e08429a0e..c49934634a5 100644 --- a/fuzzers/structure_aware/baby_fuzzer_nautilus/Cargo.lock +++ b/fuzzers/structure_aware/baby_fuzzer_nautilus/Cargo.lock @@ -52,7 +52,7 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "baby_fuzzer_nautilus" -version = "0.15.2" +version = "0.15.3" dependencies = [ "libafl", "libafl_bolts", @@ -76,11 +76,22 @@ dependencies = [ [[package]] name = "bincode" -version = "1.3.3" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" dependencies = [ + "bincode_derive", "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", ] [[package]] @@ -101,6 +112,15 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "build_id2" +version = "0.15.3" +dependencies = [ + "ahash", + "rustversion", + "uuid", +] + [[package]] name = "bumpalo" version = "3.17.0" @@ -157,6 +177,16 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2459fc9262a1aa204eb4b5764ad4f189caec88aea9634389c0a25f8be7f6265e" +[[package]] +name = "core_affinity2" +version = "0.15.3" +dependencies = [ + "libafl_core", + "libc", + "rustversion", + "serde", +] + [[package]] name = "ctor" version = "0.4.2" @@ -210,15 +240,33 @@ dependencies = [ "typeid", ] +[[package]] +name = "exceptional" +version = "0.15.3" +dependencies = [ + "libafl_core", + "libc", + "num_enum", + "rustversion", +] + +[[package]] +name = "fast_rands" +version = "0.15.3" +dependencies = [ + "rand_core", + "rustversion", + "serde", +] + [[package]] name = "fastbloom" -version = "0.9.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27cea6e7f512d43b098939ff4d5a5d6fe3db07971e1d05176fe26c642d33f5b8" +checksum = "29ec576c163744bef8707859f6aeb322bcf56b8da61215d99f77d6e33160ff01" dependencies = [ "getrandom", "siphasher", - "wide 0.7.32 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -288,7 +336,7 @@ dependencies = [ [[package]] name = "libafl" -version = "0.15.2" +version = "0.15.3" dependencies = [ "ahash", "arbitrary-int", @@ -301,9 +349,11 @@ dependencies = [ "fs2", "hashbrown", "libafl_bolts", + "libafl_core", "libafl_derive", "libc", "libm", + "ll_mp", "log", "meminterval", "nix", @@ -325,48 +375,78 @@ dependencies = [ [[package]] name = "libafl_bolts" -version = "0.15.2" +version = "0.15.3" dependencies = [ "ahash", "backtrace", - "ctor", + "build_id2", + "core_affinity2", "erased-serde", + "exceptional", + "fast_rands", "hashbrown", "hostname", + "libafl_core", "libafl_derive", + "libafl_wide", "libc", + "ll_mp", "log", "mach2", + "minibsod", "miniz_oxide", "nix", + "no_std_time", "num_enum", "once_cell", + "ownedref", "postcard", - "rand_core", "rustversion", "serde", + "serde_anymap", "serial_test", + "shmem_providers", "static_assertions", "tuple_list", + "tuple_list_ex", "typeid", "uds", "uuid", - "wide 0.7.32 (git+https://github.com/Lokathor/wide?rev=71b5df0b2620da753836fafce5f99076181a49fe)", "winapi", "windows", "windows-result", "xxhash-rust", ] +[[package]] +name = "libafl_core" +version = "0.15.3" +dependencies = [ + "nix", + "postcard", + "rustversion", + "serde", +] + [[package]] name = "libafl_derive" -version = "0.15.2" +version = "0.15.3" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "libafl_wide" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2f28d525f6e361b6cd55c0da5347027860a902d638d15194c16dc2f39a5ba9f" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "libc" version = "0.2.172" @@ -379,6 +459,26 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +[[package]] +name = "ll_mp" +version = "0.15.3" +dependencies = [ + "backtrace", + "exceptional", + "hostname", + "libafl_core", + "log", + "miniz_oxide", + "nix", + "no_std_time", + "postcard", + "rustversion", + "serde", + "serial_test", + "shmem_providers", + "tuple_list", +] + [[package]] name = "lock_api" version = "0.4.12" @@ -429,6 +529,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minibsod" +version = "0.15.3" +dependencies = [ + "exceptional", + "libc", + "mach2", + "rustversion", +] + [[package]] name = "miniz_oxide" version = "0.8.8" @@ -440,9 +550,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags", "cfg-if", @@ -451,6 +561,13 @@ dependencies = [ "memoffset", ] +[[package]] +name = "no_std_time" +version = "0.15.3" +dependencies = [ + "rustversion", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -495,6 +612,15 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "ownedref" +version = "0.15.3" +dependencies = [ + "libafl_core", + "rustversion", + "serde", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -655,6 +781,21 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_anymap" +version = "0.15.3" +dependencies = [ + "ahash", + "ctor", + "erased-serde", + "hashbrown", + "libafl_core", + "postcard", + "rustversion", + "serde", + "static_assertions", +] + [[package]] name = "serde_derive" version = "1.0.219" @@ -702,6 +843,23 @@ dependencies = [ "syn", ] +[[package]] +name = "shmem_providers" +version = "0.15.3" +dependencies = [ + "fast_rands", + "hashbrown", + "libafl_core", + "libc", + "log", + "nix", + "postcard", + "rustversion", + "serde", + "serial_test", + "uds", +] + [[package]] name = "siphasher" version = "1.0.1" @@ -737,6 +895,18 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "141fb9f71ee586d956d7d6e4d5a9ef8e946061188520140f7591b668841d502e" +[[package]] +name = "tuple_list_ex" +version = "0.15.3" +dependencies = [ + "libafl_core", + "ownedref", + "rustversion", + "serde", + "tuple_list", + "typeid", +] + [[package]] name = "typed-builder" version = "0.21.0" @@ -784,6 +954,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "uuid" version = "1.17.0" @@ -802,6 +978,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + [[package]] name = "wait-timeout" version = "0.2.1" @@ -878,25 +1060,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "wide" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22" -dependencies = [ - "bytemuck", - "safe_arch", -] - -[[package]] -name = "wide" -version = "0.7.32" -source = "git+https://github.com/Lokathor/wide?rev=71b5df0b2620da753836fafce5f99076181a49fe#71b5df0b2620da753836fafce5f99076181a49fe" -dependencies = [ - "bytemuck", - "safe_arch", -] - [[package]] name = "winapi" version = "0.3.9"