cranelift: CLIF Fuzzer limit instructions executed in interpreter

This commit is contained in:
Afonso Bordado
2021-07-05 14:38:53 +01:00
parent 1532516a36
commit fb1201cb60

View File

@@ -7,14 +7,17 @@ use cranelift_filetests::function_runner::{CompiledFunction, SingleFunctionCompi
use cranelift_fuzzgen::*; use cranelift_fuzzgen::*;
use cranelift_interpreter::environment::FuncIndex; use cranelift_interpreter::environment::FuncIndex;
use cranelift_interpreter::environment::FunctionStore; use cranelift_interpreter::environment::FunctionStore;
use cranelift_interpreter::interpreter::{Interpreter, InterpreterState}; use cranelift_interpreter::interpreter::{Interpreter, InterpreterError, InterpreterState};
use cranelift_interpreter::step::ControlFlow; use cranelift_interpreter::step::ControlFlow;
use cranelift_interpreter::step::CraneliftTrap; use cranelift_interpreter::step::CraneliftTrap;
const INTERPRETER_FUEL: u64 = 4096;
#[derive(Debug)] #[derive(Debug)]
enum RunResult { enum RunResult {
Success(Vec<DataValue>), Success(Vec<DataValue>),
Trap(CraneliftTrap), Trap(CraneliftTrap),
Timeout,
Error(Box<dyn std::error::Error>), Error(Box<dyn std::error::Error>),
} }
@@ -36,7 +39,8 @@ fn run_in_interpreter(interpreter: &mut Interpreter, args: &[DataValue]) -> RunR
Ok(ControlFlow::Return(results)) => RunResult::Success(results.to_vec()), Ok(ControlFlow::Return(results)) => RunResult::Success(results.to_vec()),
Ok(ControlFlow::Trap(trap)) => RunResult::Trap(trap), Ok(ControlFlow::Trap(trap)) => RunResult::Trap(trap),
Ok(cf) => RunResult::Error(format!("Unrecognized exit ControlFlow: {:?}", cf).into()), Ok(cf) => RunResult::Error(format!("Unrecognized exit ControlFlow: {:?}", cf).into()),
Err(e) => RunResult::Error(format!("InterpreterError: {:?}", e).into()), Err(InterpreterError::FuelExhausted) => RunResult::Timeout,
Err(e) => RunResult::Error(e.into()),
} }
} }
@@ -46,12 +50,12 @@ fn run_in_host(compiled_fn: &CompiledFunction, args: &[DataValue]) -> RunResult
} }
fuzz_target!(|testcase: TestCase| { fuzz_target!(|testcase: TestCase| {
let mut interpreter = { let build_interpreter = || {
let mut env = FunctionStore::default(); let mut env = FunctionStore::default();
env.add(testcase.func.name.to_string(), &testcase.func); env.add(testcase.func.name.to_string(), &testcase.func);
let state = InterpreterState::default().with_function_store(env); let state = InterpreterState::default().with_function_store(env);
let interpreter = Interpreter::new(state); let interpreter = Interpreter::new(state).with_fuel(Some(INTERPRETER_FUEL));
interpreter interpreter
}; };
@@ -60,6 +64,9 @@ fuzz_target!(|testcase: TestCase| {
let compiled_fn = host_compiler.compile(testcase.func.clone()).unwrap(); let compiled_fn = host_compiler.compile(testcase.func.clone()).unwrap();
for args in &testcase.inputs { for args in &testcase.inputs {
// We rebuild the interpreter every run so that we don't accidentally carry over any state
// between runs, such as fuel remaining.
let mut interpreter = build_interpreter();
let int_res = run_in_interpreter(&mut interpreter, args); let int_res = run_in_interpreter(&mut interpreter, args);
match int_res { match int_res {
RunResult::Success(_) => {} RunResult::Success(_) => {}
@@ -71,6 +78,10 @@ fuzz_target!(|testcase: TestCase| {
// not justify implementing it again here. // not justify implementing it again here.
return; return;
} }
RunResult::Timeout => {
// We probably generated an infinite loop, we can ignore this
return;
}
RunResult::Error(_) => panic!("interpreter failed: {:?}", int_res), RunResult::Error(_) => panic!("interpreter failed: {:?}", int_res),
} }