/*! Welcome to `LibAFL` */ #![allow(incomplete_features)] #![no_std] // For `type_eq` #![cfg_attr(unstable_feature, feature(specialization))] // For `type_id` and owned things #![cfg_attr(unstable_feature, feature(intrinsics))] // For `std::simd` #![cfg_attr(unstable_feature, feature(portable_simd))] #![warn(clippy::cargo)] #![deny(clippy::cargo_common_metadata)] #![deny(rustdoc::broken_intra_doc_links)] #![deny(clippy::all)] #![deny(clippy::pedantic)] #![allow( clippy::unreadable_literal, clippy::type_repetition_in_bounds, clippy::missing_errors_doc, clippy::cast_possible_truncation, clippy::used_underscore_binding, clippy::ptr_as_ptr, clippy::missing_panics_doc, clippy::missing_docs_in_private_items, clippy::module_name_repetitions, clippy::unreadable_literal )] #![cfg_attr(debug_assertions, warn( missing_debug_implementations, missing_docs, //trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces, unused_qualifications, //unused_results ))] #![cfg_attr(not(debug_assertions), deny( missing_debug_implementations, missing_docs, //trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces, unused_qualifications, unused_must_use, missing_docs, //unused_results ))] #![cfg_attr( not(debug_assertions), deny( bad_style, const_err, dead_code, improper_ctypes, non_shorthand_field_patterns, no_mangle_generic_items, overflowing_literals, path_statements, patterns_in_fns_without_body, private_in_public, unconditional_recursion, unused, unused_allocation, unused_comparisons, unused_parens, while_true ) )] // Till they fix this buggy lint in clippy #![allow(clippy::borrow_as_ptr)] #![allow(clippy::borrow_deref_ref)] #[cfg(feature = "std")] #[macro_use] extern crate std; #[macro_use] pub extern crate alloc; #[macro_use] extern crate static_assertions; #[cfg(feature = "ctor")] pub use ctor::ctor; // Re-export derive(SerdeAny) #[cfg(feature = "libafl_derive")] #[allow(unused_imports)] #[macro_use] extern crate libafl_derive; #[cfg(feature = "libafl_derive")] #[doc(hidden)] pub use libafl_derive::*; pub mod bolts; pub mod corpus; pub mod events; pub mod executors; pub mod feedbacks; pub mod generators; pub mod inputs; pub mod monitors; pub mod mutators; pub mod observers; pub mod schedulers; pub mod stages; pub mod state; pub mod fuzzer; use alloc::string::{FromUtf8Error, String}; use core::{ array::TryFromSliceError, fmt, num::{ParseIntError, TryFromIntError}, }; #[cfg(feature = "std")] use std::{env::VarError, io}; pub use fuzzer::*; #[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)] /// Empty struct 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"))] fn display_error_backtrace(_f: &mut fmt::Formatter, _err: &ErrorBacktrace) -> fmt::Result { fmt::Result::Ok(()) } /// Main error struct for `LibAFL` #[derive(Debug)] pub enum Error { /// Serialization error Serialize(String, ErrorBacktrace), /// Compression error #[cfg(feature = "llmp_compression")] Compression(ErrorBacktrace), /// File related error #[cfg(feature = "std")] File(io::Error, ErrorBacktrace), /// Optional val was supposed to be set, but isn't. EmptyOptional(String, ErrorBacktrace), /// Key not in Map KeyNotFound(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, /// Something else happened Unknown(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 = "llmp_compression")] /// Compression error #[must_use] pub fn compression() -> Self { Error::Compression(ErrorBacktrace::new()) } #[cfg(feature = "std")] /// File related error #[must_use] pub fn file(arg: io::Error) -> Self { Error::File(arg, 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()) } /// Key not in Map #[must_use] pub fn key_not_found(arg: S) -> Self where S: Into, { Error::KeyNotFound(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()) } /// Something else happened #[must_use] pub fn unknown(arg: S) -> Self where S: Into, { Error::Unknown(arg.into(), ErrorBacktrace::new()) } } impl fmt::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 = "llmp_compression")] Self::Compression(b) => { write!(f, "Error in decompression")?; display_error_backtrace(f, b) } #[cfg(feature = "std")] Self::File(err, b) => { write!(f, "File IO failed: {:?}", &err)?; 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 in Corpus", &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!"), Self::Unknown(s, b) => { write!(f, "Unknown error: {0}", &s)?; display_error_backtrace(f, b) } } } } /// Stringify the postcard serializer error impl From for Error { fn from(err: postcard::Error) -> Self { Self::serialize(format!("{:?}", err)) } } /// Stringify the json serializer error #[cfg(feature = "std")] impl From for Error { fn from(err: serde_json::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::file(err) } } impl From for Error { fn from(err: FromUtf8Error) -> 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 { fn from(err: ParseIntError) -> Self { Self::unknown(format!("Failed to parse Int: {:?}", err)) } } impl From for Error { fn from(err: TryFromIntError) -> Self { Self::illegal_state(format!("Expected conversion failed: {:?}", err)) } } impl From for Error { fn from(err: TryFromSliceError) -> Self { Self::illegal_argument(format!("Could not convert slice: {:?}", err)) } } #[cfg(windows)] impl From for Error { fn from(err: windows::core::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), ) { Self::shutting_down() } else { Self::illegal_state(format!("Python exception: {:?}", err)) } }) } } #[cfg(feature = "std")] impl std::error::Error for Error {} /// The purpose of this module is to alleviate imports of many components by adding a glob import. pub mod prelude { pub use super::{ bolts::{bolts_prelude::*, *}, corpus::*, events::*, executors::*, feedbacks::*, fuzzer::*, generators::*, inputs::*, monitors::*, mutators::*, observers::*, schedulers::*, stages::*, state::*, *, }; } // TODO: no_std test #[cfg(feature = "std")] #[cfg(test)] mod tests { #[cfg(feature = "std")] use crate::events::SimpleEventManager; use crate::{ bolts::{rands::StdRand, tuples::tuple_list}, corpus::{Corpus, InMemoryCorpus, Testcase}, executors::{ExitKind, InProcessExecutor}, inputs::BytesInput, monitors::SimpleMonitor, mutators::{mutations::BitFlipMutator, StdScheduledMutator}, schedulers::RandScheduler, stages::StdMutationalStage, state::{HasCorpus, StdState}, Fuzzer, StdFuzzer, }; #[test] #[allow(clippy::similar_names)] fn test_fuzzer() { let rand = StdRand::with_seed(0); let mut corpus = InMemoryCorpus::::new(); let testcase = Testcase::new(vec![0; 4]); corpus.add(testcase).unwrap(); let mut state = StdState::new( rand, corpus, InMemoryCorpus::::new(), &mut (), &mut (), ) .unwrap(); let monitor = SimpleMonitor::new(|s| { println!("{}", s); }); let mut event_manager = SimpleEventManager::new(monitor); let scheduler = RandScheduler::new(); let mut fuzzer = StdFuzzer::new(scheduler, (), ()); let mut harness = |_buf: &BytesInput| ExitKind::Ok; let mut executor = InProcessExecutor::new( &mut harness, tuple_list!(), &mut fuzzer, &mut state, &mut event_manager, ) .unwrap(); let mutator = StdScheduledMutator::new(tuple_list!(BitFlipMutator::new())); let mut stages = tuple_list!(StdMutationalStage::new(mutator)); for i in 0..1000 { fuzzer .fuzz_one(&mut stages, &mut executor, &mut state, &mut event_manager) .unwrap_or_else(|_| panic!("Error in iter {}", i)); } let state_serialized = postcard::to_allocvec(&state).unwrap(); let state_deserialized: StdState< InMemoryCorpus, BytesInput, StdRand, InMemoryCorpus, > = postcard::from_bytes(state_serialized.as_slice()).unwrap(); assert_eq!(state.corpus().count(), state_deserialized.corpus().count()); let corpus_serialized = postcard::to_allocvec(state.corpus()).unwrap(); let corpus_deserialized: InMemoryCorpus = postcard::from_bytes(corpus_serialized.as_slice()).unwrap(); assert_eq!(state.corpus().count(), corpus_deserialized.count()); } } #[cfg(all(test, not(feature = "std")))] /// Provide custom time in `no_std` tests. #[no_mangle] pub extern "C" fn external_current_millis() -> u64 { // TODO: use "real" time here 1000 } #[cfg(feature = "python")] #[allow(missing_docs)] pub mod pybind { use pyo3::prelude::*; use super::{ bolts, corpus, events, executors, feedbacks, fuzzer, generators, monitors, mutators, observers, stages, state, }; #[derive(Debug, Clone)] pub struct PythonMetadata { pub map: PyObject, } crate::impl_serde_pyobjectwrapper!(PythonMetadata, map); crate::impl_serdeany!(PythonMetadata); impl PythonMetadata { #[must_use] pub fn new(map: PyObject) -> Self { Self { map } } } #[macro_export] macro_rules! unwrap_me_body { ($wrapper:expr, $name:ident, $body:block, $wrapper_type:ident, { $($wrapper_option:tt),* }) => { match &$wrapper { $( $wrapper_type::$wrapper_option(py_wrapper) => { Python::with_gil(|py| -> PyResult<_> { let borrowed = py_wrapper.borrow(py); let $name = &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 &$wrapper { $( $wrapper_type::$wrapper_option(py_wrapper) => { Python::with_gil(|py| -> PyResult<_> { let borrowed = py_wrapper.borrow(py); let $name = &borrowed.inner; Ok($body) }) .unwrap() } )* $($wrapper_type::$wrapper_optional($pw) => { $code_block })* } }; } #[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) => { const _: () = { use alloc::vec::Vec; use pyo3::prelude::*; use serde::{Deserialize, Deserializer, Serialize, Serializer}; impl Serialize for $struct_name { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let buf = Python::with_gil(|py| -> PyResult> { let pickle = PyModule::import(py, "pickle")?; let buf: Vec = pickle.getattr("dumps")?.call1((&self.$inner,))?.extract()?; Ok(buf) }) .unwrap(); serializer.serialize_bytes(&buf) } } struct PyObjectVisitor; impl<'de> serde::de::Visitor<'de> for PyObjectVisitor { type Value = $struct_name; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter .write_str("Expecting some bytes to deserialize from the Python side") } fn visit_byte_buf(self, v: Vec) -> Result where E: serde::de::Error, { let obj = Python::with_gil(|py| -> PyResult { let pickle = PyModule::import(py, "pickle")?; let obj = pickle.getattr("loads")?.call1((v,))?.to_object(py); Ok(obj) }) .unwrap(); Ok($struct_name::new(obj)) } } impl<'de> Deserialize<'de> for $struct_name { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_byte_buf(PyObjectVisitor) } } }; }; } #[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)?; observers::pybind::register(py, m)?; feedbacks::map::pybind::register(py, m)?; feedbacks::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)?; mutators::pybind::register(py, m)?; mutators::scheduled::pybind::register(py, m)?; corpus::pybind::register(py, m)?; corpus::testcase::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::mutational::pybind::register(py, m)?; Ok(()) } }