Skip to content

Commit

Permalink
add driver cache (#221)
Browse files Browse the repository at this point in the history
  • Loading branch information
camshaft authored Jun 1, 2024
1 parent ad6ec5d commit 17014af
Show file tree
Hide file tree
Showing 30 changed files with 1,082 additions and 547 deletions.
6 changes: 3 additions & 3 deletions lib/bolero-afl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#[doc(hidden)]
#[cfg(any(test, all(feature = "lib", fuzzing_afl)))]
pub mod fuzzer {
use bolero_engine::{driver, panic, ByteSliceTestInput, Engine, Never, TargetLocation, Test};
use bolero_engine::{driver, input, panic, Engine, Never, TargetLocation, Test};
use std::io::Read;

extern "C" {
Expand Down Expand Up @@ -75,9 +75,9 @@ pub mod fuzzer {
.expect("could not read next input");
}

fn test_input(&mut self) -> ByteSliceTestInput {
fn test_input(&mut self) -> input::Bytes {
self.reset();
ByteSliceTestInput::new(&self.input, &self.options)
input::Bytes::new(&self.input, &self.options)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/bolero-engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ readme = "../../README.md"
rust-version = "1.66.0"

[features]
cache = ["bolero-generator/alloc"]
rng = ["rand", "rand_xoshiro", "bolero-generator/alloc"]

[dependencies]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use crate::panic::{rust_backtrace, PanicError};
use crate::{
panic::{rust_backtrace, PanicError},
Seed,
};
use core::fmt::{Debug, Display};

/// Contains information about a test failure
#[derive(Debug)]
pub struct TestFailure<Input> {
pub struct Failure<Input> {
pub error: PanicError,
pub input: Input,
pub seed: Option<u64>,
pub seed: Option<Seed>,
}

impl<Input: Debug> Display for TestFailure<Input> {
impl<Input: Debug> Display for Failure<Input> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
writeln!(
f,
Expand Down Expand Up @@ -37,4 +40,4 @@ impl<Input: Debug> Display for TestFailure<Input> {
}
}

impl<Input: Debug> std::error::Error for TestFailure<Input> {}
impl<Input: Debug> std::error::Error for Failure<Input> {}
134 changes: 134 additions & 0 deletions lib/bolero-engine/src/input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use crate::{driver, Driver};
use bolero_generator::driver::ByteSliceDriver;
use core::fmt;
use pretty_hex::pretty_hex_write;
use std::panic::RefUnwindSafe;

pub trait Input<Output> {
type Driver: Driver + RefUnwindSafe;

/// Provide a slice of the test input
fn with_slice<F: FnMut(&[u8]) -> Output>(&mut self, f: &mut F) -> Output;

/// Provide a test driver for the test input
///
/// Note: Drivers are used with `bolero_generator::ValueGenerator` implementations.
fn with_driver<F: FnMut(&mut Self::Driver) -> Output>(&mut self, f: &mut F) -> Output;
}

impl<'a, Output> Input<Output> for &'a [u8] {
type Driver = ByteSliceDriver<'a>;

fn with_slice<F: FnMut(&[u8]) -> Output>(&mut self, f: &mut F) -> Output {
f(self)
}

fn with_driver<F: FnMut(&mut Self::Driver) -> Output>(&mut self, f: &mut F) -> Output {
let options = driver::Options::default();
f(&mut ByteSliceDriver::new(self, &options))
}
}

#[derive(Clone, Copy, Debug)]
pub struct Bytes<'a> {
slice: &'a [u8],
options: &'a driver::Options,
}

impl<'a> Bytes<'a> {
pub fn new(slice: &'a [u8], options: &'a driver::Options) -> Self {
Self { slice, options }
}
}

impl<'a, Output> Input<Output> for Bytes<'a> {
type Driver = ByteSliceDriver<'a>;

fn with_slice<F: FnMut(&[u8]) -> Output>(&mut self, f: &mut F) -> Output {
f(self.slice)
}

fn with_driver<F: FnMut(&mut Self::Driver) -> Output>(&mut self, f: &mut F) -> Output {
f(&mut ByteSliceDriver::new(self.slice, self.options))
}
}

#[cfg(feature = "cache")]
pub mod cache {
use super::*;
use driver::cache::{self, Cache};

pub struct Bytes<'a> {
driver: cache::Driver<'a, ByteSliceDriver<'a>>,
}

impl<'a> Bytes<'a> {
#[inline]
pub fn new(slice: &'a [u8], options: &'a driver::Options, cache: &'a mut Cache) -> Self {
let driver = ByteSliceDriver::new(slice, options);
let driver = cache::Driver::new(driver, cache);
Self { driver }
}
}

impl<'a, Output> Input<Output> for Bytes<'a> {
type Driver = cache::Driver<'a, ByteSliceDriver<'a>>;

#[inline]
fn with_slice<F: FnMut(&[u8]) -> Output>(&mut self, f: &mut F) -> Output {
f(self.driver.as_ref().as_slice())
}

#[inline]
fn with_driver<F: FnMut(&mut Self::Driver) -> Output>(&mut self, f: &mut F) -> Output {
f(&mut self.driver)
}
}

pub struct Driver<'a, I: crate::Driver> {
slice: &'a mut Vec<u8>,
driver: cache::Driver<'a, I>,
}

impl<'a, I: crate::Driver> Driver<'a, I> {
#[inline]
pub fn new(inner: I, cache: &'a mut driver::cache::Cache, slice: &'a mut Vec<u8>) -> Self {
Self {
slice,
driver: cache::Driver::new(inner, cache),
}
}
}

impl<'a, Output, I: crate::Driver + core::panic::RefUnwindSafe> Input<Output> for Driver<'a, I> {
type Driver = cache::Driver<'a, I>;

#[inline]
fn with_slice<F: FnMut(&[u8]) -> Output>(&mut self, f: &mut F) -> Output {
bolero_generator::TypeGenerator::mutate(self.slice, &mut self.driver);
f(self.slice)
}

#[inline]
fn with_driver<F: FnMut(&mut Self::Driver) -> Output>(&mut self, f: &mut F) -> Output {
f(&mut self.driver)
}
}
}

#[derive(Clone, Copy)]
pub struct SliceDebug<T>(pub(crate) T);

impl<T> core::ops::Deref for SliceDebug<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T: AsRef<[u8]>> fmt::Debug for SliceDebug<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
pretty_hex_write(f, &self.0.as_ref())
}
}
14 changes: 8 additions & 6 deletions lib/bolero-engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pub use bolero_generator::{
TypeGenerator, ValueGenerator,
};

pub type Seed = u128;

#[cfg(not(kani))]
pub mod panic;
#[cfg(kani)]
Expand All @@ -16,19 +18,19 @@ pub mod shrink;
mod test;
pub use test::*;

pub mod test_failure;
pub use crate::test_failure::TestFailure;
pub mod failure;
pub use crate::failure::Failure;

mod test_input;
pub use test_input::*;
pub mod input;
pub use input::Input;

#[doc(hidden)]
pub mod target_location;
#[doc(hidden)]
pub use target_location::TargetLocation;

mod test_result;
pub use test_result::*;
mod result;
pub use result::IntoResult;

/// Trait for defining an engine that executes a test
pub trait Engine<T: Test>: Sized {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ use crate::panic::PanicError;
use anyhow::Error;

/// Trait that turns the test return value into a `Result`
pub trait IntoTestResult {
fn into_test_result(self) -> Result<(), PanicError>;
pub trait IntoResult {
fn into_result(self) -> Result<(), PanicError>;
}

impl IntoTestResult for () {
fn into_test_result(self) -> Result<(), PanicError> {
impl IntoResult for () {
fn into_result(self) -> Result<(), PanicError> {
Ok(())
}
}

impl IntoTestResult for bool {
fn into_test_result(self) -> Result<(), PanicError> {
impl IntoResult for bool {
fn into_result(self) -> Result<(), PanicError> {
if self {
Ok(())
} else {
Expand All @@ -22,8 +22,8 @@ impl IntoTestResult for bool {
}
}

impl<T, E: Into<Error>> IntoTestResult for Result<T, E> {
fn into_test_result(self) -> Result<(), PanicError> {
impl<T, E: Into<Error>> IntoResult for Result<T, E> {
fn into_result(self) -> Result<(), PanicError> {
if let Err(err) = self {
let err = err.into();
Err(PanicError::new(err.to_string()))
Expand All @@ -33,8 +33,8 @@ impl<T, E: Into<Error>> IntoTestResult for Result<T, E> {
}
}

impl<T> IntoTestResult for Option<T> {
fn into_test_result(self) -> Result<(), PanicError> {
impl<T> IntoResult for Option<T> {
fn into_result(self) -> Result<(), PanicError> {
if self.is_none() {
Err(PanicError::new("test returned `None`".to_string()))
} else {
Expand Down
Loading

0 comments on commit 17014af

Please sign in to comment.