Skip to content

Commit c833bc2

Browse files
committed
cranelift: CLIF Fuzzer generate brz/brnz/bricmp instructions
1 parent 2189daf commit c833bc2

File tree

2 files changed

+196
-85
lines changed

2 files changed

+196
-85
lines changed

cranelift/fuzzgen/src/function_generator.rs

Lines changed: 178 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use crate::config::Config;
22
use anyhow::Result;
3-
use arbitrary::Unstructured;
3+
use arbitrary::{Arbitrary, Unstructured};
44
use cranelift::codegen::ir::types::*;
55
use cranelift::codegen::ir::{
66
AbiParam, Block, ExternalName, Function, Opcode, Signature, Type, Value,
77
};
88
use cranelift::codegen::isa::CallConv;
99
use cranelift::frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
10-
use cranelift::prelude::{EntityRef, InstBuilder};
10+
use cranelift::prelude::{EntityRef, InstBuilder, IntCC};
1111

1212
type BlockSignature = Vec<Type>;
1313

@@ -117,11 +117,30 @@ where
117117
Ok(CallConv::SystemV)
118118
}
119119

120+
fn generate_intcc(&mut self) -> Result<IntCC> {
121+
Ok(*self.u.choose(
122+
&[
123+
IntCC::Equal,
124+
IntCC::NotEqual,
125+
IntCC::SignedLessThan,
126+
IntCC::SignedGreaterThanOrEqual,
127+
IntCC::SignedGreaterThan,
128+
IntCC::SignedLessThanOrEqual,
129+
IntCC::UnsignedLessThan,
130+
IntCC::UnsignedGreaterThanOrEqual,
131+
IntCC::UnsignedGreaterThan,
132+
IntCC::UnsignedLessThanOrEqual,
133+
IntCC::Overflow,
134+
IntCC::NotOverflow,
135+
][..],
136+
)?)
137+
}
138+
120139
fn generate_type(&mut self) -> Result<Type> {
121140
// TODO: It would be nice if we could get these directly from cranelift
122141
let scalars = [
123142
// IFLAGS, FFLAGS,
124-
// B1, B8, B16, B32, B64, B128,
143+
B1, // B8, B16, B32, B64, B128,
125144
I8, I16, I32, I64,
126145
// I128,
127146
// F32, F64,
@@ -180,60 +199,140 @@ where
180199

181200
/// Generates an instruction(`iconst`/`fconst`/etc...) to introduce a constant value
182201
fn generate_const(&mut self, builder: &mut FunctionBuilder, ty: Type) -> Result<Value> {
183-
let imm64 = match ty {
184-
I8 => self.u.arbitrary::<i8>()? as i64,
185-
I16 => self.u.arbitrary::<i16>()? as i64,
186-
I32 => self.u.arbitrary::<i32>()? as i64,
187-
I64 => self.u.arbitrary::<i64>()?,
188-
_ => unreachable!(),
189-
};
190-
let val = builder.ins().iconst(ty, imm64);
191-
192-
Ok(val)
202+
Ok(match ty {
203+
ty if ty.is_int() => {
204+
let imm64 = match ty {
205+
I8 => self.u.arbitrary::<i8>()? as i64,
206+
I16 => self.u.arbitrary::<i16>()? as i64,
207+
I32 => self.u.arbitrary::<i32>()? as i64,
208+
I64 => self.u.arbitrary::<i64>()?,
209+
_ => unreachable!(),
210+
};
211+
builder.ins().iconst(ty, imm64)
212+
}
213+
ty if ty.is_bool() => builder.ins().bconst(B1, bool::arbitrary(self.u)?),
214+
_ => unimplemented!(),
215+
})
193216
}
194217

195-
fn generate_return(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
196-
let ret_params = builder.func.signature.returns.clone();
218+
/// Chooses a random block which can be targeted by a jump / branch.
219+
/// This means any block that is not the first block.
220+
///
221+
/// For convenience we also generate values that match the block's signature
222+
fn generate_target_block(
223+
&mut self,
224+
builder: &mut FunctionBuilder,
225+
) -> Result<(Block, Vec<Value>)> {
226+
let block_targets = &self.blocks[1..];
227+
let (block, signature) = self.u.choose(block_targets)?.clone();
228+
let args = self.generate_values_for_signature(builder, signature.into_iter())?;
229+
Ok((block, args))
230+
}
197231

198-
let vars = ret_params
199-
.iter()
200-
.map(|p| self.get_variable_of_type(p.value_type))
201-
.collect::<Result<Vec<_>>>()?;
232+
fn generate_values_for_signature<I: Iterator<Item = Type>>(
233+
&mut self,
234+
builder: &mut FunctionBuilder,
235+
signature: I,
236+
) -> Result<Vec<Value>> {
237+
signature
238+
.map(|ty| {
239+
let var = self.get_variable_of_type(ty)?;
240+
let val = builder.use_var(var);
241+
Ok(val)
242+
})
243+
.collect()
244+
}
202245

203-
let vals = vars
204-
.into_iter()
205-
.map(|v| builder.use_var(v))
206-
.collect::<Vec<_>>();
246+
fn generate_return(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
247+
let types: Vec<Type> = {
248+
let rets = &builder.func.signature.returns;
249+
rets.iter().map(|p| p.value_type).collect()
250+
};
251+
let vals = self.generate_values_for_signature(builder, types.into_iter())?;
207252

208253
builder.ins().return_(&vals[..]);
209254
Ok(())
210255
}
211256

212257
fn generate_jump(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
213-
let (block, signature) = {
214-
let target = self.u.choose(&self.blocks[..])?;
215-
let target = target.clone();
216-
target
217-
};
258+
let (block, args) = self.generate_target_block(builder)?;
259+
builder.ins().jump(block, &args[..]);
260+
Ok(())
261+
}
218262

219-
let vars = signature
220-
.iter()
221-
.map(|ty| self.get_variable_of_type(*ty))
222-
.collect::<Result<Vec<_>>>()?;
263+
/// Generates a brz/brnz into a random block
264+
fn generate_br(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
265+
let (block, args) = self.generate_target_block(builder)?;
223266

224-
let vals = vars
225-
.into_iter()
226-
.map(|v| builder.use_var(v))
227-
.collect::<Vec<_>>();
267+
let condbr_types = [
268+
I8, I16, I32, I64, // TODO: I128
269+
B1,
270+
];
271+
let _type = *self.u.choose(&condbr_types[..])?;
272+
let var = self.get_variable_of_type(_type)?;
273+
let val = builder.use_var(var);
274+
275+
if bool::arbitrary(self.u)? {
276+
builder.ins().brz(val, block, &args[..]);
277+
} else {
278+
builder.ins().brnz(val, block, &args[..]);
279+
}
228280

229-
builder.ins().jump(*block, &vals[..]);
281+
// After brz/brnz we must generate a jump
282+
self.generate_jump(builder)?;
230283
Ok(())
231284
}
232285

233-
/// Inserts a random instruction into the block
234-
fn generate_instruction(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
235-
let (op, args, rets, inserter) = *self.u.choose(OPCODE_SIGNATURES)?;
236-
inserter(self, builder, op, args, rets)
286+
fn generate_bricmp(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
287+
let (block, args) = self.generate_target_block(builder)?;
288+
let cond = self.generate_intcc()?;
289+
290+
let bricmp_types = [
291+
I8, I16, I32, I64, // TODO: I128
292+
];
293+
let _type = *self.u.choose(&bricmp_types[..])?;
294+
295+
let lhs_var = self.get_variable_of_type(_type)?;
296+
let lhs_val = builder.use_var(lhs_var);
297+
298+
let rhs_var = self.get_variable_of_type(_type)?;
299+
let rhs_val = builder.use_var(rhs_var);
300+
301+
builder
302+
.ins()
303+
.br_icmp(cond, lhs_val, rhs_val, block, &args[..]);
304+
305+
// After bricmp's we must generate a jump
306+
self.generate_jump(builder)?;
307+
Ok(())
308+
}
309+
310+
/// We always need to exit safely out of a block.
311+
/// This either means a jump into another block or a return.
312+
fn finalize_block(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
313+
let gen = self.u.choose(
314+
&[
315+
Self::generate_bricmp,
316+
Self::generate_br,
317+
Self::generate_jump,
318+
Self::generate_return,
319+
][..],
320+
)?;
321+
322+
gen(self, builder)
323+
}
324+
325+
/// Fills the current block with random instructions
326+
fn generate_instructions(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
327+
for _ in 0..self
328+
.u
329+
.int_in_range(self.config.instructions_per_block.clone())?
330+
{
331+
let (op, args, rets, inserter) = *self.u.choose(OPCODE_SIGNATURES)?;
332+
inserter(self, builder, op, args, rets)?;
333+
}
334+
335+
Ok(())
237336
}
238337

239338
/// Creates a random amount of blocks in this function
@@ -260,7 +359,11 @@ where
260359
builder.append_block_params_for_function_params(block);
261360
Ok((block, sig.params.iter().map(|a| a.value_type).collect()))
262361
} else {
263-
Ok((block, self.generate_block_signature()?))
362+
let sig = self.generate_block_signature()?;
363+
sig.iter().for_each(|ty| {
364+
builder.append_block_param(block, *ty);
365+
});
366+
Ok((block, sig))
264367
}
265368
})
266369
.collect::<Result<Vec<_>>>()?;
@@ -280,7 +383,29 @@ where
280383
Ok(params)
281384
}
282385

