From 2dcdaaa89fd54554adae787fbf8b608f085b6d7e Mon Sep 17 00:00:00 2001 From: Farouk Faiz <48885293+faroukfaiz10@users.noreply.github.com> Date: Mon, 14 Feb 2022 11:41:39 +0100 Subject: [PATCH] Intial support to Python bindings for the libafl crate (#429) * Add libafl py module * Hardcoded baby_fuzzer * Trait abstraction: MapObserver Send type name as a param as it's needed for extracting the rust struct from the PyObject * Fix merge * Impl traits for python wrappers * Add PythonExecutor Not buildable version * Executor trait bindings * Monitor trait bindings * EventManager trait bindings * Fix warnings * Add corpus trait bindings * Use corpus trait bindings * Rand trait bindings * Remove python feature from default * Add cfg attribute * Fix fmt * No std box * Fix clippy * turn OwnedInProcessExecutor in a simple type alias * remove crate-type from libafl's Cargo.toml * Add python baby_fuzzer * Fix doc * Maturin doc * multiple map observer * fmt * build pylibafl with nightly * macro for map element type * Update py baby_fuzzer & fmt * Mutator bindings * fmt * merge conflicts * StdMutationalStage bindings Not working: Cannot pass mutator to new method because not clonable * Stage bindings * StagesOwnedList bindings Not working: Stage not clonable * Unsafe transmute copy fix * Use Stage bindings in baby_fuzzer * fmt * fmt * Fix doc * fix merge * Remove x86_64 feature from pylibafl Co-authored-by: Andrea Fioraldi --- bindings/pylibafl/Cargo.toml | 5 + bindings/pylibafl/README.md | 31 +++ bindings/pylibafl/rust-toolchain | 1 + bindings/pylibafl/src/lib.rs | 5 + fuzzers/baby_fuzzer/baby_fuzzer.py | 70 +++++ libafl/Cargo.toml | 3 + libafl/src/bolts/os/unix_signals.rs | 2 +- libafl/src/bolts/rands.rs | 78 ++++++ libafl/src/corpus/cached.rs | 35 +++ libafl/src/corpus/inmemory.rs | 32 +++ libafl/src/corpus/mod.rs | 168 ++++++++++++ libafl/src/corpus/ondisk.rs | 33 +++ libafl/src/events/mod.rs | 205 ++++++++++++++ libafl/src/events/simple.rs | 32 +++ libafl/src/executors/inprocess.rs | 213 +++++++++++++-- libafl/src/executors/mod.rs | 220 +++++++++++++++ libafl/src/feedbacks/map.rs | 160 ++++++++++- libafl/src/fuzzer/mod.rs | 196 ++++++++++++++ libafl/src/generators/mod.rs | 87 ++++++ libafl/src/lib.rs | 29 ++ libafl/src/monitors/mod.rs | 101 +++++++ libafl/src/observers/map.rs | 406 +++++++++++++++++++++++++++- libafl/src/stages/mod.rs | 187 +++++++++++++ libafl/src/stages/mutational.rs | 160 +++++++++++ libafl/src/stages/owned.rs | 144 ++++++++++ libafl/src/state/mod.rs | 185 +++++++++++++ 26 files changed, 2762 insertions(+), 26 deletions(-) create mode 100644 bindings/pylibafl/README.md create mode 100644 bindings/pylibafl/rust-toolchain create mode 100644 fuzzers/baby_fuzzer/baby_fuzzer.py diff --git a/bindings/pylibafl/Cargo.toml b/bindings/pylibafl/Cargo.toml index ec5585ceeb..fe497f1d64 100644 --- a/bindings/pylibafl/Cargo.toml +++ b/bindings/pylibafl/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" pyo3 = { version = "0.15", features = ["extension-module"] } libafl_qemu = { path = "../../libafl_qemu", version = "0.7", features = ["python"] } libafl_sugar = { path = "../../libafl_sugar", version = "0.7", features = ["python"] } +libafl = { path = "../../libafl", version = "0.7", features = ["python"] } + [build-dependencies] pyo3-build-config = { version = "0.15" } @@ -14,3 +16,6 @@ pyo3-build-config = { version = "0.15" } [lib] name = "pylibafl" crate-type = ["cdylib"] + +[profile.dev] +panic = "abort" diff --git a/bindings/pylibafl/README.md b/bindings/pylibafl/README.md new file mode 100644 index 0000000000..728dc6483f --- /dev/null +++ b/bindings/pylibafl/README.md @@ -0,0 +1,31 @@ +# How to use python bindings +## First time setup +``` +# Create environment variable +python -m venv .env +# Install maturin +pip install maturin +``` +## Build bindings +``` +# Activate virtual environment +source .env/bin/activate +# Build python module +maturin develop +``` +This is going to install `pylibafl` python module. + +## Use bindings +### Example: Running baby_fuzzer in fuzzers/baby_fuzzer/baby_fuzzer.py +First, make sure the python virtual environment is activated. If not, run `source .env/bin/activate +`. Running `pip freeze` at this point should display the following (versions may differ): +``` +maturin==0.12.6 +pylibafl==0.7.0 +toml==0.10.2 +``` +Then simply run +``` +python PATH_TO_BABY_FUZZER/baby_fuzzer.py +``` +The crashes' directory will be created in the directory from which you ran the command. diff --git a/bindings/pylibafl/rust-toolchain b/bindings/pylibafl/rust-toolchain new file mode 100644 index 0000000000..bf867e0ae5 --- /dev/null +++ b/bindings/pylibafl/rust-toolchain @@ -0,0 +1 @@ +nightly diff --git a/bindings/pylibafl/src/lib.rs b/bindings/pylibafl/src/lib.rs index 8094f8b25c..c004c4c6ae 100644 --- a/bindings/pylibafl/src/lib.rs +++ b/bindings/pylibafl/src/lib.rs @@ -1,5 +1,6 @@ use libafl_qemu; use libafl_sugar; +use libafl; use pyo3::prelude::*; #[pymodule] @@ -13,5 +14,9 @@ pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> { libafl_qemu::python_module(py, qemu_module)?; m.add_submodule(qemu_module)?; + let libafl_module = PyModule::new(py, "libafl")?; + libafl::python_module(py, libafl_module)?; + m.add_submodule(libafl_module)?; + Ok(()) } diff --git a/fuzzers/baby_fuzzer/baby_fuzzer.py b/fuzzers/baby_fuzzer/baby_fuzzer.py new file mode 100644 index 0000000000..bfa92c2f51 --- /dev/null +++ b/fuzzers/baby_fuzzer/baby_fuzzer.py @@ -0,0 +1,70 @@ +from pylibafl import libafl + +# LIBRARY WRAPPER + +def map_observer_wrapper(map_observer): + if type(map_observer).__name__ == "OwnedMapObserverI32": + return libafl.MapObserverI32.new_from_owned(map_observer) + +def executor_wrapper(executor): + if type(executor).__name__ == "OwnedInProcessExecutorI32": + return libafl.ExecutorI32.new_from_inprocess(executor) + +def monitor_wrapper(monitor): + if type(monitor).__name__ == "SimpleMonitor": + return libafl.Monitor.new_from_simple(monitor) + +def event_manager_wrapper(event_manager): + if type(event_manager).__name__ == "SimpleEventManager": + return libafl.EventManagerI32.new_from_simple(event_manager) + +def corpus_wrapper(corpus): + if type(corpus).__name__ == "InMemoryCorpus": + return libafl.Corpus.new_from_in_memory(corpus) + if type(corpus).__name__ == "OnDiskCorpus": + return libafl.Corpus.new_from_on_disk(corpus) + +def rand_wrapper(rand): + if type(rand).__name__ == "StdRand": + return libafl.Rand.new_from_std(rand) + +def stage_wrapper(stage): + if type(stage).__name__ == "StdScheduledHavocMutationsStageI32": + return libafl.StageI32.new_from_std_scheduled(stage) + +# CODE WRITTEN BY USER + +def harness(inp): + if len(inp.hex()) >= 2 and inp.hex()[:2] == '61': + raise Exception("NOOOOOO =)") + +map_observer = libafl.OwnedMapObserverI32("signals", [0] * 16) + +feedback_state = libafl.MapFeedbackStateI32.with_observer(map_observer_wrapper(map_observer)) + +feedback = libafl.MaxMapFeedbackI32(feedback_state, map_observer_wrapper(map_observer)) + +state = libafl.StdStateI32( + rand_wrapper(libafl.StdRand.with_current_nanos()), + corpus_wrapper(libafl.InMemoryCorpus()), + corpus_wrapper(libafl.OnDiskCorpus("./crashes")), + feedback_state +) + +monitor = libafl.SimpleMonitor() + +mgr = libafl.SimpleEventManager(monitor_wrapper(monitor)) + +fuzzer = libafl.StdFuzzerI32(feedback) + +executor = libafl.OwnedInProcessExecutorI32(harness, map_observer_wrapper(map_observer), fuzzer, state, event_manager_wrapper(mgr)) + +generator = libafl.RandPrintablesGeneratorI32(32) + +state.generate_initial_inputs(fuzzer, executor_wrapper(executor), generator, event_manager_wrapper(mgr), 8) + +stage = libafl.StdScheduledHavocMutationsStageI32.new_from_scheduled_havoc_mutations() + +stage_tuple_list = libafl.StagesOwnedListI32(stage_wrapper(stage)) + +fuzzer.fuzz_loop(executor_wrapper(executor), state, event_manager_wrapper(mgr), stage_tuple_list) \ No newline at end of file diff --git a/libafl/Cargo.toml b/libafl/Cargo.toml index 123de8f83a..e5a68c06a6 100644 --- a/libafl/Cargo.toml +++ b/libafl/Cargo.toml @@ -19,6 +19,7 @@ fork = [] # uses the fork() syscall to spawn children, instead of launching a ne rand_trait = ["rand_core"] # If set, libafl's rand implementations will implement `rand::Rng` introspection = [] # Include performance statistics of the fuzzing pipeline concolic_mutation = ["z3"] # include a simple concolic mutator based on z3 +python = ["pyo3"] tui_monitor = ["tui", "crossterm"] # enable TuiMonitor with crossterm cli = ["clap"] # expose bolts::cli qemu_cli = ["cli"] @@ -81,6 +82,8 @@ wait-timeout = { version = "0.2", optional = true } # used by CommandExecutor to z3 = { version = "0.11", features = ["static-link-z3"], optional = true } # for concolic mutation +pyo3 = { version = "0.15", optional = true } + # AGPL # !!! this create requires nightly grammartec = { version = "0.1", optional = true } diff --git a/libafl/src/bolts/os/unix_signals.rs b/libafl/src/bolts/os/unix_signals.rs index 18b7d49f4e..50aadddf21 100644 --- a/libafl/src/bolts/os/unix_signals.rs +++ b/libafl/src/bolts/os/unix_signals.rs @@ -224,7 +224,7 @@ unsafe fn handle_signal(sig: c_int, info: siginfo_t, void: *mut c_void) { /// Setup signal handlers in a somewhat rusty way. /// This will allocate a signal stack and set the signal handlers accordingly. -/// It is, for example, used in the [`struct@crate::executors::InProcessExecutor`] to restart the fuzzer in case of a crash, +/// It is, for example, used in the [`type@crate::executors::InProcessExecutor`] to restart the fuzzer in case of a crash, /// or to handle `SIGINT` in the broker process. /// # Safety /// The signal handlers will be called on any signal. They should (tm) be async safe. diff --git a/libafl/src/bolts/rands.rs b/libafl/src/bolts/rands.rs index 10406ec1dc..c8c726b651 100644 --- a/libafl/src/bolts/rands.rs +++ b/libafl/src/bolts/rands.rs @@ -430,3 +430,81 @@ mod tests { println!("random value: {}", mutator.rng.next_u32()); } } + +#[cfg(feature = "python")] +/// `Rand` Python bindings +pub mod pybind { + use super::Rand; + use crate::bolts::{current_nanos, rands::StdRand}; + use pyo3::prelude::*; + use serde::{Deserialize, Serialize}; + + #[pyclass(unsendable, name = "StdRand")] + #[derive(Serialize, Deserialize, Debug, Clone)] + /// Python class for StdRand + pub struct PythonStdRand { + /// Rust wrapped StdRand object + pub std_rand: StdRand, + } + + #[pymethods] + impl PythonStdRand { + #[staticmethod] + fn with_current_nanos() -> Self { + Self { + std_rand: StdRand::with_seed(current_nanos()), + } + } + + #[staticmethod] + fn with_seed(seed: u64) -> Self { + Self { + std_rand: StdRand::with_seed(seed), + } + } + } + + #[derive(Serialize, Deserialize, Debug, Clone)] + enum PythonRandWrapper { + StdRand(PythonStdRand), + } + + /// Rand Trait binding + #[pyclass(unsendable, name = "Rand")] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct PythonRand { + rand: PythonRandWrapper, + } + + #[pymethods] + impl PythonRand { + #[staticmethod] + fn new_from_std(py_std_rand: PythonStdRand) -> Self { + Self { + rand: PythonRandWrapper::StdRand(py_std_rand), + } + } + } + + impl Rand for PythonRand { + fn set_seed(&mut self, seed: u64) { + match &mut self.rand { + PythonRandWrapper::StdRand(py_std_rand) => py_std_rand.std_rand.set_seed(seed), + } + } + + #[inline] + fn next(&mut self) -> u64 { + match &mut self.rand { + PythonRandWrapper::StdRand(py_std_rand) => py_std_rand.std_rand.next(), + } + } + } + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/corpus/cached.rs b/libafl/src/corpus/cached.rs index 6db7ad320c..3652b9dde4 100644 --- a/libafl/src/corpus/cached.rs +++ b/libafl/src/corpus/cached.rs @@ -133,3 +133,38 @@ where }) } } + +/// ``CachedOnDiskCorpus`` Python bindings +#[cfg(feature = "python")] +pub mod pybind { + use std::path::PathBuf; + + use crate::corpus::CachedOnDiskCorpus; + use crate::inputs::BytesInput; + use pyo3::prelude::*; + use serde::{Deserialize, Serialize}; + + #[pyclass(unsendable, name = "CachedOnDiskCorpus")] + #[derive(Serialize, Deserialize, Debug, Clone)] + /// Python class for CachedOnDiskCorpus + pub struct PythonCachedOnDiskCorpus { + /// Rust wrapped CachedOnDiskCorpus object + pub cached_on_disk_corpus: CachedOnDiskCorpus, + } + + #[pymethods] + impl PythonCachedOnDiskCorpus { + #[new] + fn new(path: String, cache_max_len: usize) -> Self { + Self { + cached_on_disk_corpus: CachedOnDiskCorpus::new(PathBuf::from(path), cache_max_len) + .unwrap(), + } + } + } + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/corpus/inmemory.rs b/libafl/src/corpus/inmemory.rs index 3357977479..bd250935fd 100644 --- a/libafl/src/corpus/inmemory.rs +++ b/libafl/src/corpus/inmemory.rs @@ -87,3 +87,35 @@ where } } } + +/// `InMemoryCorpus` Python bindings +#[cfg(feature = "python")] +pub mod pybind { + use crate::corpus::InMemoryCorpus; + use crate::inputs::BytesInput; + use pyo3::prelude::*; + use serde::{Deserialize, Serialize}; + + #[pyclass(unsendable, name = "InMemoryCorpus")] + #[derive(Serialize, Deserialize, Debug, Clone)] + /// Python class for InMemoryCorpus + pub struct PythonInMemoryCorpus { + /// Rust wrapped InMemoryCorpus object + pub in_memory_corpus: InMemoryCorpus, + } + + #[pymethods] + impl PythonInMemoryCorpus { + #[new] + fn new() -> Self { + Self { + in_memory_corpus: InMemoryCorpus::new(), + } + } + } + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/corpus/mod.rs b/libafl/src/corpus/mod.rs index be3368d46e..a495f393ad 100644 --- a/libafl/src/corpus/mod.rs +++ b/libafl/src/corpus/mod.rs @@ -148,3 +148,171 @@ impl Default for RandCorpusScheduler { /// A [`StdCorpusScheduler`] uses the default scheduler in `LibAFL` to schedule [`Testcase`]s. /// The current `Std` is a [`RandCorpusScheduler`], although this may change in the future, if another [`CorpusScheduler`] delivers better results. pub type StdCorpusScheduler = RandCorpusScheduler; + +/// `Corpus` Python bindings +#[cfg(feature = "python")] +pub mod pybind { + use crate::corpus::inmemory::pybind::PythonInMemoryCorpus; + use crate::corpus::{Corpus, Testcase}; + use crate::inputs::BytesInput; + use crate::Error; + use pyo3::prelude::*; + use serde::{Deserialize, Serialize}; + use std::cell::RefCell; + + use super::cached::pybind::PythonCachedOnDiskCorpus; + use super::ondisk::pybind::PythonOnDiskCorpus; + + #[derive(Serialize, Deserialize, Debug, Clone)] + enum PythonCorpusWrapper { + InMemory(PythonInMemoryCorpus), + CachedOnDisk(PythonCachedOnDiskCorpus), + OnDisk(PythonOnDiskCorpus), + } + + /// Corpus Trait binding + #[pyclass(unsendable, name = "Corpus")] + #[derive(Serialize, Deserialize, Debug, Clone)] + pub struct PythonCorpus { + corpus: PythonCorpusWrapper, + } + + #[pymethods] + impl PythonCorpus { + #[staticmethod] + fn new_from_in_memory(py_in_memory_corpus: PythonInMemoryCorpus) -> Self { + Self { + corpus: PythonCorpusWrapper::InMemory(py_in_memory_corpus), + } + } + + #[staticmethod] + fn new_from_cached_on_disk(py_cached_on_disk_corpus: PythonCachedOnDiskCorpus) -> Self { + Self { + corpus: PythonCorpusWrapper::CachedOnDisk(py_cached_on_disk_corpus), + } + } + + #[staticmethod] + fn new_from_on_disk(py_on_disk_corpus: PythonOnDiskCorpus) -> Self { + Self { + corpus: PythonCorpusWrapper::OnDisk(py_on_disk_corpus), + } + } + } + + impl Corpus for PythonCorpus { + #[inline] + fn count(&self) -> usize { + match &self.corpus { + PythonCorpusWrapper::InMemory(py_in_memory_corpus) => { + py_in_memory_corpus.in_memory_corpus.count() + } + PythonCorpusWrapper::CachedOnDisk(py_cached_on_disk_corpus) => { + py_cached_on_disk_corpus.cached_on_disk_corpus.count() + } + PythonCorpusWrapper::OnDisk(py_on_disk_corpus) => { + py_on_disk_corpus.on_disk_corpus.count() + } + } + } + + #[inline] + fn add(&mut self, testcase: Testcase) -> Result { + match &mut self.corpus { + PythonCorpusWrapper::InMemory(py_in_memory_corpus) => { + py_in_memory_corpus.in_memory_corpus.add(testcase) + } + PythonCorpusWrapper::CachedOnDisk(py_cached_on_disk_corpus) => { + py_cached_on_disk_corpus.cached_on_disk_corpus.add(testcase) + } + PythonCorpusWrapper::OnDisk(py_on_disk_corpus) => { + py_on_disk_corpus.on_disk_corpus.add(testcase) + } + } + } + + #[inline] + fn replace(&mut self, idx: usize, testcase: Testcase) -> Result<(), Error> { + match &mut self.corpus { + PythonCorpusWrapper::InMemory(py_in_memory_corpus) => { + py_in_memory_corpus.in_memory_corpus.replace(idx, testcase) + } + PythonCorpusWrapper::CachedOnDisk(py_cached_on_disk_corpus) => { + py_cached_on_disk_corpus + .cached_on_disk_corpus + .replace(idx, testcase) + } + PythonCorpusWrapper::OnDisk(py_on_disk_corpus) => { + py_on_disk_corpus.on_disk_corpus.replace(idx, testcase) + } + } + } + + #[inline] + fn remove(&mut self, idx: usize) -> Result>, Error> { + match &mut self.corpus { + PythonCorpusWrapper::InMemory(py_in_memory_corpus) => { + py_in_memory_corpus.in_memory_corpus.remove(idx) + } + PythonCorpusWrapper::CachedOnDisk(py_cached_on_disk_corpus) => { + py_cached_on_disk_corpus.cached_on_disk_corpus.remove(idx) + } + PythonCorpusWrapper::OnDisk(py_on_disk_corpus) => { + py_on_disk_corpus.on_disk_corpus.remove(idx) + } + } + } + + #[inline] + fn get(&self, idx: usize) -> Result<&RefCell>, Error> { + match &self.corpus { + PythonCorpusWrapper::InMemory(py_in_memory_corpus) => { + py_in_memory_corpus.in_memory_corpus.get(idx) + } + PythonCorpusWrapper::CachedOnDisk(py_cached_on_disk_corpus) => { + py_cached_on_disk_corpus.cached_on_disk_corpus.get(idx) + } + PythonCorpusWrapper::OnDisk(py_on_disk_corpus) => { + py_on_disk_corpus.on_disk_corpus.get(idx) + } + } + } + + #[inline] + fn current(&self) -> &Option { + match &self.corpus { + PythonCorpusWrapper::InMemory(py_in_memory_corpus) => { + py_in_memory_corpus.in_memory_corpus.current() + } + PythonCorpusWrapper::CachedOnDisk(py_cached_on_disk_corpus) => { + py_cached_on_disk_corpus.cached_on_disk_corpus.current() + } + PythonCorpusWrapper::OnDisk(py_on_disk_corpus) => { + py_on_disk_corpus.on_disk_corpus.current() + } + } + } + + #[inline] + fn current_mut(&mut self) -> &mut Option { + match &mut self.corpus { + PythonCorpusWrapper::InMemory(py_in_memory_corpus) => { + py_in_memory_corpus.in_memory_corpus.current_mut() + } + PythonCorpusWrapper::CachedOnDisk(py_cached_on_disk_corpus) => { + py_cached_on_disk_corpus.cached_on_disk_corpus.current_mut() + } + PythonCorpusWrapper::OnDisk(py_on_disk_corpus) => { + py_on_disk_corpus.on_disk_corpus.current_mut() + } + } + } + } + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/corpus/ondisk.rs b/libafl/src/corpus/ondisk.rs index 3bf238708c..9bf891da73 100644 --- a/libafl/src/corpus/ondisk.rs +++ b/libafl/src/corpus/ondisk.rs @@ -205,3 +205,36 @@ where }) } } +#[cfg(feature = "python")] +/// `OnDiskCorpus` Python bindings +pub mod pybind { + use std::path::PathBuf; + + use crate::corpus::OnDiskCorpus; + use crate::inputs::BytesInput; + use pyo3::prelude::*; + use serde::{Deserialize, Serialize}; + + #[pyclass(unsendable, name = "OnDiskCorpus")] + #[derive(Serialize, Deserialize, Debug, Clone)] + /// Python class for OnDiskCorpus + pub struct PythonOnDiskCorpus { + /// Rust wrapped OnDiskCorpus object + pub on_disk_corpus: OnDiskCorpus, + } + + #[pymethods] + impl PythonOnDiskCorpus { + #[new] + fn new(path: String) -> Self { + Self { + on_disk_corpus: OnDiskCorpus::new(PathBuf::from(path)).unwrap(), + } + } + } + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/events/mod.rs b/libafl/src/events/mod.rs index caa883111d..fd049a5872 100644 --- a/libafl/src/events/mod.rs +++ b/libafl/src/events/mod.rs @@ -543,3 +543,208 @@ mod tests { }; } } +/// `EventManager` Python bindings +#[cfg(feature = "python")] +pub mod pybind { + use crate::events::simple::pybind::PythonSimpleEventManager; + use crate::events::{ + Event, EventFirer, EventManager, EventManagerId, EventProcessor, EventRestarter, + HasEventManagerId, ProgressReporter, + }; + use crate::inputs::BytesInput; + use crate::Error; + use pyo3::prelude::*; + + macro_rules! define_python_event_manager { + ($struct_name_trait:ident, $py_name_trait:tt, $wrapper_name: ident, $std_state_name: ident, $executor_name: ident, $my_std_fuzzer_type_name: ident) => { + use crate::executors::pybind::$executor_name; + use crate::pybind::$my_std_fuzzer_type_name; + use crate::state::pybind::$std_state_name; + + #[derive(Debug, Clone)] + enum $wrapper_name { + Simple(*mut PythonSimpleEventManager), + } + + /// EventManager Trait binding + #[pyclass(unsendable, name = $py_name_trait)] + #[derive(Debug, Clone)] + pub struct $struct_name_trait { + event_manager: $wrapper_name, + } + + impl $struct_name_trait { + fn get_event_manager( + &self, + ) -> &impl EventManager< + $executor_name, + BytesInput, + $std_state_name, + $my_std_fuzzer_type_name, + > { + unsafe { + match self.event_manager { + $wrapper_name::Simple(py_simple_event_manager) => { + &(*py_simple_event_manager).simple_event_manager + } + } + } + } + + fn get_mut_event_manager( + &mut self, + ) -> &mut impl EventManager< + $executor_name, + BytesInput, + $std_state_name, + $my_std_fuzzer_type_name, + > { + unsafe { + match self.event_manager { + $wrapper_name::Simple(py_simple_event_manager) => { + &mut (*py_simple_event_manager).simple_event_manager + } + } + } + } + } + + #[pymethods] + impl $struct_name_trait { + #[staticmethod] + fn new_from_simple(py_simple_event_manager: &mut PythonSimpleEventManager) -> Self { + Self { + event_manager: $wrapper_name::Simple(py_simple_event_manager), + } + } + } + + impl EventFirer for $struct_name_trait { + fn fire( + &mut self, + _state: &mut S, + event: Event, + ) -> Result<(), Error> { + self.get_mut_event_manager().fire(_state, event) + } + } + + impl EventRestarter for $struct_name_trait {} + + impl + EventProcessor< + $executor_name, + BytesInput, + $std_state_name, + $my_std_fuzzer_type_name, + > for $struct_name_trait + { + fn process( + &mut self, + _fuzzer: &mut $my_std_fuzzer_type_name, + state: &mut $std_state_name, + _executor: &mut $executor_name, + ) -> Result { + self.get_mut_event_manager() + .process(_fuzzer, state, _executor) + } + } + + impl ProgressReporter for $struct_name_trait {} + + impl HasEventManagerId for $struct_name_trait { + fn mgr_id(&self) -> EventManagerId { + self.get_event_manager().mgr_id() + } + } + + impl EventManager<$executor_name, BytesInput, $std_state_name, $my_std_fuzzer_type_name> + for $struct_name_trait + { + } + }; + } + + define_python_event_manager!( + PythonEventManagerI8, + "EventManagerI8", + PythonEventManagerWrapperI8, + MyStdStateI8, + PythonExecutorI8, + MyStdFuzzerI8 + ); + + define_python_event_manager!( + PythonEventManagerI16, + "EventManagerI16", + PythonEventManagerWrapperI16, + MyStdStateI16, + PythonExecutorI16, + MyStdFuzzerI16 + ); + + define_python_event_manager!( + PythonEventManagerI32, + "EventManagerI32", + PythonEventManagerWrapperI32, + MyStdStateI32, + PythonExecutorI32, + MyStdFuzzerI32 + ); + + define_python_event_manager!( + PythonEventManagerI64, + "EventManagerI64", + PythonEventManagerWrapperI64, + MyStdStateI64, + PythonExecutorI64, + MyStdFuzzerI64 + ); + + define_python_event_manager!( + PythonEventManagerU8, + "EventManagerU8", + PythonEventManagerWrapperU8, + MyStdStateU8, + PythonExecutorU8, + MyStdFuzzerU8 + ); + define_python_event_manager!( + PythonEventManagerU16, + "EventManagerU16", + PythonEventManagerWrapperU16, + MyStdStateU16, + PythonExecutorU16, + MyStdFuzzerU16 + ); + define_python_event_manager!( + PythonEventManagerU32, + "EventManagerU32", + PythonEventManagerWrapperU32, + MyStdStateU32, + PythonExecutorU32, + MyStdFuzzerU32 + ); + define_python_event_manager!( + PythonEventManagerU64, + "EventManagerU64", + PythonEventManagerWrapperU64, + MyStdStateU64, + PythonExecutorU64, + MyStdFuzzerU64 + ); + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/events/simple.rs b/libafl/src/events/simple.rs index f77b8b8f30..272f6ac386 100644 --- a/libafl/src/events/simple.rs +++ b/libafl/src/events/simple.rs @@ -425,3 +425,35 @@ where Ok((state, mgr)) } } +/// `SimpleEventManager` Python bindings +#[cfg(feature = "python")] +pub mod pybind { + use crate::events::SimpleEventManager; + use crate::inputs::BytesInput; + use crate::monitors::pybind::PythonMonitor; + use pyo3::prelude::*; + + #[pyclass(unsendable, name = "SimpleEventManager")] + #[derive(Debug, Clone)] + /// Python class for SimpleEventManager + pub struct PythonSimpleEventManager { + /// Rust wrapped SimpleEventManager object + pub simple_event_manager: SimpleEventManager, + } + + #[pymethods] + impl PythonSimpleEventManager { + #[new] + fn new(py_monitor: PythonMonitor) -> Self { + Self { + simple_event_manager: SimpleEventManager::new(py_monitor), + } + } + } + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/executors/inprocess.rs b/libafl/src/executors/inprocess.rs index aa01a31d9d..be6a9d8823 100644 --- a/libafl/src/executors/inprocess.rs +++ b/libafl/src/executors/inprocess.rs @@ -4,6 +4,7 @@ //! Needs the `fork` feature flag. use core::{ + borrow::BorrowMut, ffi::c_void, fmt::{self, Debug, Formatter}, marker::PhantomData, @@ -16,6 +17,8 @@ use core::{ sync::atomic::{compiler_fence, Ordering}, }; +use alloc::boxed::Box; + #[cfg(all(feature = "std", unix))] use std::intrinsics::transmute; @@ -52,40 +55,50 @@ use crate::{ Error, }; +/// The process executor simply calls a target function, as mutable reference to a closure +pub type InProcessExecutor<'a, H, I, OT, S> = GenericInProcessExecutor; + +/// The process executor simply calls a target function, as boxed `FnMut` trait object +pub type OwnedInProcessExecutor = + GenericInProcessExecutor ExitKind, Box ExitKind>, I, OT, S>; + /// The inmem executor simply calls a target function, then returns afterwards. #[allow(dead_code)] -pub struct InProcessExecutor<'a, H, I, OT, S> +pub struct GenericInProcessExecutor where - H: FnMut(&I) -> ExitKind, + H: FnMut(&I) -> ExitKind + ?Sized, + HB: BorrowMut, I: Input, OT: ObserversTuple, { /// The harness function, being executed for each fuzzing loop execution - harness_fn: &'a mut H, + harness_fn: HB, /// The observers, observing each run observers: OT, // Crash and timeout hah handlers: InProcessHandlers, - phantom: PhantomData<(I, S)>, + phantom: PhantomData<(I, S, *const H)>, } -impl<'a, H, I, OT, S> Debug for InProcessExecutor<'a, H, I, OT, S> +impl Debug for GenericInProcessExecutor where - H: FnMut(&I) -> ExitKind, + H: FnMut(&I) -> ExitKind + ?Sized, + HB: BorrowMut, I: Input, OT: ObserversTuple, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("InProcessExecutor") + f.debug_struct("GenericInProcessExecutor") .field("harness_fn", &"") .field("observers", &self.observers) .finish_non_exhaustive() } } -impl<'a, EM, H, I, OT, S, Z> Executor for InProcessExecutor<'a, H, I, OT, S> +impl Executor for GenericInProcessExecutor where - H: FnMut(&I) -> ExitKind, + H: FnMut(&I) -> ExitKind + ?Sized, + HB: BorrowMut, I: Input, OT: ObserversTuple, { @@ -99,16 +112,17 @@ where self.handlers .pre_run_target(self, fuzzer, state, mgr, input); - let ret = (self.harness_fn)(input); + let ret = (self.harness_fn.borrow_mut())(input); self.handlers.post_run_target(); Ok(ret) } } -impl<'a, H, I, OT, S> HasObservers for InProcessExecutor<'a, H, I, OT, S> +impl HasObservers for GenericInProcessExecutor where - H: FnMut(&I) -> ExitKind, + H: FnMut(&I) -> ExitKind + ?Sized, + HB: BorrowMut, I: Input, OT: ObserversTuple, { @@ -123,9 +137,10 @@ where } } -impl<'a, H, I, OT, S> InProcessExecutor<'a, H, I, OT, S> +impl GenericInProcessExecutor where - H: FnMut(&I) -> ExitKind, + H: FnMut(&I) -> ExitKind + ?Sized, + HB: BorrowMut, I: Input, OT: ObserversTuple, { @@ -136,7 +151,7 @@ where /// * `observers` - the observers observing the target during execution /// This may return an error on unix, if signal handler setup fails pub fn new( - harness_fn: &'a mut H, + harness_fn: HB, observers: OT, _fuzzer: &mut Z, _state: &mut S, @@ -175,13 +190,13 @@ where /// Retrieve the harness function. #[inline] pub fn harness(&self) -> &H { - self.harness_fn + self.harness_fn.borrow() } /// Retrieve the harness function for a mutable reference. #[inline] pub fn harness_mut(&mut self) -> &mut H { - self.harness_fn + self.harness_fn.borrow_mut() } /// The inprocess handlers @@ -305,7 +320,7 @@ impl InProcessHandlers { OF: Feedback, S: HasSolutions + HasClientPerfMonitor, Z: HasObjective, - H: FnMut(&I) -> ExitKind, + H: FnMut(&I) -> ExitKind + ?Sized, { #[cfg(unix)] unsafe { @@ -1376,7 +1391,7 @@ impl Handler for InProcessForkExecutorGlobalData { #[cfg(all(feature = "std", unix))] pub struct InProcessForkExecutor<'a, H, I, OT, S, SP> where - H: FnMut(&I) -> ExitKind, + H: FnMut(&I) -> ExitKind + ?Sized, I: Input, OT: ObserversTuple, SP: ShMemProvider, @@ -1391,7 +1406,7 @@ where #[cfg(all(feature = "std", unix))] impl<'a, H, I, OT, S, SP> Debug for InProcessForkExecutor<'a, H, I, OT, S, SP> where - H: FnMut(&I) -> ExitKind, + H: FnMut(&I) -> ExitKind + ?Sized, I: Input, OT: ObserversTuple, SP: ShMemProvider, @@ -1408,7 +1423,7 @@ where impl<'a, EM, H, I, OT, S, SP, Z> Executor for InProcessForkExecutor<'a, H, I, OT, S, SP> where - H: FnMut(&I) -> ExitKind, + H: FnMut(&I) -> ExitKind + ?Sized, I: Input, OT: ObserversTuple, SP: ShMemProvider, @@ -1466,7 +1481,7 @@ where #[cfg(all(feature = "std", unix))] impl<'a, H, I, OT, S, SP> InProcessForkExecutor<'a, H, I, OT, S, SP> where - H: FnMut(&I) -> ExitKind, + H: FnMut(&I) -> ExitKind + ?Sized, I: Input, OT: ObserversTuple, SP: ShMemProvider, @@ -1512,7 +1527,7 @@ where #[cfg(all(feature = "std", unix))] impl<'a, H, I, OT, S, SP> HasObservers for InProcessForkExecutor<'a, H, I, OT, S, SP> where - H: FnMut(&I) -> ExitKind, + H: FnMut(&I) -> ExitKind + ?Sized, I: Input, OT: ObserversTuple, SP: ShMemProvider, @@ -1652,3 +1667,155 @@ mod tests { .is_ok()); } } + +#[cfg(feature = "python")] +/// `InProcess` Python bindings +pub mod pybind { + use crate::bolts::tuples::tuple_list; + use crate::executors::{inprocess::OwnedInProcessExecutor, ExitKind}; + use crate::inputs::{BytesInput, HasBytesVec}; + use pyo3::prelude::*; + use pyo3::types::PyBytes; + + macro_rules! define_python_in_process_executor { + ($struct_name:ident, $py_name:tt, $my_std_state_type_name: ident, $std_state_name: ident, $event_manager_name: ident, $map_observer_name: ident, $std_fuzzer_name: ident) => { + use crate::events::pybind::$event_manager_name; + use crate::fuzzer::pybind::$std_fuzzer_name; + use crate::observers::pybind::$map_observer_name; + use crate::state::pybind::{$my_std_state_type_name, $std_state_name}; + + #[pyclass(unsendable, name = $py_name)] + #[derive(Debug)] + /// Python class for OwnedInProcessExecutor (i.e. InProcessExecutor with owned harness) + pub struct $struct_name { + /// Rust wrapped OwnedInProcessExecutor object + pub owned_in_process_executor: OwnedInProcessExecutor< + BytesInput, + ($map_observer_name, ()), + $my_std_state_type_name, + >, + } + + #[pymethods] + impl $struct_name { + #[new] + fn new( + harness: PyObject, + py_observer: $map_observer_name, + py_fuzzer: &mut $std_fuzzer_name, + py_state: &mut $std_state_name, + py_event_manager: &mut $event_manager_name, + ) -> Self { + Self { + owned_in_process_executor: OwnedInProcessExecutor::new( + Box::new(move |input: &BytesInput| { + Python::with_gil(|py| -> PyResult<()> { + let args = (PyBytes::new(py, input.bytes()),); + harness.call1(py, args)?; + Ok(()) + }) + .unwrap(); + ExitKind::Ok + }), + tuple_list!(py_observer), + &mut py_fuzzer.std_fuzzer, + &mut py_state.std_state, + py_event_manager, + ) + .expect("Failed to create the Executor".into()), + } + } + } + }; + } + + define_python_in_process_executor!( + PythonOwnedInProcessExecutorI8, + "OwnedInProcessExecutorI8", + MyStdStateI8, + PythonStdStateI8, + PythonEventManagerI8, + PythonMapObserverI8, + PythonStdFuzzerI8 + ); + + define_python_in_process_executor!( + PythonOwnedInProcessExecutorI16, + "OwnedInProcessExecutorI16", + MyStdStateI16, + PythonStdStateI16, + PythonEventManagerI16, + PythonMapObserverI16, + PythonStdFuzzerI16 + ); + define_python_in_process_executor!( + PythonOwnedInProcessExecutorI32, + "OwnedInProcessExecutorI32", + MyStdStateI32, + PythonStdStateI32, + PythonEventManagerI32, + PythonMapObserverI32, + PythonStdFuzzerI32 + ); + define_python_in_process_executor!( + PythonOwnedInProcessExecutorI64, + "OwnedInProcessExecutorI64", + MyStdStateI64, + PythonStdStateI64, + PythonEventManagerI64, + PythonMapObserverI64, + PythonStdFuzzerI64 + ); + + define_python_in_process_executor!( + PythonOwnedInProcessExecutorU8, + "OwnedInProcessExecutorU8", + MyStdStateU8, + PythonStdStateU8, + PythonEventManagerU8, + PythonMapObserverU8, + PythonStdFuzzerU8 + ); + + define_python_in_process_executor!( + PythonOwnedInProcessExecutorU16, + "OwnedInProcessExecutorU16", + MyStdStateU16, + PythonStdStateU16, + PythonEventManagerU16, + PythonMapObserverU16, + PythonStdFuzzerU16 + ); + define_python_in_process_executor!( + PythonOwnedInProcessExecutorU32, + "OwnedInProcessExecutorU32", + MyStdStateU32, + PythonStdStateU32, + PythonEventManagerU32, + PythonMapObserverU32, + PythonStdFuzzerU32 + ); + define_python_in_process_executor!( + PythonOwnedInProcessExecutorU64, + "OwnedInProcessExecutorU64", + MyStdStateU64, + PythonStdStateU64, + PythonEventManagerU64, + PythonMapObserverU64, + PythonStdFuzzerU64 + ); + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index fbc3fa5b74..22dd9a9c34 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -184,3 +184,223 @@ mod test { .is_ok()); } } + +#[cfg(feature = "python")] +/// `Executor` Python bindings +pub mod pybind { + use crate::executors::{Executor, ExitKind, HasObservers}; + use crate::inputs::BytesInput; + use crate::Error; + use pyo3::prelude::*; + + macro_rules! define_python_executor { + ($struct_name_trait:ident, $py_name_trait:tt, $wrapper_name: ident, $my_std_state_type_name: ident, $my_std_fuzzer_type_name: ident, + $event_manager_name: ident, $in_process_executor_name: ident, $map_observer_name: ident) => { + use crate::events::pybind::$event_manager_name; + use crate::executors::inprocess::pybind::$in_process_executor_name; + use crate::fuzzer::pybind::$my_std_fuzzer_type_name; + use crate::observers::map::pybind::$map_observer_name; + use crate::state::pybind::$my_std_state_type_name; + + #[derive(Debug)] + enum $wrapper_name { + OwnedInProcess(*mut $in_process_executor_name), + } + + #[pyclass(unsendable, name = $py_name_trait)] + #[derive(Debug)] + /// Executor + HasObservers Trait binding + pub struct $struct_name_trait { + executor: $wrapper_name, + } + + impl $struct_name_trait { + fn get_executor( + &self, + ) -> &(impl Executor< + $event_manager_name, + BytesInput, + $my_std_state_type_name, + $my_std_fuzzer_type_name, + > + HasObservers) + { + unsafe { + match self.executor { + $wrapper_name::OwnedInProcess(py_owned_inprocess_executor) => { + &(*py_owned_inprocess_executor).owned_in_process_executor + } + } + } + } + + fn get_mut_executor( + &mut self, + ) -> &mut (impl Executor< + $event_manager_name, + BytesInput, + $my_std_state_type_name, + $my_std_fuzzer_type_name, + > + HasObservers< + BytesInput, + ($map_observer_name, ()), + $my_std_state_type_name, + >) { + unsafe { + match self.executor { + $wrapper_name::OwnedInProcess(py_owned_inprocess_executor) => { + &mut (*py_owned_inprocess_executor).owned_in_process_executor + } + } + } + } + } + + #[pymethods] + impl $struct_name_trait { + #[staticmethod] + fn new_from_inprocess( + owned_inprocess_executor: &mut $in_process_executor_name, + ) -> Self { + Self { + executor: $wrapper_name::OwnedInProcess(owned_inprocess_executor), + } + } + } + + impl HasObservers for $struct_name_trait { + // #[inline] + fn observers(&self) -> &($map_observer_name, ()) { + self.get_executor().observers() + } + + #[inline] + fn observers_mut(&mut self) -> &mut ($map_observer_name, ()) { + self.get_mut_executor().observers_mut() + } + } + + impl + Executor< + $event_manager_name, + BytesInput, + $my_std_state_type_name, + $my_std_fuzzer_type_name, + > for $struct_name_trait + { + #[inline] + fn run_target( + &mut self, + fuzzer: &mut $my_std_fuzzer_type_name, + state: &mut $my_std_state_type_name, + mgr: &mut $event_manager_name, + input: &BytesInput, + ) -> Result { + self.get_mut_executor() + .run_target(fuzzer, state, mgr, input) + } + } + }; + } + + define_python_executor!( + PythonExecutorI8, + "ExecutorI8", + PythonExecutorWrapperI8, + MyStdStateI8, + MyStdFuzzerI8, + PythonEventManagerI8, + PythonOwnedInProcessExecutorI8, + PythonMapObserverI8 + ); + + define_python_executor!( + PythonExecutorI16, + "ExecutorI16", + PythonExecutorWrapperI16, + MyStdStateI16, + MyStdFuzzerI16, + PythonEventManagerI16, + PythonOwnedInProcessExecutorI16, + PythonMapObserverI16 + ); + + define_python_executor!( + PythonExecutorI32, + "ExecutorI32", + PythonExecutorWrapperI32, + MyStdStateI32, + MyStdFuzzerI32, + PythonEventManagerI32, + PythonOwnedInProcessExecutorI32, + PythonMapObserverI32 + ); + + define_python_executor!( + PythonExecutorI64, + "ExecutorI64", + PythonExecutorWrapperI64, + MyStdStateI64, + MyStdFuzzerI64, + PythonEventManagerI64, + PythonOwnedInProcessExecutorI64, + PythonMapObserverI64 + ); + + define_python_executor!( + PythonExecutorU8, + "ExecutorU8", + PythonExecutorWrapperU8, + MyStdStateU8, + MyStdFuzzerU8, + PythonEventManagerU8, + PythonOwnedInProcessExecutorU8, + PythonMapObserverU8 + ); + + define_python_executor!( + PythonExecutorU16, + "ExecutorU16", + PythonExecutorWrapperU16, + MyStdStateU16, + MyStdFuzzerU16, + PythonEventManagerU16, + PythonOwnedInProcessExecutorU16, + PythonMapObserverU16 + ); + + define_python_executor!( + PythonExecutorU32, + "ExecutorU32", + PythonExecutorWrapperU32, + MyStdStateU32, + MyStdFuzzerU32, + PythonEventManagerU32, + PythonOwnedInProcessExecutorU32, + PythonMapObserverU32 + ); + + define_python_executor!( + PythonExecutorU64, + "ExecutorU64", + PythonExecutorWrapperU64, + MyStdStateU64, + MyStdFuzzerU64, + PythonEventManagerU64, + PythonOwnedInProcessExecutorU64, + PythonMapObserverU64 + ); + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index e519de2e1f..a2280c174e 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -654,7 +654,6 @@ where self.name.as_str() } } - #[cfg(test)] mod tests { use crate::feedbacks::{AllIsNovel, IsNovel, NextPow2IsNovel}; @@ -679,3 +678,162 @@ mod tests { assert!(!NextPow2IsNovel::is_novel(255_u8, 255)); } } + +#[cfg(feature = "python")] +/// Map Feedback Python bindings +pub mod pybind { + use crate::feedbacks::map::{MapFeedbackState, MaxMapFeedback}; + use crate::inputs::BytesInput; + use pyo3::prelude::*; + + macro_rules! define_python_map_feedback { + ($map_feedback_state_struct_name:ident, $map_feedback_state_py_name:tt, $max_map_feedback_struct_name:ident, + $max_map_feedback_py_name:tt, $datatype:ty, $map_observer_name: ident, $std_state_name: ident) => { + use crate::observers::map::pybind::$map_observer_name; + use crate::state::pybind::$std_state_name; + + #[pyclass(unsendable, name = $map_feedback_state_py_name)] + #[derive(Clone, Debug)] + /// Python class for MapFeedbackState + pub struct $map_feedback_state_struct_name { + /// Rust wrapped MapFeedbackState object + pub map_feedback_state: MapFeedbackState<$datatype>, + } + + #[pymethods] + impl $map_feedback_state_struct_name { + #[staticmethod] + fn with_observer(py_observer: &$map_observer_name) -> Self { + Self { + map_feedback_state: MapFeedbackState::with_observer(py_observer), + } + } + } + + #[pyclass(unsendable, name = $max_map_feedback_py_name)] + #[derive(Clone, Debug)] + /// Python class for MaxMapFeedback + pub struct $max_map_feedback_struct_name { + /// Rust wrapped MaxMapFeedback object + pub max_map_feedback: + MaxMapFeedback, + } + + #[pymethods] + impl $max_map_feedback_struct_name { + #[new] + fn new( + py_feedback_state: &$map_feedback_state_struct_name, + py_observer: &$map_observer_name, + ) -> Self { + Self { + max_map_feedback: MaxMapFeedback::new( + &py_feedback_state.map_feedback_state, + py_observer, + ), + } + } + } + }; + } + + define_python_map_feedback!( + PythonMapFeedbackStateI8, + "MapFeedbackStateI8", + PythonMaxMapFeedbackI8, + "MaxMapFeedbackI8", + i8, + PythonMapObserverI8, + MyStdStateI8 + ); + + define_python_map_feedback!( + PythonMapFeedbackStateI16, + "MapFeedbackStateI16", + PythonMaxMapFeedbackI16, + "MaxMapFeedbackI16", + i16, + PythonMapObserverI16, + MyStdStateI16 + ); + define_python_map_feedback!( + PythonMapFeedbackStateI32, + "MapFeedbackStateI32", + PythonMaxMapFeedbackI32, + "MaxMapFeedbackI32", + i32, + PythonMapObserverI32, + MyStdStateI32 + ); + define_python_map_feedback!( + PythonMapFeedbackStateI64, + "MapFeedbackStateI64", + PythonMaxMapFeedbackI64, + "MaxMapFeedbackI64", + i64, + PythonMapObserverI64, + MyStdStateI64 + ); + + define_python_map_feedback!( + PythonMapFeedbackStateU8, + "MapFeedbackStateU8", + PythonMaxMapFeedbackU8, + "MaxMapFeedbackU8", + u8, + PythonMapObserverU8, + MyStdStateU8 + ); + + define_python_map_feedback!( + PythonMapFeedbackStateU16, + "MapFeedbackStateU16", + PythonMaxMapFeedbackU16, + "MaxMapFeedbackU16", + u16, + PythonMapObserverU16, + MyStdStateU16 + ); + define_python_map_feedback!( + PythonMapFeedbackStateU32, + "MapFeedbackStateU32", + PythonMaxMapFeedbackU32, + "MaxMapFeedbackU32", + u32, + PythonMapObserverU32, + MyStdStateU32 + ); + define_python_map_feedback!( + PythonMapFeedbackStateU64, + "MapFeedbackStateU64", + PythonMaxMapFeedbackU64, + "MaxMapFeedbackU64", + u64, + PythonMapObserverU64, + MyStdStateU64 + ); + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/fuzzer/mod.rs b/libafl/src/fuzzer/mod.rs index f9d0632e48..3eb643faa4 100644 --- a/libafl/src/fuzzer/mod.rs +++ b/libafl/src/fuzzer/mod.rs @@ -666,3 +666,199 @@ where Ok(exit_kind) } } + +#[cfg(feature = "python")] +/// `Fuzzer` Python bindings +pub mod pybind { + use crate::corpus::QueueCorpusScheduler; + use crate::feedbacks::{CrashFeedback, MaxMapFeedback}; + use crate::fuzzer::{Fuzzer, StdFuzzer}; + use crate::inputs::BytesInput; + use pyo3::prelude::*; + + macro_rules! define_python_fuzzer { + ($type_name:ident, $struct_name:ident, $py_name:tt, $datatype:ty, $my_std_state_type_name: ident, $std_state_name: ident, + $event_manager_name: ident, $map_observer_name: ident, $max_map_feedback_py_name: ident, $executor_name: ident, $stage_tuple_name: ident) => { + use crate::events::pybind::$event_manager_name; + use crate::executors::pybind::$executor_name; + use crate::feedbacks::map::pybind::$max_map_feedback_py_name; + use crate::observers::map::pybind::$map_observer_name; + use crate::stages::owned::pybind::$stage_tuple_name; + use crate::state::pybind::{$my_std_state_type_name, $std_state_name}; + + /// `StdFuzzer` with fixed generics + pub type $type_name = StdFuzzer< + QueueCorpusScheduler, + MaxMapFeedback, + BytesInput, + CrashFeedback, + ($map_observer_name, ()), + $my_std_state_type_name, + >; + /// Python class for StdFuzzer + #[pyclass(unsendable, name = $py_name)] + #[derive(Debug)] + pub struct $struct_name { + /// Rust wrapped StdFuzzer object + pub std_fuzzer: $type_name, + } + + #[pymethods] + impl $struct_name { + #[new] + fn new(py_max_map_feedback: $max_map_feedback_py_name) -> Self { + Self { + std_fuzzer: StdFuzzer::new( + QueueCorpusScheduler::new(), + py_max_map_feedback.max_map_feedback, + CrashFeedback::new(), + ), + } + } + + fn fuzz_loop( + &mut self, + py_executor: &mut $executor_name, + py_state: &mut $std_state_name, + py_mgr: &mut $event_manager_name, + stage_tuple: &mut $stage_tuple_name, + ) { + self.std_fuzzer + .fuzz_loop( + &mut stage_tuple.stages_owned_list, + py_executor, + &mut py_state.std_state, + py_mgr, + ) + .expect("Failed to generate the initial corpus".into()); + } + } + }; + } + + define_python_fuzzer!( + MyStdFuzzerI8, + PythonStdFuzzerI8, + "StdFuzzerI8", + i8, + MyStdStateI8, + PythonStdStateI8, + PythonEventManagerI8, + PythonMapObserverI8, + PythonMaxMapFeedbackI8, + PythonExecutorI8, + PythonStagesOwnedListI8 + ); + + define_python_fuzzer!( + MyStdFuzzerI16, + PythonStdFuzzerI16, + "StdFuzzerI16", + i16, + MyStdStateI16, + PythonStdStateI16, + PythonEventManagerI16, + PythonMapObserverI16, + PythonMaxMapFeedbackI16, + PythonExecutorI16, + PythonStagesOwnedListI16 + ); + + define_python_fuzzer!( + MyStdFuzzerI32, + PythonStdFuzzerI32, + "StdFuzzerI32", + i32, + MyStdStateI32, + PythonStdStateI32, + PythonEventManagerI32, + PythonMapObserverI32, + PythonMaxMapFeedbackI32, + PythonExecutorI32, + PythonStagesOwnedListI32 + ); + + define_python_fuzzer!( + MyStdFuzzerI64, + PythonStdFuzzerI64, + "StdFuzzerI64", + i64, + MyStdStateI64, + PythonStdStateI64, + PythonEventManagerI64, + PythonMapObserverI64, + PythonMaxMapFeedbackI64, + PythonExecutorI64, + PythonStagesOwnedListI64 + ); + + define_python_fuzzer!( + MyStdFuzzerU8, + PythonStdFuzzerU8, + "StdFuzzerU8", + u8, + MyStdStateU8, + PythonStdStateU8, + PythonEventManagerU8, + PythonMapObserverU8, + PythonMaxMapFeedbackU8, + PythonExecutorU8, + PythonStagesOwnedListU8 + ); + + define_python_fuzzer!( + MyStdFuzzerU16, + PythonStdFuzzerU16, + "StdFuzzerU16", + u16, + MyStdStateU16, + PythonStdStateU16, + PythonEventManagerU16, + PythonMapObserverU16, + PythonMaxMapFeedbackU16, + PythonExecutorU16, + PythonStagesOwnedListU16 + ); + + define_python_fuzzer!( + MyStdFuzzerU32, + PythonStdFuzzerU32, + "StdFuzzerU32", + u32, + MyStdStateU32, + PythonStdStateU32, + PythonEventManagerU32, + PythonMapObserverU32, + PythonMaxMapFeedbackU32, + PythonExecutorU32, + PythonStagesOwnedListU32 + ); + + define_python_fuzzer!( + MyStdFuzzerU64, + PythonStdFuzzerU64, + "StdFuzzerU64", + u64, + MyStdStateU64, + PythonStdStateU64, + PythonEventManagerU64, + PythonMapObserverU64, + PythonMaxMapFeedbackU64, + PythonExecutorU64, + PythonStagesOwnedListU64 + ); + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/generators/mod.rs b/libafl/src/generators/mod.rs index e9871be5a3..3cd9c12229 100644 --- a/libafl/src/generators/mod.rs +++ b/libafl/src/generators/mod.rs @@ -125,3 +125,90 @@ where } } } + +/// `Generator` Python bindings +#[cfg(feature = "python")] +pub mod pybind { + use crate::generators::RandPrintablesGenerator; + use pyo3::prelude::*; + + macro_rules! define_python_event_manager { + ($struct_name:ident, $py_name:tt, $my_std_state_type_name: ident) => { + use crate::state::pybind::$my_std_state_type_name; + + #[pyclass(unsendable, name = $py_name)] + /// Python class for RandPrintablesGenerator + #[derive(Debug)] + pub struct $struct_name { + /// Rust wrapped SimpleEventManager object + pub rand_printable_generator: RandPrintablesGenerator<$my_std_state_type_name>, + } + + #[pymethods] + impl $struct_name { + #[new] + fn new(max_size: usize) -> Self { + Self { + rand_printable_generator: RandPrintablesGenerator::new(max_size), + } + } + } + }; + } + + define_python_event_manager!( + PythonRandPrintablesGeneratorI8, + "RandPrintablesGeneratorI8", + MyStdStateI8 + ); + define_python_event_manager!( + PythonRandPrintablesGeneratorI16, + "RandPrintablesGeneratorI16", + MyStdStateI16 + ); + define_python_event_manager!( + PythonRandPrintablesGeneratorI32, + "RandPrintablesGeneratorI32", + MyStdStateI32 + ); + define_python_event_manager!( + PythonRandPrintablesGeneratorI64, + "RandPrintablesGeneratorI64", + MyStdStateI64 + ); + + define_python_event_manager!( + PythonRandPrintablesGeneratorU8, + "RandPrintablesGeneratorU8", + MyStdStateU8 + ); + define_python_event_manager!( + PythonRandPrintablesGeneratorU16, + "RandPrintablesGeneratorU16", + MyStdStateU16 + ); + define_python_event_manager!( + PythonRandPrintablesGeneratorU32, + "RandPrintablesGeneratorU32", + MyStdStateU32 + ); + define_python_event_manager!( + PythonRandPrintablesGeneratorU64, + "RandPrintablesGeneratorU64", + MyStdStateU64 + ); + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index 026035a560..34628bb44f 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -314,3 +314,32 @@ pub extern "C" fn external_current_millis() -> u64 { // TODO: use "real" time here 1000 } + +#[cfg(feature = "python")] +use pyo3::prelude::*; + +#[cfg(feature = "python")] +#[pymodule] +#[pyo3(name = "libafl")] +/// Register the classes to the python module +pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> { + observers::map::pybind::register(py, m)?; + feedbacks::map::pybind::register(py, m)?; + state::pybind::register(py, m)?; + monitors::pybind::register(py, m)?; + events::pybind::register(py, m)?; + events::simple::pybind::register(py, m)?; + fuzzer::pybind::register(py, m)?; + executors::pybind::register(py, m)?; + executors::inprocess::pybind::register(py, m)?; + generators::pybind::register(py, m)?; + corpus::pybind::register(py, m)?; + corpus::ondisk::pybind::register(py, m)?; + corpus::inmemory::pybind::register(py, m)?; + corpus::cached::pybind::register(py, m)?; + bolts::rands::pybind::register(py, m)?; + stages::pybind::register(py, m)?; + stages::owned::pybind::register(py, m)?; + stages::mutational::pybind::register(py, m)?; + Ok(()) +} diff --git a/libafl/src/monitors/mod.rs b/libafl/src/monitors/mod.rs index 615b08821c..ab84cde6fb 100644 --- a/libafl/src/monitors/mod.rs +++ b/libafl/src/monitors/mod.rs @@ -784,3 +784,104 @@ impl Default for ClientPerfMonitor { Self::new() } } +/// `Monitor` Python bindings +#[cfg(feature = "python")] +pub mod pybind { + use crate::monitors::{Monitor, SimpleMonitor}; + use pyo3::prelude::*; + + use super::ClientStats; + use core::time::Duration; + + #[pyclass(unsendable, name = "SimpleMonitor")] + #[derive(Clone, Debug)] + /// Python class for SimpleMonitor + pub struct PythonSimpleMonitor { + /// Rust wrapped SimpleMonitor object + pub simple_monitor: SimpleMonitor, + } + + #[pymethods] + impl PythonSimpleMonitor { + #[new] + fn new(/*py_print_fn: PyObject */) -> Self { + // TODO: Find a fix to: closures can only be coerced to `fn` types if they + // do not capture any variables and print_fn expected to be fn pointer. + // fn printf_fn (s: String){ + // Python::with_gil(|py| -> PyResult<()> { + // print_fn.call1(py, (PyUnicode::new(py, &s),)); + // Ok(()) + // }); + // } + Self { + simple_monitor: SimpleMonitor::new(|s| println!("{}", s)), + } + } + } + + #[derive(Clone, Debug)] + enum PythonMonitorWrapper { + Simple(PythonSimpleMonitor), + } + + #[pyclass(unsendable, name = "Monitor")] + #[derive(Clone, Debug)] + /// EventManager Trait binding + pub struct PythonMonitor { + monitor: PythonMonitorWrapper, + } + + impl PythonMonitor { + fn get_monitor(&self) -> &impl Monitor { + match &self.monitor { + PythonMonitorWrapper::Simple(py_simple_monitor) => { + &py_simple_monitor.simple_monitor + } + } + } + + fn get_mut_monitor(&mut self) -> &mut impl Monitor { + match &mut self.monitor { + PythonMonitorWrapper::Simple(py_simple_monitor) => { + &mut py_simple_monitor.simple_monitor + } + } + } + } + + #[pymethods] + impl PythonMonitor { + #[staticmethod] + fn new_from_simple(simple_monitor: PythonSimpleMonitor) -> Self { + Self { + monitor: PythonMonitorWrapper::Simple(simple_monitor), + } + } + } + + impl Monitor for PythonMonitor { + fn client_stats_mut(&mut self) -> &mut Vec { + self.get_mut_monitor().client_stats_mut() + } + + fn client_stats(&self) -> &[ClientStats] { + self.get_monitor().client_stats() + } + + /// Time this fuzzing run stated + fn start_time(&mut self) -> Duration { + self.get_mut_monitor().start_time() + } + + fn display(&mut self, event_msg: String, sender_id: u32) { + self.get_mut_monitor().display(event_msg, sender_id); + } + } + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index c61290917c..04afbfb91d 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -769,7 +769,6 @@ where Self { base } } } - /// The Multi Map Observer merge different maps into one observer #[derive(Serialize, Deserialize, Debug)] #[serde(bound = "T: serde::de::DeserializeOwned")] @@ -977,3 +976,408 @@ where self.maps.iter().flatten() } } + +/// Exact copy of `StdMapObserver` that owns its map +/// Used for python bindings +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(bound = "T: serde::de::DeserializeOwned")] +#[allow(clippy::unsafe_derive_deserialize)] +pub struct OwnedMapObserver +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, +{ + map: Vec, + initial: T, + name: String, +} + +impl Observer for OwnedMapObserver +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, + Self: MapObserver, +{ + #[inline] + fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.reset_map() + } +} + +impl Named for OwnedMapObserver +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl HasLen for OwnedMapObserver +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn len(&self) -> usize { + self.map.as_slice().len() + } +} + +impl<'it, T> IntoIterator for &'it OwnedMapObserver +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, +{ + type Item = as Iterator>::Item; + type IntoIter = Iter<'it, T>; + + fn into_iter(self) -> Self::IntoIter { + self.as_slice().iter() + } +} + +impl<'it, T> IntoIterator for &'it mut OwnedMapObserver +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, +{ + type Item = as Iterator>::Item; + type IntoIter = IterMut<'it, T>; + + fn into_iter(self) -> Self::IntoIter { + self.as_mut_slice().iter_mut() + } +} + +impl MapObserver for OwnedMapObserver +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, +{ + type Entry = T; + + #[inline] + fn get(&self, pos: usize) -> &T { + &self.as_slice()[pos] + } + + #[inline] + fn get_mut(&mut self, idx: usize) -> &mut T { + &mut self.as_mut_slice()[idx] + } + + #[inline] + fn usable_count(&self) -> usize { + self.as_slice().len() + } + + fn hash(&self) -> u64 { + hash_slice(self.as_slice()) + } + + #[inline] + fn initial(&self) -> T { + self.initial + } + + #[inline] + fn initial_mut(&mut self) -> &mut T { + &mut self.initial + } + + #[inline] + fn set_initial(&mut self, initial: T) { + self.initial = initial; + } + + fn to_vec(&self) -> Vec { + self.as_slice().to_vec() + } +} + +impl AsSlice for OwnedMapObserver +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, +{ + #[must_use] + #[inline] + fn as_slice(&self) -> &[T] { + self.map.as_slice() + } +} +impl AsMutSlice for OwnedMapObserver +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug, +{ + #[must_use] + #[inline] + fn as_mut_slice(&mut self) -> &mut [T] { + self.map.as_mut_slice() + } +} + +impl OwnedMapObserver +where + T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned, +{ + /// Creates a new [`MapObserver`] with an owned map + #[must_use] + pub fn new(name: &'static str, map: Vec) -> Self { + let initial = if map.is_empty() { T::default() } else { map[0] }; + Self { + map: map, + name: name.to_string(), + initial, + } + } +} +/// `MapObserver` Python bindings +#[cfg(feature = "python")] +pub mod pybind { + use crate::bolts::{tuples::Named, HasLen}; + use crate::observers::{map::OwnedMapObserver, MapObserver, Observer}; + use crate::Error; + use pyo3::prelude::*; + use serde::{Deserialize, Serialize}; + + macro_rules! define_python_map_observer { + ($struct_name:ident, $py_name:tt, $struct_name_trait:ident, $py_name_trait:tt, $datatype:ty, $wrapper_name: ident) => { + #[pyclass(unsendable, name = $py_name)] + #[derive(Serialize, Deserialize, Debug, Clone)] + /// Python class for OwnedMapObserver (i.e. StdMapObserver with owned map) + pub struct $struct_name { + /// Rust wrapped OwnedMapObserver object + pub owned_map_observer: OwnedMapObserver<$datatype>, + } + + #[pymethods] + impl $struct_name { + #[new] + fn new(name: String, map: Vec<$datatype>) -> Self { + Self { + //TODO: Not leak memory + owned_map_observer: OwnedMapObserver::new( + Box::leak(name.into_boxed_str()), + map, + ), + } + } + } + + #[derive(Serialize, Deserialize, Debug, Clone)] + enum $wrapper_name { + Owned($struct_name), + } + + // Should not be exposed to user + #[pyclass(unsendable, name = $py_name_trait)] + #[derive(Serialize, Deserialize, Debug, Clone)] + /// MapObserver + Observer Trait binding + pub struct $struct_name_trait { + map_observer: $wrapper_name, + } + + #[pymethods] + impl $struct_name_trait { + #[staticmethod] + fn new_from_owned(owned_map_observer: $struct_name) -> Self { + Self { + map_observer: $wrapper_name::Owned(owned_map_observer), + } + } + } + + impl MapObserver for $struct_name_trait { + type Entry = $datatype; + + #[inline] + fn get(&self, idx: usize) -> &$datatype { + match &self.map_observer { + $wrapper_name::Owned(map_observer) => { + &map_observer.owned_map_observer.get(idx) + } + } + } + + #[inline] + fn get_mut(&mut self, idx: usize) -> &mut $datatype { + match &mut self.map_observer { + $wrapper_name::Owned(map_observer) => { + map_observer.owned_map_observer.get_mut(idx) + } + } + } + + #[inline] + fn usable_count(&self) -> usize { + match &self.map_observer { + $wrapper_name::Owned(map_observer) => { + map_observer.owned_map_observer.usable_count() + } + } + } + + fn hash(&self) -> u64 { + match &self.map_observer { + $wrapper_name::Owned(map_observer) => { + map_observer.owned_map_observer.hash() + } + } + } + + #[inline] + fn initial(&self) -> $datatype { + match &self.map_observer { + $wrapper_name::Owned(map_observer) => { + map_observer.owned_map_observer.initial() + } + } + } + + #[inline] + fn initial_mut(&mut self) -> &mut $datatype { + match &mut self.map_observer { + $wrapper_name::Owned(map_observer) => { + map_observer.owned_map_observer.initial_mut() + } + } + } + + #[inline] + fn set_initial(&mut self, initial: $datatype) { + match &mut self.map_observer { + $wrapper_name::Owned(map_observer) => { + map_observer.owned_map_observer.set_initial(initial); + } + } + } + + fn to_vec(&self) -> Vec<$datatype> { + match &self.map_observer { + $wrapper_name::Owned(map_observer) => { + map_observer.owned_map_observer.to_vec() + } + } + } + } + + impl Named for $struct_name_trait { + #[inline] + fn name(&self) -> &str { + match &self.map_observer { + $wrapper_name::Owned(map_observer) => { + map_observer.owned_map_observer.name() + } + } + } + } + + impl HasLen for $struct_name_trait { + #[inline] + fn len(&self) -> usize { + match &self.map_observer { + $wrapper_name::Owned(map_observer) => map_observer.owned_map_observer.len(), + } + } + } + + impl Observer for $struct_name_trait + where + Self: MapObserver, + { + #[inline] + fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + match &mut self.map_observer { + $wrapper_name::Owned(map_observer) => { + map_observer.owned_map_observer.pre_exec(_state, _input) + } + } + } + } + }; + } + + define_python_map_observer!( + PythonOwnedMapObserverI8, + "OwnedMapObserverI8", + PythonMapObserverI8, + "MapObserverI8", + i8, + PythonMapObserverWrapperI8 + ); + define_python_map_observer!( + PythonOwnedMapObserverI16, + "OwnedMapObserverI16", + PythonMapObserverI16, + "MapObserverI16", + i16, + PythonMapObserverWrapperI16 + ); + define_python_map_observer!( + PythonOwnedMapObserverI32, + "OwnedMapObserverI32", + PythonMapObserverI32, + "MapObserverI32", + i32, + PythonMapObserverWrapperI32 + ); + define_python_map_observer!( + PythonOwnedMapObserverI64, + "OwnedMapObserverI64", + PythonMapObserverI64, + "MapObserverI64", + i64, + PythonMapObserverWrapperI64 + ); + + define_python_map_observer!( + PythonOwnedMapObserverU8, + "OwnedMapObserverU8", + PythonMapObserverU8, + "MapObserverU8", + u8, + PythonMapObserverWrapperU8 + ); + define_python_map_observer!( + PythonOwnedMapObserverU16, + "OwnedMapObserverU16", + PythonMapObserverU16, + "MapObserverU16", + u16, + PythonMapObserverWrapperU16 + ); + define_python_map_observer!( + PythonOwnedMapObserverU32, + "OwnedMapObserverU32", + PythonMapObserverU32, + "MapObserverU32", + u32, + PythonMapObserverWrapperU32 + ); + define_python_map_observer!( + PythonOwnedMapObserverU64, + "OwnedMapObserverU64", + PythonMapObserverU64, + "MapObserverU64", + u64, + PythonMapObserverWrapperU64 + ); + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index beab1dc228..d1803835b4 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -256,3 +256,190 @@ where .deinit(fuzzer, state, event_mgr, executor.observers_mut()) } } + +/// `Stage` Python bindings +#[cfg(feature = "python")] +pub mod pybind { + use crate::impl_asany; + use crate::stages::Stage; + use crate::Error; + use pyo3::prelude::*; + + use super::owned::AnyStage; + + macro_rules! define_python_stage { + ($struct_name_trait:ident, $py_name_trait:tt, $wrapper_name: ident, $std_havoc_mutations_stage_name: ident, $my_std_state_type_name: ident, + $my_std_fuzzer_type_name: ident, $executor_name: ident, $event_manager_name: ident) => { + use crate::events::pybind::$event_manager_name; + use crate::executors::pybind::$executor_name; + use crate::fuzzer::pybind::$my_std_fuzzer_type_name; + use crate::stages::mutational::pybind::$std_havoc_mutations_stage_name; + use crate::state::pybind::$my_std_state_type_name; + + #[derive(Debug)] + enum $wrapper_name { + StdHavocMutations(*mut $std_havoc_mutations_stage_name), + } + + /// Stage Trait binding + #[pyclass(unsendable, name = $py_name_trait)] + #[derive(Debug)] + pub struct $struct_name_trait { + stage: $wrapper_name, + } + + #[pymethods] + impl $struct_name_trait { + #[staticmethod] + fn new_from_std_scheduled( + py_std_havoc_mutations_stage: &mut $std_havoc_mutations_stage_name, + ) -> Self { + Self { + stage: $wrapper_name::StdHavocMutations(py_std_havoc_mutations_stage), + } + } + } + + impl + Stage< + $executor_name, + $event_manager_name, + $my_std_state_type_name, + $my_std_fuzzer_type_name, + > for $struct_name_trait + { + #[inline] + #[allow(clippy::let_and_return)] + fn perform( + &mut self, + fuzzer: &mut $my_std_fuzzer_type_name, + executor: &mut $executor_name, + state: &mut $my_std_state_type_name, + manager: &mut $event_manager_name, + corpus_idx: usize, + ) -> Result<(), Error> { + unsafe { + match self.stage { + $wrapper_name::StdHavocMutations(py_std_havoc_mutations_stage) => { + (*py_std_havoc_mutations_stage) + .std_mutational_stage + .perform(fuzzer, executor, state, manager, corpus_idx) + } + } + } + } + } + + impl_asany!($struct_name_trait); + + impl + AnyStage< + $executor_name, + $event_manager_name, + $my_std_state_type_name, + $my_std_fuzzer_type_name, + > for $struct_name_trait + { + } + }; + } + + define_python_stage!( + PythonStageI8, + "StageI8", + PythonStageWrapperI8, + PythonStdScheduledHavocMutationsStageI8, + MyStdStateI8, + MyStdFuzzerI8, + PythonExecutorI8, + PythonEventManagerI8 + ); + + define_python_stage!( + PythonStageI16, + "StageI16", + PythonStageWrapperI16, + PythonStdScheduledHavocMutationsStageI16, + MyStdStateI16, + MyStdFuzzerI16, + PythonExecutorI16, + PythonEventManagerI16 + ); + + define_python_stage!( + PythonStageI32, + "StageI32", + PythonStageWrapperI32, + PythonStdScheduledHavocMutationsStageI32, + MyStdStateI32, + MyStdFuzzerI32, + PythonExecutorI32, + PythonEventManagerI32 + ); + + define_python_stage!( + PythonStageI64, + "StageI64", + PythonStageWrapperI64, + PythonStdScheduledHavocMutationsStageI64, + MyStdStateI64, + MyStdFuzzerI64, + PythonExecutorI64, + PythonEventManagerI64 + ); + + define_python_stage!( + PythonStageU8, + "StageU8", + PythonStageWrapperU8, + PythonStdScheduledHavocMutationsStageU8, + MyStdStateU8, + MyStdFuzzerU8, + PythonExecutorU8, + PythonEventManagerU8 + ); + define_python_stage!( + PythonStageU16, + "StageU16", + PythonStageWrapperU16, + PythonStdScheduledHavocMutationsStageU16, + MyStdStateU16, + MyStdFuzzerU16, + PythonExecutorU16, + PythonEventManagerU16 + ); + define_python_stage!( + PythonStageU32, + "StageU32", + PythonStageWrapperU32, + PythonStdScheduledHavocMutationsStageU32, + MyStdStateU32, + MyStdFuzzerU32, + PythonExecutorU32, + PythonEventManagerU32 + ); + define_python_stage!( + PythonStageU64, + "StageU64", + PythonStageWrapperU64, + PythonStdScheduledHavocMutationsStageU64, + MyStdStateU64, + MyStdFuzzerU64, + PythonExecutorU64, + PythonEventManagerU64 + ); + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/stages/mutational.rs b/libafl/src/stages/mutational.rs index 89a6407c8e..1513756f10 100644 --- a/libafl/src/stages/mutational.rs +++ b/libafl/src/stages/mutational.rs @@ -161,3 +161,163 @@ where } } } + +#[cfg(feature = "python")] +/// `StdMutationalStage` Python bindings +pub mod pybind { + use crate::bolts::tuples::tuple_list_type; + use crate::inputs::BytesInput; + pub use crate::mutators::mutations::*; + pub use crate::mutators::mutations::*; + use crate::mutators::{havoc_mutations, StdScheduledMutator}; + use crate::stages::StdMutationalStage; + use pyo3::prelude::*; + + type HavocMutationsType = tuple_list_type!( + BitFlipMutator, + ByteFlipMutator, + ByteIncMutator, + ByteDecMutator, + ByteNegMutator, + ByteRandMutator, + ByteAddMutator, + WordAddMutator, + DwordAddMutator, + QwordAddMutator, + ByteInterestingMutator, + WordInterestingMutator, + DwordInterestingMutator, + BytesDeleteMutator, + BytesDeleteMutator, + BytesDeleteMutator, + BytesDeleteMutator, + BytesExpandMutator, + BytesInsertMutator, + BytesRandInsertMutator, + BytesSetMutator, + BytesRandSetMutator, + BytesCopyMutator, + BytesInsertCopyMutator, + BytesSwapMutator, + CrossoverInsertMutator, + CrossoverReplaceMutator, + ); + + macro_rules! define_python_std_mutational_stage { + ($struct_name:ident, $py_name:tt, $my_std_state_type_name: ident, $my_std_fuzzer_type_name: ident, $executor_name: ident, $event_manager_name: ident) => { + use crate::events::pybind::$event_manager_name; + use crate::executors::pybind::$executor_name; + use crate::fuzzer::pybind::$my_std_fuzzer_type_name; + use crate::state::pybind::$my_std_state_type_name; + + #[pyclass(unsendable, name = $py_name)] + #[derive(Debug)] + /// Python class for StdMutationalStage + pub struct $struct_name { + /// Rust wrapped StdMutationalStage object + pub std_mutational_stage: StdMutationalStage< + $executor_name, + $event_manager_name, + BytesInput, + StdScheduledMutator, + $my_std_state_type_name, + $my_std_fuzzer_type_name, + >, + } + + #[pymethods] + impl $struct_name { + #[staticmethod] + fn new_from_scheduled_havoc_mutations() -> Self { + Self { + std_mutational_stage: StdMutationalStage::new(StdScheduledMutator::new( + havoc_mutations(), + )), + } + } + } + }; + } + + define_python_std_mutational_stage!( + PythonStdScheduledHavocMutationsStageI8, + "StdScheduledHavocMutationsStageI8", + MyStdStateI8, + MyStdFuzzerI8, + PythonExecutorI8, + PythonEventManagerI8 + ); + + define_python_std_mutational_stage!( + PythonStdScheduledHavocMutationsStageI16, + "StdScheduledHavocMutationsStageI16", + MyStdStateI16, + MyStdFuzzerI16, + PythonExecutorI16, + PythonEventManagerI16 + ); + define_python_std_mutational_stage!( + PythonStdScheduledHavocMutationsStageI32, + "StdScheduledHavocMutationsStageI32", + MyStdStateI32, + MyStdFuzzerI32, + PythonExecutorI32, + PythonEventManagerI32 + ); + define_python_std_mutational_stage!( + PythonStdScheduledHavocMutationsStageI64, + "StdScheduledHavocMutationsStageI64", + MyStdStateI64, + MyStdFuzzerI64, + PythonExecutorI64, + PythonEventManagerI64 + ); + + define_python_std_mutational_stage!( + PythonStdScheduledHavocMutationsStageU8, + "StdScheduledHavocMutationsStageU8", + MyStdStateU8, + MyStdFuzzerU8, + PythonExecutorU8, + PythonEventManagerU8 + ); + + define_python_std_mutational_stage!( + PythonStdScheduledHavocMutationsStageU16, + "StdScheduledHavocMutationsStageU16", + MyStdStateU16, + MyStdFuzzerU16, + PythonExecutorU16, + PythonEventManagerU16 + ); + define_python_std_mutational_stage!( + PythonStdScheduledHavocMutationsStageU32, + "StdScheduledHavocMutationsStageU32", + MyStdStateU32, + MyStdFuzzerU32, + PythonExecutorU32, + PythonEventManagerU32 + ); + define_python_std_mutational_stage!( + PythonStdScheduledHavocMutationsStageU64, + "StdScheduledHavocMutationsStageU64", + MyStdStateU64, + MyStdFuzzerU64, + PythonExecutorU64, + PythonEventManagerU64 + ); + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/stages/owned.rs b/libafl/src/stages/owned.rs index efa4f5841b..5dc2a68b29 100644 --- a/libafl/src/stages/owned.rs +++ b/libafl/src/stages/owned.rs @@ -42,3 +42,147 @@ impl StagesOwnedList { Self { list } } } + +#[cfg(feature = "python")] +/// `StagesOwnedList` Python bindings +pub mod pybind { + use crate::stages::owned::StagesOwnedList; + use pyo3::prelude::*; + + macro_rules! define_python_stage_owned_list { + ($struct_name:ident, $py_name:tt, $my_std_state_type_name: ident, $my_std_fuzzer_type_name: ident, $event_manager_name: ident, + $executor_name: ident, $stage_name: ident) => { + use crate::events::pybind::$event_manager_name; + use crate::executors::pybind::$executor_name; + use crate::fuzzer::pybind::$my_std_fuzzer_type_name; + use crate::stages::pybind::$stage_name; + use crate::state::pybind::$my_std_state_type_name; + #[pyclass(unsendable, name = $py_name)] + + /// Python class for StagesOwnedList + #[allow(missing_debug_implementations)] + pub struct $struct_name { + /// Rust wrapped StagesOwnedList object + pub stages_owned_list: StagesOwnedList< + $executor_name, + $event_manager_name, + $my_std_state_type_name, + $my_std_fuzzer_type_name, + >, + } + + #[pymethods] + impl $struct_name { + //TODO: Add new from list + #[new] + fn new(stage: &$stage_name) -> Self { + // TODO: Be safe + unsafe { + Self { + stages_owned_list: StagesOwnedList { + list: vec![Box::new(std::mem::transmute_copy::< + $stage_name, + $stage_name, + >(stage))], + }, + } + } + } + } + }; + } + + define_python_stage_owned_list!( + PythonStagesOwnedListI8, + "StagesOwnedListI8", + MyStdStateI8, + MyStdFuzzerI8, + PythonEventManagerI8, + PythonExecutorI8, + PythonStageI8 + ); + + define_python_stage_owned_list!( + PythonStagesOwnedListI16, + "StagesOwnedListI16", + MyStdStateI16, + MyStdFuzzerI16, + PythonEventManagerI16, + PythonExecutorI16, + PythonStageI16 + ); + + define_python_stage_owned_list!( + PythonStagesOwnedListI32, + "StagesOwnedListI32", + MyStdStateI32, + MyStdFuzzerI32, + PythonEventManagerI32, + PythonExecutorI32, + PythonStageI32 + ); + + define_python_stage_owned_list!( + PythonStagesOwnedListI64, + "StagesOwnedListI64", + MyStdStateI64, + MyStdFuzzerI64, + PythonEventManagerI64, + PythonExecutorI64, + PythonStageI64 + ); + + define_python_stage_owned_list!( + PythonStagesOwnedListU8, + "StagesOwnedListU8", + MyStdStateU8, + MyStdFuzzerU8, + PythonEventManagerU8, + PythonExecutorU8, + PythonStageU8 + ); + + define_python_stage_owned_list!( + PythonStagesOwnedListU16, + "StagesOwnedListU16", + MyStdStateU16, + MyStdFuzzerU16, + PythonEventManagerU16, + PythonExecutorU16, + PythonStageU16 + ); + + define_python_stage_owned_list!( + PythonStagesOwnedListU32, + "StagesOwnedListU32", + MyStdStateU32, + MyStdFuzzerU32, + PythonEventManagerU32, + PythonExecutorU32, + PythonStageU32 + ); + + define_python_stage_owned_list!( + PythonStagesOwnedListU64, + "StagesOwnedListU64", + MyStdStateU64, + MyStdFuzzerU64, + PythonEventManagerU64, + PythonExecutorU64, + PythonStageU64 + ); + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +} diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 7b201dfdac..011f7b10bc 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -638,3 +638,188 @@ where &mut self.stability } } + +#[cfg(feature = "python")] +/// `State` Python bindings +pub mod pybind { + use crate::bolts::rands::pybind::PythonRand; + use crate::bolts::tuples::tuple_list; + use crate::corpus::pybind::PythonCorpus; + use crate::feedbacks::map::MapFeedbackState; + use crate::inputs::BytesInput; + use crate::state::StdState; + use pyo3::prelude::*; + + macro_rules! define_python_state { + ($type_name:ident, $struct_name:ident, $py_name:tt, $datatype:ty, $py_map_feedback_state_name:ident, $event_manager_name: ident, + $fuzzer_name: ident, $executor_name: ident, $rand_printable_generator: ident) => { + use crate::events::pybind::$event_manager_name; + use crate::executors::pybind::$executor_name; + use crate::feedbacks::map::pybind::$py_map_feedback_state_name; + use crate::fuzzer::pybind::$fuzzer_name; + use crate::generators::pybind::$rand_printable_generator; + + /// `StdState` with fixed generics + pub type $type_name = StdState< + PythonCorpus, + (MapFeedbackState<$datatype>, ()), + BytesInput, + PythonRand, + PythonCorpus, + >; + + #[pyclass(unsendable, name = $py_name)] + #[derive(Debug)] + /// Python class for StdState + pub struct $struct_name { + /// Rust wrapped StdState object + pub std_state: $type_name, + } + + #[pymethods] + impl $struct_name { + #[new] + fn new( + py_rand: PythonRand, + corpus: PythonCorpus, + solutions: PythonCorpus, + py_map_feedback_state: $py_map_feedback_state_name, + ) -> Self { + Self { + std_state: StdState::new( + py_rand, + corpus, + solutions, + tuple_list!(py_map_feedback_state.map_feedback_state), + ), + } + } + + fn generate_initial_inputs( + &mut self, + py_fuzzer: &mut $fuzzer_name, + py_executor: &mut $executor_name, + py_generator: &mut $rand_printable_generator, + py_mgr: &mut $event_manager_name, + num: usize, + ) { + self.std_state + .generate_initial_inputs( + &mut py_fuzzer.std_fuzzer, + py_executor, + &mut py_generator.rand_printable_generator, + py_mgr, + num, + ) + .expect("Failed to generate the initial corpus".into()); + } + } + }; + } + + define_python_state!( + MyStdStateI8, + PythonStdStateI8, + "StdStateI8", + i8, + PythonMapFeedbackStateI8, + PythonEventManagerI8, + PythonStdFuzzerI8, + PythonExecutorI8, + PythonRandPrintablesGeneratorI8 + ); + + define_python_state!( + MyStdStateI16, + PythonStdStateI16, + "StdStateI16", + i16, + PythonMapFeedbackStateI16, + PythonEventManagerI16, + PythonStdFuzzerI16, + PythonExecutorI16, + PythonRandPrintablesGeneratorI16 + ); + + define_python_state!( + MyStdStateI32, + PythonStdStateI32, + "StdStateI32", + i32, + PythonMapFeedbackStateI32, + PythonEventManagerI32, + PythonStdFuzzerI32, + PythonExecutorI32, + PythonRandPrintablesGeneratorI32 + ); + + define_python_state!( + MyStdStateI64, + PythonStdStateI64, + "StdStateI64", + i64, + PythonMapFeedbackStateI64, + PythonEventManagerI64, + PythonStdFuzzerI64, + PythonExecutorI64, + PythonRandPrintablesGeneratorI64 + ); + define_python_state!( + MyStdStateU8, + PythonStdStateU8, + "StdStateU8", + u8, + PythonMapFeedbackStateU8, + PythonEventManagerU8, + PythonStdFuzzerU8, + PythonExecutorU8, + PythonRandPrintablesGeneratorU8 + ); + define_python_state!( + MyStdStateU16, + PythonStdStateU16, + "StdStateU16", + u16, + PythonMapFeedbackStateU16, + PythonEventManagerU16, + PythonStdFuzzerU16, + PythonExecutorU16, + PythonRandPrintablesGeneratorU16 + ); + define_python_state!( + MyStdStateU32, + PythonStdStateU32, + "StdStateU32", + u32, + PythonMapFeedbackStateU32, + PythonEventManagerU32, + PythonStdFuzzerU32, + PythonExecutorU32, + PythonRandPrintablesGeneratorU32 + ); + define_python_state!( + MyStdStateU64, + PythonStdStateU64, + "StdStateU64", + u64, + PythonMapFeedbackStateU64, + PythonEventManagerU64, + PythonStdFuzzerU64, + PythonExecutorU64, + PythonRandPrintablesGeneratorU64 + ); + + /// Register the classes to the python module + pub fn register(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + Ok(()) + } +}