Merge pull request #3299 from afonso360/fuzzer-brtable
Cranelift CLIF Fuzzer add jump tables and `br_table`
This commit is contained in:
@@ -16,6 +16,8 @@ pub struct Config {
|
|||||||
/// This value does not apply to block0 which takes the function params
|
/// This value does not apply to block0 which takes the function params
|
||||||
/// and is thus governed by `signature_params`
|
/// and is thus governed by `signature_params`
|
||||||
pub block_signature_params: RangeInclusive<usize>,
|
pub block_signature_params: RangeInclusive<usize>,
|
||||||
|
pub jump_tables_per_function: RangeInclusive<usize>,
|
||||||
|
pub jump_table_entries: RangeInclusive<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
@@ -28,6 +30,8 @@ impl Default for Config {
|
|||||||
vars_per_function: 0..=16,
|
vars_per_function: 0..=16,
|
||||||
blocks_per_function: 0..=16,
|
blocks_per_function: 0..=16,
|
||||||
block_signature_params: 0..=16,
|
block_signature_params: 0..=16,
|
||||||
|
jump_tables_per_function: 0..=4,
|
||||||
|
jump_table_entries: 0..=16,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ use anyhow::Result;
|
|||||||
use arbitrary::{Arbitrary, Unstructured};
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
use cranelift::codegen::ir::types::*;
|
use cranelift::codegen::ir::types::*;
|
||||||
use cranelift::codegen::ir::{
|
use cranelift::codegen::ir::{
|
||||||
AbiParam, Block, ExternalName, Function, Opcode, Signature, Type, Value,
|
AbiParam, Block, ExternalName, Function, JumpTable, 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, IntCC};
|
use cranelift::prelude::{EntityRef, InstBuilder, IntCC, JumpTableData};
|
||||||
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
type BlockSignature = Vec<Type>;
|
type BlockSignature = Vec<Type>;
|
||||||
|
|
||||||
@@ -97,6 +98,7 @@ where
|
|||||||
config: &'r Config,
|
config: &'r Config,
|
||||||
vars: Vec<(Type, Variable)>,
|
vars: Vec<(Type, Variable)>,
|
||||||
blocks: Vec<(Block, BlockSignature)>,
|
blocks: Vec<(Block, BlockSignature)>,
|
||||||
|
jump_tables: Vec<JumpTable>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r, 'data> FunctionGenerator<'r, 'data>
|
impl<'r, 'data> FunctionGenerator<'r, 'data>
|
||||||
@@ -109,9 +111,15 @@ where
|
|||||||
config,
|
config,
|
||||||
vars: vec![],
|
vars: vec![],
|
||||||
blocks: vec![],
|
blocks: vec![],
|
||||||
|
jump_tables: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a random value for config `param`
|
||||||
|
fn param(&mut self, param: &RangeInclusive<usize>) -> Result<usize> {
|
||||||
|
Ok(self.u.int_in_range(param.clone())?)
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_callconv(&mut self) -> Result<CallConv> {
|
fn generate_callconv(&mut self) -> Result<CallConv> {
|
||||||
// TODO: Generate random CallConvs per target
|
// TODO: Generate random CallConvs per target
|
||||||
Ok(CallConv::SystemV)
|
Ok(CallConv::SystemV)
|
||||||
@@ -162,11 +170,11 @@ where
|
|||||||
let callconv = self.generate_callconv()?;
|
let callconv = self.generate_callconv()?;
|
||||||
let mut sig = Signature::new(callconv);
|
let mut sig = Signature::new(callconv);
|
||||||
|
|
||||||
for _ in 0..self.u.int_in_range(self.config.signature_params.clone())? {
|
for _ in 0..self.param(&self.config.signature_params)? {
|
||||||
sig.params.push(self.generate_abi_param()?);
|
sig.params.push(self.generate_abi_param()?);
|
||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0..self.u.int_in_range(self.config.signature_rets.clone())? {
|
for _ in 0..self.param(&self.config.signature_rets)? {
|
||||||
sig.returns.push(self.generate_abi_param()?);
|
sig.returns.push(self.generate_abi_param()?);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,6 +237,16 @@ where
|
|||||||
Ok((block, args))
|
Ok((block, args))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Valid blocks for jump tables have to have no parameters in the signature, and must also
|
||||||
|
/// not be the first block.
|
||||||
|
fn generate_valid_jumptable_target_blocks(&mut self) -> Vec<Block> {
|
||||||
|
self.blocks[1..]
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, sig)| sig.len() == 0)
|
||||||
|
.map(|(b, _)| *b)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_values_for_signature<I: Iterator<Item = Type>>(
|
fn generate_values_for_signature<I: Iterator<Item = Type>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
builder: &mut FunctionBuilder,
|
builder: &mut FunctionBuilder,
|
||||||
@@ -260,6 +278,20 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a br_table into a random block
|
||||||
|
fn generate_br_table(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
||||||
|
let _type = *self.u.choose(&[I8, I16, I32, I64][..])?;
|
||||||
|
let var = self.get_variable_of_type(_type)?;
|
||||||
|
let val = builder.use_var(var);
|
||||||
|
|
||||||
|
let valid_blocks = self.generate_valid_jumptable_target_blocks();
|
||||||
|
let default_block = *self.u.choose(&valid_blocks[..])?;
|
||||||
|
|
||||||
|
let jt = *self.u.choose(&self.jump_tables[..])?;
|
||||||
|
builder.ins().br_table(val, default_block, jt);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates a brz/brnz into a random block
|
/// Generates a brz/brnz into a random block
|
||||||
fn generate_br(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
fn generate_br(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
||||||
let (block, args) = self.generate_target_block(builder)?;
|
let (block, args) = self.generate_target_block(builder)?;
|
||||||
@@ -314,6 +346,7 @@ where
|
|||||||
&[
|
&[
|
||||||
Self::generate_bricmp,
|
Self::generate_bricmp,
|
||||||
Self::generate_br,
|
Self::generate_br,
|
||||||
|
Self::generate_br_table,
|
||||||
Self::generate_jump,
|
Self::generate_jump,
|
||||||
Self::generate_return,
|
Self::generate_return,
|
||||||
][..],
|
][..],
|
||||||
@@ -324,10 +357,7 @@ where
|
|||||||
|
|
||||||
/// Fills the current block with random instructions
|
/// Fills the current block with random instructions
|
||||||
fn generate_instructions(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
fn generate_instructions(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
||||||
for _ in 0..self
|
for _ in 0..self.param(&self.config.instructions_per_block)? {
|
||||||
.u
|
|
||||||
.int_in_range(self.config.instructions_per_block.clone())?
|
|
||||||
{
|
|
||||||
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)?;
|
||||||
}
|
}
|
||||||
@@ -335,15 +365,29 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_jumptables(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
|
||||||
|
let valid_blocks = self.generate_valid_jumptable_target_blocks();
|
||||||
|
|
||||||
|
for _ in 0..self.param(&self.config.jump_tables_per_function)? {
|
||||||
|
let mut jt_data = JumpTableData::new();
|
||||||
|
|
||||||
|
for _ in 0..self.param(&self.config.jump_table_entries)? {
|
||||||
|
let block = *self.u.choose(&valid_blocks[..])?;
|
||||||
|
jt_data.push_entry(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.jump_tables.push(builder.create_jump_table(jt_data));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a random amount of blocks in this function
|
/// Creates a random amount of blocks in this function
|
||||||
fn generate_blocks(
|
fn generate_blocks(
|
||||||
&mut self,
|
&mut self,
|
||||||
builder: &mut FunctionBuilder,
|
builder: &mut FunctionBuilder,
|
||||||
sig: &Signature,
|
sig: &Signature,
|
||||||
) -> Result<Vec<(Block, BlockSignature)>> {
|
) -> Result<Vec<(Block, BlockSignature)>> {
|
||||||
let extra_block_count = self
|
let extra_block_count = self.param(&self.config.blocks_per_function)?;
|
||||||
.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
|
// We must always have at least one block, so we generate the "extra" blocks and add 1 for
|
||||||
// the entry block.
|
// the entry block.
|
||||||
@@ -372,9 +416,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn generate_block_signature(&mut self) -> Result<BlockSignature> {
|
fn generate_block_signature(&mut self) -> Result<BlockSignature> {
|
||||||
let param_count = self
|
let param_count = self.param(&self.config.block_signature_params)?;
|
||||||
.u
|
|
||||||
.int_in_range(self.config.block_signature_params.clone())?;
|
|
||||||
|
|
||||||
let mut params = Vec::with_capacity(param_count);
|
let mut params = Vec::with_capacity(param_count);
|
||||||
for _ in 0..param_count {
|
for _ in 0..param_count {
|
||||||
@@ -395,7 +437,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a pool of vars that are going to be used in this function
|
// Create a pool of vars that are going to be used in this function
|
||||||
for _ in 0..self.u.int_in_range(self.config.vars_per_function.clone())? {
|
for _ in 0..self.param(&self.config.vars_per_function)? {
|
||||||
let ty = self.generate_type()?;
|
let ty = self.generate_type()?;
|
||||||
let var = self.create_var(builder, ty)?;
|
let var = self.create_var(builder, ty)?;
|
||||||
let value = self.generate_const(builder, ty)?;
|
let value = self.generate_const(builder, ty)?;
|
||||||
@@ -423,6 +465,9 @@ where
|
|||||||
|
|
||||||
self.blocks = self.generate_blocks(&mut builder, &sig)?;
|
self.blocks = self.generate_blocks(&mut builder, &sig)?;
|
||||||
|
|
||||||
|
// Function preamble
|
||||||
|
self.generate_jumptables(&mut builder)?;
|
||||||
|
|
||||||
// Main instruction generation loop
|
// Main instruction generation loop
|
||||||
for (i, (block, block_sig)) in self.blocks.clone().iter().enumerate() {
|
for (i, (block, block_sig)) in self.blocks.clone().iter().enumerate() {
|
||||||
let is_block0 = i == 0;
|
let is_block0 = i == 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user