283-
/// We generate a function in multiple stages: by first creating a random number of empty
386+
fn build_variable_pool(&mut self, builder: &mut FunctionBuilder) -> Result<()> {
387+
let block = builder.current_block().unwrap();
388+
let func_params = builder.func.signature.params.clone();
389+
390+
// Define variables for the function signature
391+
for (i, param) in func_params.iter().enumerate() {
392+
let var = self.create_var(builder, param.value_type)?;
393+
let block_param = builder.block_params(block)[i];
394+
builder.def_var(var, block_param);
395+
}
396+
397+
// Create a pool of vars that are going to be used in this function
398+
for _ in 0..self.u.int_in_range(self.config.vars_per_function.clone())? {
399+
let ty = self.generate_type()?;
400+
let var = self.create_var(builder, ty)?;
401+
let value = self.generate_const(builder, ty)?;
402+
builder.def_var(var, value);
403+
}
404+
405+
Ok(())
406+
}
407+
408+
/// We generate a function in multiple stages:
284409
///
285410
/// * First we generate a random number of empty blocks
286411
/// * Then we generate a random pool of variables to be used throughout the function
@@ -299,50 +424,28 @@ where
299424
self.blocks = self.generate_blocks(&mut builder, &sig)?;
300425

301426
// Main instruction generation loop
302-
for (i, (block, signature)) in self.blocks.clone().iter().enumerate() {
427+
for (i, (block, block_sig)) in self.blocks.clone().iter().enumerate() {
428+
let is_block0 = i == 0;
303429
builder.switch_to_block(*block);
304430

305-
if i == 0 {
306-
// Define variables for the function signature
307-
for (i, param) in sig.params.iter().enumerate() {
308-
let var = self.create_var(&mut builder, param.value_type)?;
309-
let block_param = builder.block_params(*block)[i];
310-
builder.def_var(var, block_param);
311-
}
312-
313-
// Create a pool of vars that are going to be used in this function
314-
for _ in 0..self.u.int_in_range(self.config.vars_per_function.clone())? {
315-
let ty = self.generate_type()?;
316-
let var = self.create_var(&mut builder, ty)?;
317-
let value = self.generate_const(&mut builder, ty)?;
318-
builder.def_var(var, value);
319-
}
431+
if is_block0 {
432+
// The first block is special because we must create variables both for the
433+
// block signature and for the variable pool. Additionally, we must also define
434+
// initial values for all variables that are not the function signature.
435+
self.build_variable_pool(&mut builder)?;
320436
} else {
321437
// Define variables for the block params
322-
for (i, ty) in signature.iter().enumerate() {
438+
for (i, ty) in block_sig.iter().enumerate() {
323439
let var = self.get_variable_of_type(*ty)?;
324440
let block_param = builder.block_params(*block)[i];
325441
builder.def_var(var, block_param);
326442
}
327443
}
328444

329445
// Generate block instructions
330-
for _ in 0..self
331-
.u
332-
.int_in_range(self.config.instructions_per_block.clone())?
333-
{
334-
self.generate_instruction(&mut builder)?;
335-
}
446+
self.generate_instructions(&mut builder)?;
336447

337-
// We always need to exit safely out of a block.
338-
// For block 0 this means a return, but for other block it is a jump into any other
339-
// random block.
340-
if i == 0 {
341-
// TODO: We should make this, part of the regular instruction selection
342-
self.generate_return(&mut builder)?;
343-
} else {
344-
self.generate_jump(&mut builder)?;
345-
}
448+
self.finalize_block(&mut builder)?;
346449
}
347450

348451
builder.seal_all_blocks();

cranelift/fuzzgen/src/lib.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,23 @@ where
4747
}
4848
}
4949

50+
fn generate_datavalue(&mut self, ty: Type) -> Result<DataValue> {
51+
Ok(match ty {
52+
ty if ty.is_int() => {
53+
let imm64 = match ty {
54+
I8 => self.u.arbitrary::<i8>()? as i64,
55+
I16 => self.u.arbitrary::<i16>()? as i64,
56+
I32 => self.u.arbitrary::<i32>()? as i64,
57+
I64 => self.u.arbitrary::<i64>()?,
58+
_ => unreachable!(),
59+
};
60+
DataValue::from_integer(imm64, ty)?
61+
}
62+
ty if ty.is_bool() => DataValue::B(bool::arbitrary(self.u)?),
63+
_ => unimplemented!(),
64+
})
65+
}
66+
5067
fn generate_test_inputs(&mut self, signature: &Signature) -> Result<Vec<TestCaseInput>> {
5168
let num_tests = self.u.int_in_range(self.config.test_case_inputs.clone())?;
5269
let mut inputs = Vec::with_capacity(num_tests);
@@ -55,16 +72,7 @@ where
5572
let test_args = signature
5673
.params
5774
.iter()
58-
.map(|p| {
59-
let imm64 = match p.value_type {
60-
I8 => self.u.arbitrary::<i8>()? as i64,
61-
I16 => self.u.arbitrary::<i16>()? as i64,
62-
I32 => self.u.arbitrary::<i32>()? as i64,
63-
I64 => self.u.arbitrary::<i64>()?,
64-
_ => unreachable!(),
65-
};
66-
Ok(DataValue::from_integer(imm64, p.value_type)?)
67-
})
75+
.map(|p| self.generate_datavalue(p.value_type))
6876
.collect::<Result<TestCaseInput>>()?;
6977

7078
inputs.push(test_args);

0 commit comments

Comments
 (0)