#![doc = include_str!("../README.md")]
#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
#![allow(incomplete_features)]
#![no_std]
#![cfg_attr(nightly, feature(specialization))]
#![cfg_attr(nightly, feature(portable_simd))]
#![cfg_attr(nightly, feature(error_in_core))]
#![warn(clippy::cargo)]
#![allow(ambiguous_glob_reexports)]
#![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::ptr_cast_constness,
clippy::negative_feature_names
)]
#![cfg_attr(not(test), warn(
missing_debug_implementations,
missing_docs,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
))]
#![cfg_attr(test, deny(
missing_debug_implementations,
missing_docs,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
unused_must_use,
))]
#![cfg_attr(
test,
deny(
bad_style,
dead_code,
improper_ctypes,
non_shorthand_field_patterns,
no_mangle_generic_items,
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
unconditional_recursion,
unused,
unused_allocation,
unused_comparisons,
unused_parens,
while_true
)
)]
#![allow(clippy::borrow_as_ptr)]
#![allow(clippy::borrow_deref_ref)]
#[cfg(not(feature = "alloc"))]
type String = &'static str;
#[cfg(not(feature = "alloc"))]
macro_rules! format {
($fmt:literal) => {{
$fmt
}};
}
#[cfg(feature = "std")]
#[macro_use]
extern crate std;
#[cfg(feature = "alloc")]
#[macro_use]
#[doc(hidden)]
pub extern crate alloc;
#[cfg(feature = "ctor")]
#[doc(hidden)]
pub use ctor::ctor;
#[cfg(feature = "alloc")]
pub mod anymap;
#[cfg(feature = "std")]
pub mod build_id;
#[cfg(all(
any(feature = "cli", feature = "frida_cli", feature = "qemu_cli"),
feature = "std"
))]
pub mod cli;
#[cfg(feature = "gzip")]
pub mod compress;
#[cfg(feature = "std")]
pub mod core_affinity;
pub mod cpu;
#[cfg(feature = "std")]
pub mod fs;
#[cfg(feature = "alloc")]
pub mod llmp;
pub mod math;
#[cfg(all(feature = "std", unix))]
pub mod minibsod;
pub mod os;
#[cfg(feature = "alloc")]
pub mod ownedref;
pub mod rands;
#[cfg(feature = "alloc")]
pub mod serdeany;
pub mod shmem;
#[cfg(feature = "std")]
pub mod staterestore;
#[cfg(any(feature = "xxh3", feature = "alloc"))]
pub mod tuples;
#[cfg(feature = "prelude")]
pub mod bolts_prelude {
#[cfg(feature = "std")]
pub use super::build_id::*;
#[cfg(all(
any(feature = "cli", feature = "frida_cli", feature = "qemu_cli"),
feature = "std"
))]
pub use super::cli::*;
#[cfg(feature = "gzip")]
pub use super::compress::*;
#[cfg(feature = "std")]
pub use super::core_affinity::*;
#[cfg(feature = "std")]
pub use super::fs::*;
#[cfg(all(feature = "std", unix))]
pub use super::minibsod::*;
#[cfg(feature = "std")]
pub use super::staterestore::*;
#[cfg(feature = "alloc")]
pub use super::{anymap::*, llmp::*, ownedref::*, rands::*, serdeany::*, shmem::*, tuples::*};
pub use super::{cpu::*, os::*};
}
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(all(not(feature = "xxh3"), feature = "alloc"))]
use core::hash::BuildHasher;
#[cfg(any(feature = "xxh3", feature = "alloc"))]
use core::hash::Hasher;
#[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH};
#[cfg(all(unix, feature = "std"))]
use std::{
fs::File,
io::Write,
mem,
os::fd::{FromRawFd, RawFd},
};
#[cfg(all(not(feature = "xxh3"), feature = "alloc"))]
use ahash::RandomState;
use serde::{Deserialize, Serialize};
#[cfg(feature = "xxh3")]
use xxhash_rust::xxh3::xxh3_64;
#[repr(transparent)]
#[derive(
Debug, Default, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
)]
pub struct ClientId(pub u32);
#[cfg(feature = "std")]
use log::{Metadata, Record};
#[deprecated(
since = "0.11.0",
note = "The launcher module has moved out of `libafl_bolts` into `libafl::events::launcher`."
)]
pub mod launcher {}
use core::{
array::TryFromSliceError,
fmt::{self, Display},
iter::Iterator,
num::{ParseIntError, TryFromIntError},
time,
};
#[cfg(feature = "std")]
use std::{env::VarError, io};
#[cfg(feature = "libafl_derive")]
pub use libafl_derive::SerdeAny;
#[cfg(feature = "alloc")]
use {
alloc::string::{FromUtf8Error, String},
core::cell::{BorrowError, BorrowMutError},
core::str::Utf8Error,
};
pub trait Named {
fn name(&self) -> &str;
}
#[cfg(feature = "errors_backtrace")]
pub type ErrorBacktrace = backtrace::Backtrace;
#[cfg(not(feature = "errors_backtrace"))]
#[derive(Debug, Default)]
pub struct ErrorBacktrace {}
#[cfg(not(feature = "errors_backtrace"))]
impl ErrorBacktrace {
#[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"))]
#[allow(clippy::unnecessary_wraps)]
fn display_error_backtrace(_f: &mut fmt::Formatter, _err: &ErrorBacktrace) -> fmt::Result {
fmt::Result::Ok(())
}
#[cfg(any(feature = "xxh3", feature = "alloc"))]
#[must_use]
pub fn hasher_std() -> impl Hasher + Clone {
#[cfg(feature = "xxh3")]
return xxhash_rust::xxh3::Xxh3::new();
#[cfg(not(feature = "xxh3"))]
RandomState::with_seeds(0, 0, 0, 0).build_hasher()
}
#[cfg(any(feature = "xxh3", feature = "alloc"))]
#[must_use]
pub fn hash_std(input: &[u8]) -> u64 {
#[cfg(feature = "xxh3")]
return xxh3_64(input);
#[cfg(not(feature = "xxh3"))]
{
let mut hasher = hasher_std();
hasher.write(input);
hasher.finish()
}
}
#[derive(Debug)]
pub enum Error {
Serialize(String, ErrorBacktrace),
#[cfg(feature = "gzip")]
Compression(ErrorBacktrace),
#[cfg(feature = "std")]
File(io::Error, ErrorBacktrace),
EmptyOptional(String, ErrorBacktrace),
KeyNotFound(String, ErrorBacktrace),
Empty(String, ErrorBacktrace),
IteratorEnd(String, ErrorBacktrace),
NotImplemented(String, ErrorBacktrace),
IllegalState(String, ErrorBacktrace),
IllegalArgument(String, ErrorBacktrace),
Unsupported(String, ErrorBacktrace),
ShuttingDown,
Unknown(String, ErrorBacktrace),
}
impl Error {
#[must_use]
pub fn serialize<S>(arg: S) -> Self
where
S: Into<String>,
{
Error::Serialize(arg.into(), ErrorBacktrace::new())
}
#[cfg(feature = "gzip")]
#[must_use]
pub fn compression() -> Self {
Error::Compression(ErrorBacktrace::new())
}
#[cfg(feature = "std")]
#[must_use]
pub fn file(arg: io::Error) -> Self {
Error::File(arg, ErrorBacktrace::new())
}
#[must_use]
pub fn empty_optional<S>(arg: S) -> Self
where
S: Into<String>,
{
Error::EmptyOptional(arg.into(), ErrorBacktrace::new())
}
#[must_use]
pub fn key_not_found<S>(arg: S) -> Self
where
S: Into<String>,
{
Error::KeyNotFound(arg.into(), ErrorBacktrace::new())
}
#[must_use]
pub fn empty<S>(arg: S) -> Self
where
S: Into<String>,
{
Error::Empty(arg.into(), ErrorBacktrace::new())
}
#[must_use]
pub fn iterator_end<S>(arg: S) -> Self
where
S: Into<String>,
{
Error::IteratorEnd(arg.into(), ErrorBacktrace::new())
}
#[must_use]
pub fn not_implemented<S>(arg: S) -> Self
where
S: Into<String>,
{
Error::NotImplemented(arg.into(), ErrorBacktrace::new())
}
#[must_use]
pub fn illegal_state<S>(arg: S) -> Self
where
S: Into<String>,
{
Error::IllegalState(arg.into(), ErrorBacktrace::new())
}
#[must_use]
pub fn illegal_argument<S>(arg: S) -> Self
where
S: Into<String>,
{
Error::IllegalArgument(arg.into(), ErrorBacktrace::new())
}
#[must_use]
pub fn shutting_down() -> Self {
Error::ShuttingDown
}
#[must_use]
pub fn unsupported<S>(arg: S) -> Self
where
S: Into<String>,
{
Error::Unsupported(arg.into(), ErrorBacktrace::new())
}
#[must_use]
pub fn unknown<S>(arg: S) -> Self
where
S: Into<String>,
{
Error::Unknown(arg.into(), ErrorBacktrace::new())
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Serialize(s, b) => {
write!(f, "Error in Serialization: `{0}`", &s)?;
display_error_backtrace(f, b)
}
#[cfg(feature = "gzip")]
Self::Compression(b) => {
write!(f, "Error in decompression")?;
display_error_backtrace(f, b)
}
#[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)
}
}
}
}
#[cfg(feature = "alloc")]
impl From<BorrowError> for Error {
fn from(err: BorrowError) -> Self {
Self::illegal_state(format!(
"Couldn't borrow from a RefCell as immutable: {err:?}"
))
}
}
#[cfg(feature = "alloc")]
impl From<BorrowMutError> for Error {
fn from(err: BorrowMutError) -> Self {
Self::illegal_state(format!(
"Couldn't borrow from a RefCell as mutable: {err:?}"
))
}
}
#[cfg(feature = "alloc")]
impl From<postcard::Error> for Error {
fn from(err: postcard::Error) -> Self {
Self::serialize(format!("{err:?}"))
}
}
#[cfg(feature = "std")]
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Self::serialize(format!("{err:?}"))
}
}
#[cfg(all(unix, feature = "std"))]
impl From<nix::Error> for Error {
fn from(err: nix::Error) -> Self {
Self::unknown(format!("Unix error: {err:?}"))
}
}
#[cfg(feature = "std")]
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Self::file(err)
}
}
#[cfg(feature = "alloc")]
impl From<FromUtf8Error> for Error {
#[allow(unused_variables)]
fn from(err: FromUtf8Error) -> Self {
Self::unknown(format!("Could not convert byte / utf-8: {err:?}"))
}
}
#[cfg(feature = "alloc")]
impl From<Utf8Error> for Error {
#[allow(unused_variables)]
fn from(err: Utf8Error) -> Self {
Self::unknown(format!("Could not convert byte / utf-8: {err:?}"))
}
}
#[cfg(feature = "std")]
impl From<VarError> for Error {
#[allow(unused_variables)]
fn from(err: VarError) -> Self {
Self::empty(format!("Could not get env var: {err:?}"))
}
}
impl From<ParseIntError> for Error {
#[allow(unused_variables)]
fn from(err: ParseIntError) -> Self {
Self::unknown(format!("Failed to parse Int: {err:?}"))
}
}
impl From<TryFromIntError> for Error {
#[allow(unused_variables)]
fn from(err: TryFromIntError) -> Self {
Self::illegal_state(format!("Expected conversion failed: {err:?}"))
}
}
impl From<TryFromSliceError> for Error {
#[allow(unused_variables)]
fn from(err: TryFromSliceError) -> Self {
Self::illegal_argument(format!("Could not convert slice: {err:?}"))
}
}
#[cfg(windows)]
impl From<windows::core::Error> for Error {
#[allow(unused_variables)]
fn from(err: windows::core::Error) -> Self {
Self::unknown(format!("Windows API error: {err:?}"))
}
}
#[cfg(feature = "python")]
impl From<pyo3::PyErr> for Error {
fn from(err: pyo3::PyErr) -> Self {
pyo3::Python::with_gil(|py| {
if err.matches(
py,
pyo3::types::PyType::new::<pyo3::exceptions::PyKeyboardInterrupt>(py),
) {
Self::shutting_down()
} else {
Self::illegal_state(format!("Python exception: {err:?}"))
}
})
}
}
#[cfg(all(not(nightly), feature = "std"))]
impl std::error::Error for Error {}
#[cfg(nightly)]
impl core::error::Error for Error {}
#[cfg(feature = "prelude")]
pub mod prelude {
pub use super::{bolts_prelude::*, *};
}
#[cfg(all(any(doctest, test), not(feature = "std")))]
#[no_mangle]
pub unsafe extern "C" fn external_current_millis() -> u64 {
1000
}
pub trait IntoOwned {
#[must_use]
fn is_owned(&self) -> bool;
#[must_use]
fn into_owned(self) -> Self;
}
pub trait AsSlice {
type Entry;
fn as_slice(&self) -> &[Self::Entry];
}
pub trait AsMutSlice {
type Entry;
fn as_mut_slice(&mut self) -> &mut [Self::Entry];
}
#[cfg(feature = "alloc")]
impl<T> AsSlice for Vec<T> {
type Entry = T;
fn as_slice(&self) -> &[Self::Entry] {
self
}
}
#[cfg(feature = "alloc")]
impl<T> AsMutSlice for Vec<T> {
type Entry = T;
fn as_mut_slice(&mut self) -> &mut [Self::Entry] {
self
}
}
impl<T> AsSlice for &[T] {
type Entry = T;
fn as_slice(&self) -> &[Self::Entry] {
self
}
}
impl<T> AsSlice for [T] {
type Entry = T;
fn as_slice(&self) -> &[Self::Entry] {
self
}
}
impl<T> AsMutSlice for &mut [T] {
type Entry = T;
fn as_mut_slice(&mut self) -> &mut [Self::Entry] {
self
}
}
impl<T> AsMutSlice for [T] {
type Entry = T;
fn as_mut_slice(&mut self) -> &mut [Self::Entry] {
self
}
}
pub trait AsIter<'it> {
type Item: 'it;
type IntoIter: Iterator<Item = &'it Self::Item>;
fn as_iter(&'it self) -> Self::IntoIter;
}
pub trait AsIterMut<'it> {
type Item: 'it;
type IntoIter: Iterator<Item = &'it mut Self::Item>;
fn as_iter_mut(&'it mut self) -> Self::IntoIter;
}
pub trait HasLen {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub trait HasRefCnt {
fn refcnt(&self) -> isize;
fn refcnt_mut(&mut self) -> &mut isize;
}
pub trait Truncate {
fn truncate(&mut self, len: usize);
}
#[cfg(feature = "std")]
#[must_use]
#[inline]
pub fn current_time() -> time::Duration {
SystemTime::now().duration_since(UNIX_EPOCH).unwrap()
}
#[cfg(all(not(any(doctest, test)), not(feature = "std")))]
extern "C" {
fn external_current_millis() -> u64;
}
#[cfg(not(feature = "std"))]
#[inline]
#[must_use]
pub fn current_time() -> time::Duration {
let millis = unsafe { external_current_millis() };
time::Duration::from_millis(millis)
}
#[must_use]
#[inline]
pub fn current_nanos() -> u64 {
current_time().as_nanos() as u64
}
#[must_use]
#[inline]
pub fn current_milliseconds() -> u64 {
current_time().as_millis() as u64
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn format_duration_hms(duration: &time::Duration) -> String {
let secs = duration.as_secs();
format!("{}h-{}m-{}s", (secs / 60) / 60, (secs / 60) % 60, secs % 60)
}
#[cfg(feature = "std")]
pub static LIBAFL_STDERR_LOGGER: SimpleStderrLogger = SimpleStderrLogger::new();
#[cfg(feature = "std")]
pub static LIBAFL_STDOUT_LOGGER: SimpleStdoutLogger = SimpleStdoutLogger::new();
#[derive(Debug)]
#[cfg(feature = "std")]
pub struct SimpleStdoutLogger {}
#[cfg(feature = "std")]
impl Default for SimpleStdoutLogger {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "std")]
impl SimpleStdoutLogger {
#[must_use]
pub const fn new() -> Self {
Self {}
}
pub fn set_logger() -> Result<(), Error> {
log::set_logger(&LIBAFL_STDOUT_LOGGER)
.map_err(|_| Error::unknown("Failed to register logger"))
}
}
#[cfg(feature = "std")]
impl log::Log for SimpleStdoutLogger {
#[inline]
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
println!(
"[{:?}] {}: {}",
current_time(),
record.level(),
record.args()
);
}
fn flush(&self) {}
}
#[derive(Debug)]
#[cfg(feature = "std")]
pub struct SimpleStderrLogger {}
#[cfg(feature = "std")]
impl Default for SimpleStderrLogger {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "std")]
impl SimpleStderrLogger {
#[must_use]
pub const fn new() -> Self {
Self {}
}
pub fn set_logger() -> Result<(), Error> {
log::set_logger(&LIBAFL_STDERR_LOGGER)
.map_err(|_| Error::unknown("Failed to register logger"))
}
}
#[cfg(feature = "std")]
impl log::Log for SimpleStderrLogger {
#[inline]
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
eprintln!(
"[{:?}] {}: {}",
current_time(),
record.level(),
record.args()
);
}
fn flush(&self) {}
}
#[derive(Debug)]
#[cfg(all(feature = "std", unix))]
pub struct SimpleFdLogger {
fd: RawFd,
}
#[cfg(all(feature = "std", unix))]
impl SimpleFdLogger {
#[must_use]
pub const unsafe fn new(fd: RawFd) -> Self {
Self { fd }
}
pub unsafe fn set_fd(&mut self, fd: RawFd) {
self.fd = fd;
}
}
#[cfg(all(feature = "std", unix))]
impl log::Log for SimpleFdLogger {
#[inline]
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
let mut f = unsafe { File::from_raw_fd(self.fd) };
writeln!(
f,
"[{:?}] {}: {}",
current_time(),
record.level(),
record.args()
)
.unwrap_or_else(|err| println!("Failed to log to fd {}: {err}", self.fd));
mem::forget(f);
}
fn flush(&self) {}
}
#[cfg(feature = "python")]
#[allow(missing_docs)]
pub mod pybind {
use pyo3::{pymodule, types::PyModule, PyResult, Python};
#[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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let buf = Python::with_gil(|py| -> PyResult<Vec<u8>> {
let pickle = PyModule::import(py, "pickle")?;
let buf: Vec<u8> =
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<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let obj = Python::with_gil(|py| -> PyResult<PyObject> {
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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_byte_buf(PyObjectVisitor)
}
}
};
};
}
#[pymodule]
#[pyo3(name = "libafl_bolts")]
pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
crate::rands::pybind::register(py, m)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
#[cfg(all(feature = "std", unix))]
use crate::SimpleFdLogger;
#[cfg(all(feature = "std", unix))]
pub static mut LOGGER: SimpleFdLogger = unsafe { SimpleFdLogger::new(1) };
#[test]
#[cfg(all(unix, feature = "std"))]
fn test_logger() {
use std::{io::stdout, os::fd::AsRawFd};
unsafe { LOGGER.fd = stdout().as_raw_fd() };
unsafe {
log::set_logger(&LOGGER).unwrap();
}
log::set_max_level(log::LevelFilter::Debug);
log::info!("Test");
}
}