cranelift: CLIF Fuzzer generate multiple blocks
This commit is contained in:
@@ -9,6 +9,13 @@ pub struct Config {
|
|||||||
/// Number of variables that we allocate per function
|
/// Number of variables that we allocate per function
|
||||||
/// This value does not include the signature params
|
/// This value does not include the signature params
|
||||||
pub vars_per_function: RangeInclusive<usize>,
|
pub vars_per_function: RangeInclusive<usize>,
|
||||||
|
/// Number of blocks that we generate per function.
|
||||||
|
/// This value does not include the entry block
|
||||||
|
pub blocks_per_function: RangeInclusive<usize>,
|
||||||
|
/// Number of params a block should take
|
||||||
|
/// This value does not apply to block0 which takes the function params
|
||||||
|
/// and is thus governed by `signature_params`
|
||||||
|
pub block_signature_params: RangeInclusive<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@@ -19,6 +26,8 @@ impl Default for Config {
|
|||||||
signature_rets: 0..=16,
|
signature_rets: 0..=16,
|
||||||
instructions_per_block: 0..=64,
|
instructions_per_block: 0..=64,
|
||||||
vars_per_function: 0..=16,
|
vars_per_function: 0..=16,
|
||||||
|
blocks_per_function: 0..=16,
|
||||||
|
block_signature_params: 0..=16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,15 @@ use crate::config::Config;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use arbitrary::Unstructured;
|
use arbitrary::Unstructured;
|
||||||
use cranelift::codegen::ir::types::*;
|
use cranelift::codegen::ir::types::*;
|
||||||
use cranelift::codegen::ir::{AbiParam, ExternalName, Function, Opcode, Signature, Type, Value};
|
use cranelift::codegen::ir::{
|
||||||
|
AbiParam, Block, ExternalName, Function, Opcode, Signature, Type, Value,
|
||||||
|
};
|
||||||
use cranelift::codegen::isa::CallConv;
|
use cranelift::codegen::isa::CallConv;
|
||||||
use cranelift::frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
|
use cranelift::frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
|
||||||
use cranelift::prelude::{EntityRef, InstBuilder};
|
use cranelift::prelude::{EntityRef, InstBuilder};
|
||||||
|
|
||||||
|
type BlockSignature = Vec<Type>;
|
||||||
|
|
||||||
fn insert_opcode_arity_0(
|
fn insert_opcode_arity_0(
|
||||||
_fgen: &mut FunctionGenerator,
|
_fgen: &mut FunctionGenerator,
|
||||||
builder: &mut FunctionBuilder,
|
builder: &mut FunctionBuilder,
|
||||||
@@ -92,6 +96,7 @@ where
|
|||||||
u: &'r mut Unstructured<'data>,
|
u: &'r mut Unstructured<'data>,
|
||||||
config: &'r Config,
|
config: &'r Config,
|
||||||
vars: Vec<(Type, Variable)>,
|
vars: Vec<(Type, Variable)>,
|
||||||
|
blocks: Vec<(Block, BlockSignature)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, 'data> FunctionGenerator<'r, 'data>
|
impl<'r, 'data> FunctionGenerator<'r, 'data>
|
||||||
@@ -103,6 +108,7 @@ where
|
|||||||
u,
|
u,
|
||||||
config,
|
config,
|
||||||
vars: vec![],
|
vars: vec![],
|
||||||
|
blocks: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,12 +209,85 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_jump(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
||||||
|
let (block, signature) = {
|
||||||
|
let target = self.u.choose(&self.blocks[..])?;
|
||||||
|
let target = target.clone();
|
||||||
|
target
|
||||||
|
};
|
||||||
|
|
||||||
|
let vars = signature
|
||||||
|
.iter()
|
||||||
|
.map(|ty| self.get_variable_of_type(*ty))
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
let vals = vars
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| builder.use_var(v))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
builder.ins().jump(*block, &vals[..]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Inserts a random instruction into the block
|
/// Inserts a random instruction into the block
|
||||||
fn generate_instruction(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
fn generate_instruction(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
||||||
let (op, args, rets, inserter) = *self.u.choose(OPCODE_SIGNATURES)?;
|
let (op, args, rets, inserter) = *self.u.choose(OPCODE_SIGNATURES)?;
|
||||||
inserter(self, builder, op, args, rets)
|
inserter(self, builder, op, args, rets)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a random amount of blocks in this function
|
||||||
|
fn generate_blocks(
|
||||||
|
&mut self,
|
||||||
|
builder: &mut FunctionBuilder,
|
||||||
|
sig: &Signature,
|
||||||
|
) -> Result<Vec<(Block, BlockSignature)>> {
|
||||||
|
let extra_block_count = self
|
||||||
|
.u
|
||||||
|
.int_in_range(self.config.blocks_per_function.clone())?;
|
||||||
|
|
||||||
|
// We must always have at least one block, so we generate the "extra" blocks and add 1 for
|
||||||
|
// the entry block.
|
||||||
|
let block_count = 1 + extra_block_count;
|
||||||
|
|
||||||
|
let blocks = (0..block_count)
|
||||||
|
.map(|i| {
|
||||||
|
let block = builder.create_block();
|
||||||
|
|
||||||
|
// The first block has to have the function signature, but for the rest of them we generate
|
||||||
|
// a random signature;
|
||||||
|
if i == 0 {
|
||||||
|
builder.append_block_params_for_function_params(block);
|
||||||
|
Ok((block, sig.params.iter().map(|a| a.value_type).collect()))
|
||||||
|
} else {
|
||||||
|
Ok((block, self.generate_block_signature()?))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
Ok(blocks)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_block_signature(&mut self) -> Result<BlockSignature> {
|
||||||
|
let param_count = self
|
||||||
|
.u
|
||||||
|
.int_in_range(self.config.block_signature_params.clone())?;
|
||||||
|
|
||||||
|
let mut params = Vec::with_capacity(param_count);
|
||||||
|
for _ in 0..param_count {
|
||||||
|
params.push(self.generate_type()?);
|
||||||
|
}
|
||||||
|
Ok(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We generate a function in multiple stages: by first creating a random number of empty
|
||||||
|
///
|
||||||
|
/// * First we generate a random number of empty blocks
|
||||||
|
/// * Then we generate a random pool of variables to be used throughout the function
|
||||||
|
/// * We then visit each block and generate random instructions
|
||||||
|
///
|
||||||
|
/// Because we generate all blocks and variables up front we already know everything that
|
||||||
|
/// we need when generating instructions (i.e. jump targets / variables)
|
||||||
pub fn generate(mut self) -> Result<Function> {
|
pub fn generate(mut self) -> Result<Function> {
|
||||||
let sig = self.generate_signature()?;
|
let sig = self.generate_signature()?;
|
||||||
|
|
||||||
@@ -216,15 +295,18 @@ where
|
|||||||
let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig.clone());
|
let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig.clone());
|
||||||
|
|
||||||
let mut builder = FunctionBuilder::new(&mut func, &mut fn_builder_ctx);
|
let mut builder = FunctionBuilder::new(&mut func, &mut fn_builder_ctx);
|
||||||
let block0 = builder.create_block();
|
|
||||||
builder.append_block_params_for_function_params(block0);
|
|
||||||
builder.switch_to_block(block0);
|
|
||||||
builder.seal_block(block0);
|
|
||||||
|
|
||||||
|
self.blocks = self.generate_blocks(&mut builder, &sig)?;
|
||||||
|
|
||||||
|
// Main instruction generation loop
|
||||||
|
for (i, (block, signature)) in self.blocks.clone().iter().enumerate() {
|
||||||
|
builder.switch_to_block(*block);
|
||||||
|
|
||||||
|
if i == 0 {
|
||||||
// Define variables for the function signature
|
// Define variables for the function signature
|
||||||
for (i, param) in sig.params.iter().enumerate() {
|
for (i, param) in sig.params.iter().enumerate() {
|
||||||
let var = self.create_var(&mut builder, param.value_type)?;
|
let var = self.create_var(&mut builder, param.value_type)?;
|
||||||
let block_param = builder.block_params(block0)[i];
|
let block_param = builder.block_params(*block)[i];
|
||||||
builder.def_var(var, block_param);
|
builder.def_var(var, block_param);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +317,16 @@ where
|
|||||||
let value = self.generate_const(&mut builder, ty)?;
|
let value = self.generate_const(&mut builder, ty)?;
|
||||||
builder.def_var(var, value);
|
builder.def_var(var, value);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Define variables for the block params
|
||||||
|
for (i, ty) in signature.iter().enumerate() {
|
||||||
|
let var = self.get_variable_of_type(*ty)?;
|
||||||
|
let block_param = builder.block_params(*block)[i];
|
||||||
|
builder.def_var(var, block_param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate block instructions
|
||||||
for _ in 0..self
|
for _ in 0..self
|
||||||
.u
|
.u
|
||||||
.int_in_range(self.config.instructions_per_block.clone())?
|
.int_in_range(self.config.instructions_per_block.clone())?
|
||||||
@@ -243,9 +334,18 @@ where
|
|||||||
self.generate_instruction(&mut builder)?;
|
self.generate_instruction(&mut builder)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We should make this part of the regular instruction selection
|
// We always need to exit safely out of a block.
|
||||||
|
// For block 0 this means a return, but for other block it is a jump into any other
|
||||||
|
// random block.
|
||||||
|
if i == 0 {
|
||||||
|
// TODO: We should make this, part of the regular instruction selection
|
||||||
self.generate_return(&mut builder)?;
|
self.generate_return(&mut builder)?;
|
||||||
|
} else {
|
||||||
|
self.generate_jump(&mut builder)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.seal_all_blocks();
|
||||||
builder.finalize();
|
builder.finalize();
|
||||||
|
|
||||||
Ok(func)
|
Ok(func)
|
||||||
|
|||||||
Reference in New Issue
Block a user