fuzz: fuzz everything at once

This commit is contained in:
René Kijewski 2024-08-18 02:08:07 +02:00
parent 79873f6a17
commit 1176c654ba
9 changed files with 97 additions and 18 deletions

View File

@ -6,8 +6,9 @@ license = "MIT OR Apache-2.0"
publish = false
[dependencies]
fuzz = { path = "fuzz"}
fuzz = { path = "fuzz" }
arbitrary = "1.3.2"
pretty-error-debug = "0.3.0"
thiserror = "1.0.63"

View File

@ -13,7 +13,7 @@ Then execute in this folder:
RUST_BACKTRACE=1 nice cargo +nightly fuzz run <fuzz_target>
```
`fuzz_target` is one out of `html` or `parser`.
`fuzz_target` is one out of `all`, `html` or `parser`.
The execution won't stop, but continue until you kill it with ctrl+c.
Or until it finds a panic.

View File

@ -15,6 +15,12 @@ arbitrary = { version = "1.3.2", features = ["derive"] }
html-escape = "0.2.13"
libfuzzer-sys = "0.4.7"
[[bin]]
name = "all"
path = "fuzz_targets/all.rs"
test = false
doc = false
[[bin]]
name = "html"
path = "fuzz_targets/html.rs"

View File

@ -0,0 +1,9 @@
#![no_main]
use fuzz::Scenario;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
if let Ok(scenario) = fuzz::all::Scenario::new(data) {
let _ = scenario.run();
}
});

70
fuzzing/fuzz/src/all.rs Normal file
View File

@ -0,0 +1,70 @@
macro_rules! this_file {
($(crate :: $mod:ident :: $ty:ident;)*) => {
use std::fmt;
use arbitrary::{Arbitrary, Unstructured};
#[derive(Debug)]
pub enum Scenario<'a> {
$($ty(crate::$mod::Scenario<'a>),)*
}
impl<'a> super::Scenario<'a> for Scenario<'a> {
type RunError = RunError;
fn new(data: &'a [u8]) -> Result<Self, arbitrary::Error> {
let mut data = Unstructured::new(data);
let target = Arbitrary::arbitrary(&mut data)?;
let data = Arbitrary::arbitrary_take_rest(data)?;
match target {
$(Target::$ty => Ok(Self::$ty(crate::$mod::Scenario::new(data)?)),)*
}
}
fn run(&self) -> Result<(), Self::RunError> {
match self {
$(Self::$ty(scenario) => scenario.run().map_err(RunError::$ty),)*
}
}
}
#[derive(Arbitrary)]
enum Target {
$($ty,)*
}
pub enum RunError {
$($ty(<crate::$mod::Scenario<'static> as crate::Scenario<'static>>::RunError),)*
}
impl fmt::Display for RunError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
$(Self::$ty(err) => err.fmt(f),)*
}
}
}
impl fmt::Debug for RunError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
$(Self::$ty(err) => err.fmt(f),)*
}
}
}
impl std::error::Error for RunError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
$(RunError::$ty(err) => Some(err),)*
}
}
}
};
}
this_file! {
crate::html::Html;
crate::parser::Parser;
}

View File

@ -11,11 +11,9 @@ pub enum Scenario<'a> {
}
impl<'a> super::Scenario<'a> for Scenario<'a> {
type NewError = arbitrary::Error;
type RunError = std::convert::Infallible;
fn new(data: &'a [u8]) -> Result<Self, Self::NewError> {
fn new(data: &'a [u8]) -> Result<Self, arbitrary::Error> {
Self::arbitrary_take_rest(Unstructured::new(data))
}

View File

@ -1,3 +1,4 @@
pub mod all;
pub mod html;
pub mod parser;
@ -5,18 +6,17 @@ use std::error::Error;
use std::fmt;
pub const TARGETS: &[(&str, TargetBuilder)] = &[
("all", |data| NamedTarget::new::<all::Scenario>(data)),
("html", |data| NamedTarget::new::<html::Scenario>(data)),
("parser", |data| NamedTarget::new::<parser::Scenario>(data)),
];
pub type TargetBuilder =
for<'a> fn(&'a [u8]) -> Result<NamedTarget<'a>, Box<dyn Error + Send + 'static>>;
pub type TargetBuilder = for<'a> fn(&'a [u8]) -> Result<NamedTarget<'a>, arbitrary::Error>;
pub trait Scenario<'a>: fmt::Debug + Sized {
type NewError: Error + Send + 'static;
type RunError: Error + Send + 'static;
fn new(data: &'a [u8]) -> Result<Self, Self::NewError>;
fn new(data: &'a [u8]) -> Result<Self, arbitrary::Error>;
fn run(&self) -> Result<(), Self::RunError>;
}
@ -53,11 +53,8 @@ impl fmt::Debug for NamedTarget<'_> {
impl<'a> NamedTarget<'a> {
#[inline]
fn new<S: Scenario<'a> + 'a>(data: &'a [u8]) -> Result<Self, Box<dyn Error + Send + 'static>> {
match S::new(data) {
Ok(scenario) => Ok(Self(Box::new(scenario))),
Err(err) => Err(Box::new(err)),
}
fn new<S: Scenario<'a> + 'a>(data: &'a [u8]) -> Result<Self, arbitrary::Error> {
Ok(Self(Box::new(S::new(data)?)))
}
}

View File

@ -8,11 +8,9 @@ pub struct Scenario<'a> {
}
impl<'a> super::Scenario<'a> for Scenario<'a> {
type NewError = arbitrary::Error;
type RunError = rinja_parser::ParseError;
fn new(data: &'a [u8]) -> Result<Self, Self::NewError> {
fn new(data: &'a [u8]) -> Result<Self, arbitrary::Error> {
let mut data = Unstructured::new(data);
let syntax = ArbitrarySyntax::arbitrary(&mut data)?;

View File

@ -52,7 +52,7 @@ enum Error {
#[error("could not read opened input file {}", .1.display())]
Read(#[source] std::io::Error, PathBuf),
#[error("could not build scenario")]
Build(#[source] Box<dyn std::error::Error + Send + 'static>),
Build(#[source] arbitrary::Error),
#[error("could not run scenario")]
Run(#[source] Box<dyn std::error::Error + Send + 'static>),
}