Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
369 changes: 339 additions & 30 deletions tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/block_context.rs

Large diffs are not rendered by default.

74 changes: 33 additions & 41 deletions tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/function_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,10 @@ impl<'a> FuzzerFunctionContext<'a> {
/// SSA block can use variables from predecessor that is not in branch.
/// Look [Self::find_closest_parent] for more details.
fn store_variables(&mut self) {
self.stored_variables_for_block
.insert(self.current_block.block_id, self.current_block.context.stored_values.clone());
self.stored_variables_for_block.insert(
self.current_block.block_id,
self.current_block.context.stored_variables.clone(),
);
}

fn process_jmp_if_command(&mut self, block_then_idx: usize, block_else_idx: usize) {
Expand Down Expand Up @@ -324,18 +326,16 @@ impl<'a> FuzzerFunctionContext<'a> {
// creates new contexts of created blocks
let mut parent_blocks_history = self.current_block.context.parent_blocks_history.clone();
parent_blocks_history.push_front(self.current_block.block_id);
let mut block_then_context = BlockContext::new(
self.current_block.context.stored_values.clone(),
self.current_block.context.memory_addresses.clone(),
parent_blocks_history.clone(),
SsaBlockOptions::from(self.function_context_options.clone()),
);
let mut block_else_context = BlockContext::new(
self.current_block.context.stored_values.clone(),
self.current_block.context.memory_addresses.clone(),
let mut block_then_context = BlockContext {
children_blocks: vec![],
parent_blocks_history: parent_blocks_history.clone(),
..self.current_block.context.clone()
};
let mut block_else_context = BlockContext {
children_blocks: vec![],
parent_blocks_history,
SsaBlockOptions::from(self.function_context_options.clone()),
);
..self.current_block.context.clone()
};

// inserts instructions into created blocks
self.switch_to_block(block_then_id);
Expand Down Expand Up @@ -431,12 +431,11 @@ impl<'a> FuzzerFunctionContext<'a> {
let mut parent_blocks_history = self.current_block.context.parent_blocks_history.clone();
parent_blocks_history.push_front(self.current_block.block_id);
self.switch_to_block(destination_block_id);
let mut destination_block_context = BlockContext::new(
self.current_block.context.stored_values.clone(),
self.current_block.context.memory_addresses.clone(),
let mut destination_block_context = BlockContext {
children_blocks: vec![],
parent_blocks_history,
SsaBlockOptions::from(self.function_context_options.clone()),
);
..self.current_block.context.clone()
};

// inserts instructions into the new block
destination_block_context.insert_instructions(
Expand Down Expand Up @@ -582,12 +581,8 @@ impl<'a> FuzzerFunctionContext<'a> {
self.insert_jmp_instruction(block_if_id, vec![start_id.clone()]);

// fill body block with instructions
let mut block_body_context = BlockContext::new(
self.current_block.context.stored_values.clone(),
self.current_block.context.memory_addresses.clone(),
self.current_block.context.parent_blocks_history.clone(),
SsaBlockOptions::from(self.function_context_options.clone()),
);
let mut block_body_context =
BlockContext { children_blocks: vec![], ..self.current_block.context.clone() };
self.switch_to_block(block_body_id);
block_body_context.insert_instructions(
self.acir_builder,
Expand All @@ -605,12 +600,11 @@ impl<'a> FuzzerFunctionContext<'a> {
self.current_block.context.clone()
};
// end block does not share variables with body block, so we copy them from the current block
let block_end_context = BlockContext::new(
end_context.stored_values.clone(),
end_context.memory_addresses.clone(),
block_body_context.parent_blocks_history.clone(),
SsaBlockOptions::from(self.function_context_options.clone()),
);
let block_end_context = BlockContext {
children_blocks: vec![],
parent_blocks_history: block_body_context.parent_blocks_history.clone(),
..end_context
};

let end_block_stored = StoredBlock { context: block_end_context, block_id: block_end_id };
// connect end block with the current block
Expand Down Expand Up @@ -718,12 +712,11 @@ impl<'a> FuzzerFunctionContext<'a> {
let closest_parent = self.find_closest_parent(&first_block, &second_block);
let closest_parent_block = self.stored_blocks[&closest_parent].clone();

let merged_block_context = BlockContext::new(
closest_parent_block.context.stored_values.clone(),
closest_parent_block.context.memory_addresses.clone(),
let merged_block_context = BlockContext {
children_blocks: vec![],
parent_blocks_history,
SsaBlockOptions::from(self.function_context_options.clone()),
);
..closest_parent_block.context.clone()
};
self.switch_to_block(first_block.block_id);
first_block.context.finalize_block_with_jmp(
self.acir_builder,
Expand Down Expand Up @@ -915,12 +908,11 @@ impl<'a> FuzzerFunctionContext<'a> {

// add instructions to the return block
self.switch_to_block(return_block_id);
let mut return_block_context = BlockContext::new(
last_block.context.stored_values.clone(),
last_block.context.memory_addresses.clone(),
VecDeque::new(),
SsaBlockOptions::from(self.function_context_options.clone()),
);
let mut return_block_context = BlockContext {
children_blocks: vec![],
parent_blocks_history: VecDeque::new(),
..last_block.context.clone()
};
return_block_context.insert_instructions(
self.acir_builder,
self.brillig_builder,
Expand Down
159 changes: 159 additions & 0 deletions tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/fuzz_target_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -764,4 +764,163 @@ mod tests {
None => panic!("Program failed to execute"),
}
}

/// Test array get and set
/// fn main f0 {
/// b0(v0: Field, v1: Field, v2: Field, v3: Field, v4: Field, v5: u1, v6: u1):
/// v7 = make_array [v0, v1, v2, v3, v4] : [Field; 5]
/// v8 = truncate v0 to 32 bits, max_bit_size: 254
/// v9 = cast v8 as u32
/// v11 = mod v9, u32 5
/// v12 = array_set v7, index v11, value v4
/// v13 = truncate v0 to 32 bits, max_bit_size: 254
/// v14 = cast v13 as u32
/// v15 = mod v14, u32 5
/// v16 = array_get v12, index v15 -> Field
/// return v16
/// }
#[test]
fn array_get_and_set() {
let arg_0_field = Argument { index: 0, value_type: ValueType::Field };
// create array [v0, v1]
let create_array_block = InstructionBlock {
instructions: vec![Instruction::CreateArray {
elements_indices: vec![0, 1, 2, 3, 4],
element_type: ValueType::Field,
is_references: false,
}],
};
// create new array setting new_array[0] = v4
let array_set_block = InstructionBlock {
instructions: vec![Instruction::ArraySet {
array_index: 0,
index: arg_0_field,
value_index: 4, // set v4
mutable: false,
safe_index: true,
}],
};
// get new_array[0]
let array_get_block = InstructionBlock {
instructions: vec![Instruction::ArrayGet {
array_index: 1,
index: arg_0_field,
safe_index: true,
}],
};
let instructions_blocks = vec![create_array_block, array_set_block, array_get_block];
let commands = vec![
FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 0 },
FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 1 },
];
let main_func = FunctionData {
commands,
return_instruction_block_idx: 2,
return_type: ValueType::Field,
};
let fuzzer_data = FuzzerData {
instruction_blocks: instructions_blocks,
functions: vec![main_func],
initial_witness: default_witness(),
};
let result = fuzz_target(fuzzer_data, FuzzerOptions::default());
match result {
Some(result) => assert_eq!(result.get_return_value(), FieldElement::from(4_u32)),
None => panic!("Program failed to execute"),
}
}

/// Test that references in arrays work
/// fn main f0 {
/// b0(v0: Field, v1: Field, v2: Field, v3: Field, v4: Field, v5: u1, v6: u1):
/// v7 = allocate -> &mut Field
/// store v0 at v7
/// v8 = allocate -> &mut Field
/// store v2 at v8
/// v9 = make_array [v7, v8, v7] : [&mut Field; 3]
/// store v1 at v7
/// v10 = make_array [v7, v7, v7] : [&mut Field; 3] <----- set v9, index 1, value v7
/// jmp b1()
/// b1():
/// v11 = load v7 -> Field <---- its simplified from v11 = v10[1]; v12 = load v11
/// return v11
/// }
/// assert that return value is v1
#[test]
fn test_reference_in_array() {
let _ = env_logger::try_init();
let arg_0_field = Argument { index: 0, value_type: ValueType::Field };
let arg_1_field = Argument { index: 1, value_type: ValueType::Field };
let arg_2_field = Argument { index: 2, value_type: ValueType::Field };

let add_to_memory_block =
InstructionBlock { instructions: vec![Instruction::AddToMemory { lhs: arg_0_field }] };
let add_to_memory_block_2 =
InstructionBlock { instructions: vec![Instruction::AddToMemory { lhs: arg_2_field }] };
let set_to_memory_block = InstructionBlock {
instructions: vec![Instruction::SetToMemory {
memory_addr_index: 0,
value: arg_1_field,
}],
};
let set_to_array_block = InstructionBlock {
instructions: vec![Instruction::ArraySetWithConstantIndex {
array_index: 0,
index: 1,
value_index: 0,
mutable: false,
safe_index: true,
}],
};
let create_array_block = InstructionBlock {
instructions: vec![Instruction::CreateArray {
elements_indices: vec![0, 1, 2],
element_type: ValueType::Field,
is_references: true,
}],
};
let get_from_array_block = InstructionBlock {
instructions: vec![Instruction::ArrayGetWithConstantIndex {
array_index: 1,
index: 1,
safe_index: true,
}],
};
let typed_memory_2 = Argument { index: 2, value_type: ValueType::Field };
let load_from_memory_block = InstructionBlock {
instructions: vec![Instruction::LoadFromMemory { memory_addr: typed_memory_2 }],
};
let instructions_blocks = vec![
add_to_memory_block,
add_to_memory_block_2,
create_array_block,
set_to_memory_block,
set_to_array_block,
get_from_array_block,
load_from_memory_block,
];
let commands = vec![
FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 0 },
FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 1 },
FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 2 },
FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 3 },
FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 4 },
FuzzerFunctionCommand::InsertSimpleInstructionBlock { instruction_block_idx: 5 },
];
let main_func = FunctionData {
commands,
return_instruction_block_idx: 6,
return_type: ValueType::Field,
};
let fuzzer_data = FuzzerData {
instruction_blocks: instructions_blocks,
functions: vec![main_func],
initial_witness: default_witness(),
};
let result = fuzz_target(fuzzer_data, FuzzerOptions::default());
match result {
Some(result) => assert_eq!(result.get_return_value(), FieldElement::from(1_u32)),
None => panic!("Program failed to execute"),
}
}
}
38 changes: 37 additions & 1 deletion tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ use libfuzzer_sys::arbitrary::Arbitrary;
use noir_ssa_fuzzer::typed_value::ValueType;
use serde::{Deserialize, Serialize};

#[derive(Arbitrary, Debug, Clone, Copy, Serialize, Deserialize)]
pub(crate) struct Array {
pub(crate) size: usize,
pub(crate) element_type: ValueType,
}

#[derive(Arbitrary, Debug, Clone, Copy, Serialize, Deserialize)]
pub(crate) struct Argument {
/// Index of the argument in the context of stored variables of this type
Expand All @@ -18,7 +24,7 @@ pub(crate) struct Argument {
/// Represents set of instructions
///
/// For operations that take two arguments we ignore type of the second argument.
#[derive(Arbitrary, Debug, Clone, Copy, Serialize, Deserialize)]
#[derive(Arbitrary, Debug, Clone, Serialize, Deserialize)]
pub(crate) enum Instruction {
/// Addition of two values
AddChecked { lhs: Argument, rhs: Argument },
Expand Down Expand Up @@ -68,6 +74,36 @@ pub(crate) enum Instruction {
/// Store value to mutable memory
/// Stores value to memory with insert_store
SetToMemory { memory_addr_index: usize, value: Argument },

/// Create array, only type of first argument is used
/// Other elements will be taken from stored variables of the same type
CreateArray { elements_indices: Vec<usize>, element_type: ValueType, is_references: bool },
/// Get element from array, index will be casted to u32, only for arrays without references
/// If safe_index is true, index will be taken modulo the size of the array
ArrayGet { array_index: usize, index: Argument, safe_index: bool },
/// Set element in array, index will be casted to u32, only for arrays without references
/// Value will be cast to the type of the array
/// If safe_index is true, index will be taken modulo the size of the array
ArraySet {
array_index: usize,
index: Argument,
value_index: usize,
mutable: bool,
safe_index: bool,
},
/// Get element from array, index is constant
/// If safe_index is true, index will be taken modulo the size of the array
ArrayGetWithConstantIndex { array_index: usize, index: usize, safe_index: bool },
/// Set element in array, index is constant
/// Value will be cast to the type of the array
/// If safe_index is true, index will be taken modulo the size of the array
ArraySetWithConstantIndex {
array_index: usize,
index: usize,
value_index: usize,
mutable: bool,
safe_index: bool,
},
}

/// Represents set of instructions
Expand Down
8 changes: 7 additions & 1 deletion tooling/ssa_fuzzer/fuzzer/src/fuzz_lib/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ pub(crate) struct InstructionOptions {
pub(crate) load_enabled: bool,
pub(crate) store_enabled: bool,
pub(crate) alloc_enabled: bool,
pub(crate) create_array_enabled: bool,
pub(crate) array_get_enabled: bool,
pub(crate) array_set_enabled: bool,
}

impl Default for InstructionOptions {
Expand All @@ -42,6 +45,9 @@ impl Default for InstructionOptions {
load_enabled: true,
store_enabled: true,
alloc_enabled: true,
create_array_enabled: true,
array_get_enabled: true,
array_set_enabled: true,
}
}
}
Expand Down Expand Up @@ -137,7 +143,7 @@ pub(crate) struct FuzzerOptions {
impl Default for FuzzerOptions {
fn default() -> Self {
Self {
compile_options: CompileOptions { show_ssa: false, ..Default::default() },
compile_options: CompileOptions { show_ssa: true, ..Default::default() },
max_ssa_blocks_num: 100,
max_instructions_num: 1000,
max_iterations_num: 1000,
Expand Down
Loading
Loading