Unverified Commit d1d04f32 by van den Bosch Committed by GitHub

Replace old mocks in unit tests (#301)

parent c7182ddb
......@@ -8,16 +8,13 @@ fn intersect_relative_values<T: SpecializeByConditional + RegisterDomain>(
values_left
.iter()
.filter_map(|(id, offset)| {
values_right
.get(id)
.map(|other_offset| {
if let Ok(intersected_offset) = offset.clone().intersect(other_offset) {
Some((id.clone(), intersected_offset))
} else {
None
}
})
.flatten()
values_right.get(id).and_then(|other_offset| {
if let Ok(intersected_offset) = offset.clone().intersect(other_offset) {
Some((id.clone(), intersected_offset))
} else {
None
}
})
})
.collect()
}
......@@ -26,8 +23,7 @@ impl<T: SpecializeByConditional + RegisterDomain> SpecializeByConditional for Da
fn add_signed_less_equal_bound(mut self, bound: &Bitvector) -> Result<Self, Error> {
self.absolute_value = self
.absolute_value
.map(|value| value.add_signed_less_equal_bound(bound).ok())
.flatten();
.and_then(|value| value.add_signed_less_equal_bound(bound).ok());
if self.is_empty() {
Err(anyhow!("Empty value"))
} else {
......@@ -38,8 +34,7 @@ impl<T: SpecializeByConditional + RegisterDomain> SpecializeByConditional for Da
fn add_unsigned_less_equal_bound(mut self, bound: &Bitvector) -> Result<Self, Error> {
self.absolute_value = self
.absolute_value
.map(|value| value.add_unsigned_less_equal_bound(bound).ok())
.flatten();
.and_then(|value| value.add_unsigned_less_equal_bound(bound).ok());
if self.is_empty() {
Err(anyhow!("Empty value"))
} else {
......@@ -50,8 +45,7 @@ impl<T: SpecializeByConditional + RegisterDomain> SpecializeByConditional for Da
fn add_signed_greater_equal_bound(mut self, bound: &Bitvector) -> Result<Self, Error> {
self.absolute_value = self
.absolute_value
.map(|value| value.add_signed_greater_equal_bound(bound).ok())
.flatten();
.and_then(|value| value.add_signed_greater_equal_bound(bound).ok());
if self.is_empty() {
Err(anyhow!("Empty value"))
} else {
......@@ -62,8 +56,7 @@ impl<T: SpecializeByConditional + RegisterDomain> SpecializeByConditional for Da
fn add_unsigned_greater_equal_bound(mut self, bound: &Bitvector) -> Result<Self, Error> {
self.absolute_value = self
.absolute_value
.map(|value| value.add_unsigned_greater_equal_bound(bound).ok())
.flatten();
.and_then(|value| value.add_unsigned_greater_equal_bound(bound).ok());
if self.is_empty() {
Err(anyhow!("Empty value"))
} else {
......@@ -74,8 +67,7 @@ impl<T: SpecializeByConditional + RegisterDomain> SpecializeByConditional for Da
fn add_not_equal_bound(mut self, bound: &Bitvector) -> Result<Self, Error> {
self.absolute_value = self
.absolute_value
.map(|value| value.add_not_equal_bound(bound).ok())
.flatten();
.and_then(|value| value.add_not_equal_bound(bound).ok());
if self.is_empty() {
Err(anyhow!("Empty value"))
} else {
......
......@@ -2,11 +2,11 @@ use super::*;
impl State {
/// Generate a mock state for an ARM-32 state.
pub fn mock() -> State {
pub fn mock_arm32() -> State {
State::new(
&Tid::new("mock_fn"),
&Variable::mock("sp", 4),
&CallingConvention::mock_standard_arm_32(),
&CallingConvention::mock_arm32(),
)
}
......@@ -39,12 +39,12 @@ fn mock_stack_param_id(offset: i64, size: u64) -> AbstractIdentifier {
#[test]
fn test_new() {
let state = State::mock();
let state = State::mock_arm32();
// Test the generated stack
assert_eq!(&state.stack_id, &mock_stack_id());
assert_eq!(state.stack.iter().len(), 0);
// Assert that the register values are as expected
assert_eq!(state.register.len(), 9); // 8 parameters plus stack pointer
assert_eq!(state.register.len(), 7); // 6 parameter register plus stack pointer
assert_eq!(
state.get_register(&Variable::mock("sp", 4)),
DataDomain::from_target(
......@@ -53,11 +53,14 @@ fn test_new() {
)
);
// Check the generated tracked IDs
assert_eq!(state.tracked_ids.len(), 8);
assert_eq!(state.tracked_ids.len(), 6);
for (id, access_pattern) in state.tracked_ids.iter() {
assert_eq!(
state.get_register(id.unwrap_register()),
DataDomain::from_target(id.clone(), Bitvector::zero(ByteSize::new(4).into()).into())
DataDomain::from_target(
id.clone(),
Bitvector::zero(id.unwrap_register().size.into()).into()
)
);
assert_eq!(access_pattern, &AccessPattern::new());
}
......@@ -65,7 +68,7 @@ fn test_new() {
#[test]
fn test_store_and_load_from_stack() {
let mut state = State::mock();
let mut state = State::mock_arm32();
let address = DataDomain::from_target(mock_stack_id(), Bitvector::from_i32(-4).into());
let value: DataDomain<BitvectorDomain> = Bitvector::from_i32(0).into();
// write and load a value to the current stack frame
......@@ -81,12 +84,12 @@ fn test_store_and_load_from_stack() {
let stack_param_id = mock_stack_param_id(4, 4);
let stack_param =
DataDomain::from_target(stack_param_id.clone(), Bitvector::from_i32(0).into());
assert_eq!(state.tracked_ids.iter().len(), 8);
assert_eq!(state.tracked_ids.iter().len(), 6);
assert_eq!(
state.load_value(address.clone(), ByteSize::new(4)),
stack_param
);
assert_eq!(state.tracked_ids.iter().len(), 9);
assert_eq!(state.tracked_ids.iter().len(), 7);
assert_eq!(
state
.tracked_ids
......@@ -99,7 +102,7 @@ fn test_store_and_load_from_stack() {
#[test]
fn test_load_unsized_from_stack() {
let mut state = State::mock();
let mut state = State::mock_arm32();
// Load an existing stack param (generated by a sized load at the same address)
let address = DataDomain::from_target(mock_stack_id(), Bitvector::from_i32(0).into());
let stack_param_id = mock_stack_param_id(0, 4);
......@@ -122,7 +125,7 @@ fn test_load_unsized_from_stack() {
#[test]
fn test_eval() {
let mut state = State::mock();
let mut state = State::mock_arm32();
// Test the eval method
let expr = Expression::Var(Variable::mock("sp", 4)).plus_const(42);
assert_eq!(
......@@ -139,7 +142,7 @@ fn test_eval() {
#[test]
fn test_extern_symbol_handling() {
let mut state = State::mock();
let mut state = State::mock_arm32();
let extern_symbol = ExternSymbol::mock_arm32();
let cconv = CallingConvention::mock_arm32();
let call = Term {
......
use super::*;
use crate::intermediate_representation::DatatypeProperties;
fn bv(value: i64) -> ValueDomain {
ValueDomain::from(Bitvector::from_i64(value))
......@@ -12,24 +11,6 @@ fn new_id(time: &str, reg_name: &str) -> AbstractIdentifier {
)
}
fn mock_extern_symbol(name: &str) -> (Tid, ExternSymbol) {
let arg = Arg::from_var(register("RDX"), None);
let tid = Tid::new("extern_".to_string() + name);
(
tid.clone(),
ExternSymbol {
tid,
addresses: vec![],
name: name.into(),
calling_convention: None,
parameters: vec![arg.clone()],
return_values: vec![arg],
no_return: false,
has_var_args: false,
},
)
}
fn register(name: &str) -> Variable {
Variable {
name: name.into(),
......@@ -70,36 +51,9 @@ fn return_term(target_name: &str) -> Term<Jmp> {
}
fn mock_project() -> (Project, Config) {
let program = Program {
subs: BTreeMap::new(),
extern_symbols: vec![
mock_extern_symbol("malloc"),
mock_extern_symbol("free"),
mock_extern_symbol("other"),
]
.into_iter()
.collect(),
entry_points: BTreeSet::new(),
address_base_offset: 0,
};
let program_term = Term {
tid: Tid::new("program"),
term: program,
};
let cconv = CallingConvention::mock_x64();
let register_set = vec!["RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI"]
.into_iter()
.map(|name| Variable::mock(name, ByteSize::new(8)))
.collect();
let project = Project::mock_x64();
(
Project {
program: program_term,
cpu_architecture: "x86_64".to_string(),
stack_pointer_register: register("RSP"),
calling_conventions: BTreeMap::from_iter([(cconv.name.clone(), cconv)]),
register_set,
datatype_properties: DatatypeProperties::mock(),
},
project,
Config {
allocation_symbols: vec!["malloc".into()],
deallocation_symbols: vec!["free".into()],
......@@ -160,11 +114,11 @@ fn context_problem_implementation() {
state.set_register(&register("RBP"), bv(13).into());
state.set_register(&register("RSI"), bv(14).into());
let malloc = call_term("extern_malloc");
let malloc = call_term("malloc");
let mut state_after_malloc = context.update_call_stub(&state, &malloc).unwrap();
assert_eq!(
state_after_malloc.get_register(&register("RDX")),
Data::from_target(new_id("call_extern_malloc", "RDX"), bv(0))
state_after_malloc.get_register(&register("RAX")),
Data::from_target(new_id("call_malloc", "RAX"), bv(0))
);
assert_eq!(state_after_malloc.memory.get_num_objects(), 2);
assert_eq!(
......@@ -181,9 +135,9 @@ fn context_problem_implementation() {
state_after_malloc.set_register(
&register("RBP"),
Data::from_target(new_id("call_extern_malloc", "RDX"), bv(0)),
Data::from_target(new_id("call_malloc", "RAX"), bv(0)),
);
let free = call_term("extern_free");
let free = call_term("free");
let state_after_free = context
.update_call_stub(&state_after_malloc, &free)
.unwrap();
......@@ -191,10 +145,10 @@ fn context_problem_implementation() {
assert_eq!(state_after_free.memory.get_num_objects(), 2);
assert_eq!(
state_after_free.get_register(&register("RBP")),
Data::from_target(new_id("call_extern_malloc", "RDX"), bv(0))
Data::from_target(new_id("call_malloc", "RAX"), bv(0))
);
let other_extern_fn = call_term("extern_other");
let other_extern_fn = call_term("other_function");
let state_after_other_fn = context.update_call_stub(&state, &other_extern_fn).unwrap();
assert_eq!(
......
......@@ -245,8 +245,7 @@ impl State {
let mut address_val = self.eval(address_expr);
if let Some((start_index, end_index)) = address_val
.get_absolute_value()
.map(|val| val.try_to_offset_interval().ok())
.flatten()
.and_then(|val| val.try_to_offset_interval().ok())
{
if (start_index > -1024 && start_index < 1024)
|| (end_index > -1024 && end_index < 1024)
......
use crate::intermediate_representation::*;
use std::{collections::BTreeMap, iter::FromIterator};
pub struct Setup;
......@@ -394,133 +393,78 @@ fn mock_defs_for_memcpy(copy_from_global: bool, blk_num: usize) -> Vec<Term<Def>
impl ExternSymbol {
pub fn mock_memcpy_symbol_arm() -> ExternSymbol {
ExternSymbol {
tid: Tid::new("memcpy"),
addresses: vec!["UNKNOWN".to_string()],
name: "memcpy".to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![
Arg::mock_register("r0", 4),
Arg::mock_register("r1", 4),
Arg::mock_register("r2", 4),
],
return_values: vec![Arg::mock_register("r0", 4)],
no_return: false,
has_var_args: true,
}
let mut ex = ExternSymbol::create_extern_symbol(
"memcpy",
CallingConvention::mock_arm32(),
None,
None,
);
ex.parameters = vec![
Arg::mock_register("r0", 4),
Arg::mock_register("r1", 4),
Arg::mock_register("r2", 4),
];
ex
}
pub fn mock_sprintf_symbol_arm() -> ExternSymbol {
ExternSymbol {
tid: Tid::new("sprintf"),
addresses: vec!["UNKNOWN".to_string()],
name: "sprintf".to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![Arg::mock_register("r0", 4), Arg::mock_register("r1", 4)],
return_values: vec![Arg::mock_register("r0", 4)],
no_return: false,
has_var_args: true,
}
let mut ex = ExternSymbol::create_extern_symbol(
"sprintf",
CallingConvention::mock_arm32(),
None,
None,
);
ex.parameters = vec![Arg::mock_register("r0", 4), Arg::mock_register("r1", 4)];
ex
}
pub fn mock_scanf_symbol_arm() -> ExternSymbol {
ExternSymbol {
tid: Tid::new("scanf"),
addresses: vec!["UNKNOWN".to_string()],
name: "scanf".to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![Arg::mock_register("r0", 4)],
return_values: vec![Arg::mock_register("r0", 4)],
no_return: false,
has_var_args: true,
}
ExternSymbol::create_extern_symbol(
"scanf",
CallingConvention::mock_arm32(),
Some(Datatype::Pointer),
Some(Datatype::Integer),
)
}
pub fn mock_sscanf_symbol_arm() -> ExternSymbol {
ExternSymbol {
tid: Tid::new("sscanf"),
addresses: vec!["UNKNOWN".to_string()],
name: "sscanf".to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![Arg::mock_register("r0", 4), Arg::mock_register("r1", 4)],
return_values: vec![Arg::mock_register("r0", 4)],
no_return: false,
has_var_args: true,
}
let mut ex = ExternSymbol::create_extern_symbol(
"sscanf",
CallingConvention::mock_arm32(),
None,
None,
);
ex.parameters = vec![Arg::mock_register("r0", 4), Arg::mock_register("r1", 4)];
ex
}
pub fn mock_strcat_symbol_arm() -> ExternSymbol {
ExternSymbol {
tid: Tid::new("strcat"),
addresses: vec!["UNKNOWN".to_string()],
name: "strcat".to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![Arg::mock_register("r0", 4), Arg::mock_register("r1", 4)],
return_values: vec![Arg::mock_register("r0", 4)],
no_return: false,
has_var_args: false,
}
let mut ex = ExternSymbol::create_extern_symbol(
"strcat",
CallingConvention::mock_arm32(),
None,
None,
);
ex.parameters = vec![Arg::mock_register("r0", 4), Arg::mock_register("r1", 4)];
ex
}
pub fn mock_free_symbol_arm() -> ExternSymbol {
ExternSymbol {
tid: Tid::new("free"),
addresses: vec!["UNKNOWN".to_string()],
name: "free".to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![Arg::mock_register("r0", 4)],
return_values: vec![],
no_return: false,
has_var_args: false,
}
ExternSymbol::create_extern_symbol(
"free",
CallingConvention::mock_arm32(),
Some(Datatype::Pointer),
None,
)
}
pub fn mock_malloc_symbol_arm() -> ExternSymbol {
ExternSymbol {
tid: Tid::new("malloc"),
addresses: vec!["UNKNOWN".to_string()],
name: "malloc".to_string(),
calling_convention: Some("__stdcall".to_string()),
parameters: vec![Arg::mock_register("r0", 4)],
return_values: vec![Arg::mock_register("r0", 4)],
no_return: false,
has_var_args: false,
}
}
}
impl CallingConvention {
pub fn mock_standard_arm_32() -> CallingConvention {
CallingConvention {
name: "__stdcall".to_string(), // so that the mock is useable as standard calling convention in tests
integer_parameter_register: ["r0", "r1", "r2", "r3"]
.iter()
.map(|s| Variable::mock(s, 4))
.collect(),
float_parameter_register: ["s0", "s1", "s2", "s3"]
.iter()
.map(|s| Expression::Var(Variable::mock(s, 4)))
.collect(),
integer_return_register: vec![Variable::mock("r0", 4)],
float_return_register: vec![],
callee_saved_register: vec![Variable::mock("r11", 4)],
}
}
}
impl DatatypeProperties {
pub fn mock_standard_arm_32() -> DatatypeProperties {
DatatypeProperties {
char_size: ByteSize::new(1),
double_size: ByteSize::new(8),
float_size: ByteSize::new(4),
integer_size: ByteSize::new(4),
long_double_size: ByteSize::new(8),
long_long_size: ByteSize::new(8),
long_size: ByteSize::new(4),
pointer_size: ByteSize::new(4),
short_size: ByteSize::new(2),
}
ExternSymbol::create_extern_symbol(
"malloc",
CallingConvention::mock_arm32(),
Some(Datatype::Integer),
Some(Datatype::Pointer),
)
}
}
......@@ -591,7 +535,7 @@ pub fn mock_project_with_intraprocedural_control_flow(
symbol_call_config: Vec<(ExternSymbol, Vec<bool>)>,
sub_name: &str,
) -> Project {
let mut program = Program::mock_x64();
let mut program = Program::mock_arm32();
let mocked_sub = mock_sub_with_name_and_symbol_calls(sub_name, symbol_call_config);
program.subs.insert(mocked_sub.tid.clone(), mocked_sub);
......@@ -611,21 +555,7 @@ pub fn mock_project_with_intraprocedural_control_flow(
program.extern_symbols.insert(malloc.tid.clone(), malloc);
program.entry_points.insert(Tid::new(sub_name));
let register_set = ["r0", "r1", "r2", "r3", "r11", "sp"]
.iter()
.map(|name| Variable::mock(name, ByteSize::new(4)))
.collect();
let cconv = CallingConvention::mock_standard_arm_32();
Project {
program: Term {
tid: Tid::new("program"),
term: program,
},
cpu_architecture: "arm_32".to_string(),
stack_pointer_register: Variable::mock("sp", 4u64),
calling_conventions: BTreeMap::from_iter([(cconv.name.clone(), cconv)]),
register_set,
datatype_properties: DatatypeProperties::mock_standard_arm_32(),
}
let mut project = Project::mock_arm32();
project.program.term = program;
project
}
......@@ -29,54 +29,18 @@ impl Program {
pub fn find_block(&self, tid: &Tid) -> Option<&Term<Blk>> {
self.subs
.iter()
.map(|(_, sub)| sub.term.blocks.iter())
.flatten()
.flat_map(|(_, sub)| sub.term.blocks.iter())
.find(|block| block.tid == *tid)
}
}
#[cfg(test)]
mod tests {
use crate::intermediate_representation::{Arg, CallingConvention, Datatype};
use crate::intermediate_representation::{CallingConvention, Datatype};
use super::*;
impl Program {
/// Returns extern symbol with argument/return register according to calling convention
fn create_extern_symbol(
name: &str,
cconv: CallingConvention,
arg_type: Option<Datatype>,
return_type: Option<Datatype>,
) -> ExternSymbol {
ExternSymbol {
tid: Tid::new(name),
addresses: vec![],
name: name.to_string(),
calling_convention: Some(cconv.name),
parameters: match arg_type {
Some(data_type) => {
vec![Arg::from_var(
cconv.integer_parameter_register[0].clone(),
Some(data_type),
)]
}
None => vec![],
},
return_values: match return_type {
Some(data_type) => {
vec![Arg::from_var(
cconv.integer_return_register[0].clone(),
Some(data_type),
)]
}
None => vec![],
},
no_return: false,
has_var_args: false,
}
}
fn add_extern_symbols_to_program(a: Vec<(Tid, ExternSymbol)>) -> Program {
Program {
subs: BTreeMap::new(),
......@@ -87,19 +51,19 @@ mod tests {
}
/// Returns Program with malloc, free and other_function
pub fn mock_x64() -> Program {
let malloc = Program::create_extern_symbol(
let malloc = ExternSymbol::create_extern_symbol(
"malloc",
CallingConvention::mock_x64(),
Some(Datatype::Integer),
Some(Datatype::Pointer),
);
let free = Program::create_extern_symbol(
let free = ExternSymbol::create_extern_symbol(
"free",
CallingConvention::mock_x64(),
Some(Datatype::Pointer),
None,
);
let other_function = Program::create_extern_symbol(
let other_function = ExternSymbol::create_extern_symbol(
"other_function",
CallingConvention::mock_x64(),
None,
......@@ -114,19 +78,19 @@ mod tests {
}
/// Returns Program with malloc, free and other_function
pub fn mock_arm32() -> Program {
let malloc = Program::create_extern_symbol(
let malloc = ExternSymbol::create_extern_symbol(
"malloc",
CallingConvention::mock_arm32(),
Some(Datatype::Integer),
Some(Datatype::Pointer),
);
let free = Program::create_extern_symbol(
let free = ExternSymbol::create_extern_symbol(
"free",
CallingConvention::mock_arm32(),
Some(Datatype::Pointer),
None,
);
let other_function = Program::create_extern_symbol(
let other_function = ExternSymbol::create_extern_symbol(
"other_function",
CallingConvention::mock_arm32(),
None,
......
......@@ -301,7 +301,7 @@ mod tests {
Project {
program: Term {
tid: Tid::new("program_tid"),
term: Program::mock_x64(),
term: Program::mock_arm32(),
},
cpu_architecture: "arm32".to_string(),
stack_pointer_register: Variable::mock("sp", 4u64),
......
......@@ -387,5 +387,40 @@ mod tests {
has_var_args: true,
}
}
/// Returns extern symbol with argument/return register according to calling convention
pub fn create_extern_symbol(
name: &str,
cconv: CallingConvention,
arg_type: Option<Datatype>,
return_type: Option<Datatype>,
) -> ExternSymbol {
ExternSymbol {
tid: Tid::new(name),
addresses: vec![],
name: name.to_string(),
calling_convention: Some(cconv.name),
parameters: match arg_type {
Some(data_type) => {
vec![Arg::from_var(
cconv.integer_parameter_register[0].clone(),
Some(data_type),
)]
}
None => vec![],
},
return_values: match return_type {
Some(data_type) => {
vec![Arg::from_var(
cconv.integer_return_register[0].clone(),
Some(data_type),
)]
}
None => vec![],
},
no_return: false,
has_var_args: false,
}
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment