Cranelift CLIF-level differential fuzzer (#3038)

* cranelift: Initial fuzzer implementation

* cranelift: Generate multiple test cases in fuzzer

* cranelift: Separate function generator in fuzzer

* cranelift: Insert random instructions in fuzzer

* cranelift: Rename gen_testcase

* cranelift: Implement div for unsigned values in interpreter

* cranelift: Run all test cases in fuzzer

* cranelift: Comment options in function_runner

* cranelift: Improve fuzzgen README.md

* cranelift: Fuzzgen remove unused variable

* cranelift: Fuzzer code style fixes

Thanks! @bjorn3

* cranelift: Fix nits in CLIF fuzzer

Thanks @cfallin!

* cranelift: Implement Arbitrary for TestCase

* cranelift: Remove gen_testcase

* cranelift: Move fuzzers to wasmtime fuzz directory

* cranelift: CLIF-Fuzzer ignore tests that produce traps

* cranelift: CLIF-Fuzzer create new fuzz target to validate generated testcases

* cranelift: Store clif-fuzzer config in a separate struct

* cranelift: Generate variables upfront per function

* cranelift: Prevent publishing of fuzzgen crate
This commit is contained in:
Afonso Bordado
2021-07-01 14:32:01 +01:00
committed by GitHub
parent a603fc5bd5
commit 7453bd5f0d
14 changed files with 732 additions and 2 deletions

View File

@@ -0,0 +1,82 @@
use crate::config::Config;
use crate::function_generator::FunctionGenerator;
use anyhow::Result;
use arbitrary::{Arbitrary, Unstructured};
use cranelift::codegen::data_value::DataValue;
use cranelift::codegen::ir::types::*;
use cranelift::codegen::ir::Function;
use cranelift::prelude::*;
mod config;
mod function_generator;
pub type TestCaseInput = Vec<DataValue>;
#[derive(Debug)]
pub struct TestCase {
pub func: Function,
/// Generate multiple test inputs for each test case.
/// This allows us to get more coverage per compilation, which may be somewhat expensive.
pub inputs: Vec<TestCaseInput>,
}
impl<'a> Arbitrary<'a> for TestCase {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
FuzzGen::new(u)
.generate_test()
.map_err(|_| arbitrary::Error::IncorrectFormat)
}
}
pub struct FuzzGen<'r, 'data>
where
'data: 'r,
{
u: &'r mut Unstructured<'data>,
config: Config,
}
impl<'r, 'data> FuzzGen<'r, 'data>
where
'data: 'r,
{
pub fn new(u: &'r mut Unstructured<'data>) -> Self {
Self {
u,
config: Config::default(),
}
}
fn generate_test_inputs(&mut self, signature: &Signature) -> Result<Vec<TestCaseInput>> {
let num_tests = self.u.int_in_range(self.config.test_case_inputs.clone())?;
let mut inputs = Vec::with_capacity(num_tests);
for _ in 0..num_tests {
let test_args = signature
.params
.iter()
.map(|p| {
let imm64 = match p.value_type {
I8 => self.u.arbitrary::<i8>()? as i64,
I16 => self.u.arbitrary::<i16>()? as i64,
I32 => self.u.arbitrary::<i32>()? as i64,
I64 => self.u.arbitrary::<i64>()?,
_ => unreachable!(),
};
Ok(DataValue::from_integer(imm64, p.value_type)?)
})
.collect::<Result<TestCaseInput>>()?;
inputs.push(test_args);
}
Ok(inputs)
}
pub fn generate_test(mut self) -> Result<TestCase> {
let func = FunctionGenerator::new(&mut self.u, &self.config).generate()?;
let inputs = self.generate_test_inputs(&func.signature)?;
Ok(TestCase { func, inputs })
}
}