Unverified Commit 0662acda by van den Bosch Committed by GitHub

Test reorganization (#285)

parent 19eac24e
......@@ -4,7 +4,7 @@ use std::collections::HashSet;
#[test]
fn test_compute_return_values_of_call() {
let project = Project::mock_empty();
let cconv = CallingConvention::mock();
let cconv = CallingConvention::mock_x64();
let graph = crate::analysis::graph::get_program_cfg(&project.program, HashSet::new());
let context = Context::new(&project, &graph);
......@@ -25,7 +25,7 @@ fn test_compute_return_values_of_call() {
AbstractIdentifier::new_from_var(Tid::new("call_tid"), &Variable::mock("RAX", 8)),
Bitvector::from_i64(0).into(),
);
assert_eq!(return_values.iter().len(), 1);
assert_eq!(return_values.iter().len(), 3);
assert_eq!(return_values[0], (&Variable::mock("RAX", 8), expected_val));
// Test returning a known value.
let param_ref = DataDomain::from_target(
......@@ -39,6 +39,6 @@ fn test_compute_return_values_of_call() {
);
let return_values =
context.compute_return_values_of_call(&mut caller_state, &callee_state, &cconv, &call);
assert_eq!(return_values.iter().len(), 1);
assert_eq!(return_values.iter().len(), 3);
assert_eq!(return_values[0], (&Variable::mock("RAX", 8), expected_val));
}
......@@ -15,7 +15,7 @@ impl State {
State::new(
&Tid::new(tid_name),
&Variable::mock("RSP", 8),
&CallingConvention::mock(),
&CallingConvention::mock_x64(),
)
}
}
......
......@@ -338,7 +338,7 @@ fn merge_callee_stack_to_caller_stack() {
fn remove_and_restore_callee_saved_register() {
let mut state = State::new(&register("RSP"), Tid::new("func_tid"));
let value: Data = Bitvector::from_u64(42).into();
let cconv = CallingConvention::mock();
let cconv = CallingConvention::mock_x64();
state.set_register(&register("RBP"), value.clone());
state.set_register(&register("RAX"), value.clone());
......
......@@ -211,7 +211,7 @@ pub mod tests {
project.program.term.entry_points.insert(Tid::new("func"));
project
.calling_conventions
.insert("__stdcall".to_string(), CallingConvention::mock());
.insert("__stdcall".to_string(), CallingConvention::mock_x64());
project
}
......
......@@ -213,45 +213,108 @@ mod tests {
}
}
}
/// Wrapper for subpiece to model float register for argument passing
fn create_float_register_subpiece(
name: &str,
reg_size: u64,
low_byte: u64,
size: u64,
) -> Expression {
Expression::subpiece(
Expression::Var(Variable::mock(name, reg_size)),
ByteSize::new(low_byte),
ByteSize::new(size),
)
}
impl CallingConvention {
pub fn mock() -> CallingConvention {
/// Creates System V Calling Convention with Advanced Vector Extensions 512
pub fn mock_x64() -> CallingConvention {
CallingConvention {
name: "__stdcall".to_string(), // so that the mock is useable as standard calling convention in tests
integer_parameter_register: vec![Variable::mock("RDI", 8)],
float_parameter_register: vec![Expression::Var(Variable::mock("XMMO", 16))],
integer_return_register: vec![Variable::mock("RAX", 8)],
float_return_register: vec![],
callee_saved_register: vec![Variable::mock("RBP", 8)],
integer_parameter_register: vec![
Variable::mock("RDI", 8),
Variable::mock("RSI", 8),
Variable::mock("RDX", 8),
Variable::mock("RCX", 8),
Variable::mock("R8", 8),
Variable::mock("R9", 8),
],
// ABI: first 8 Bytes of ZMM0-ZMM7 for float parameter
// Ghidra: first 8 Bytes of YMM0-YMM7 for float parameter
float_parameter_register: vec![
create_float_register_subpiece("ZMM0", 64, 0, 8),
create_float_register_subpiece("ZMM1", 64, 0, 8),
create_float_register_subpiece("ZMM2", 64, 0, 8),
create_float_register_subpiece("ZMM3", 64, 0, 8),
create_float_register_subpiece("ZMM4", 64, 0, 8),
create_float_register_subpiece("ZMM5", 64, 0, 8),
create_float_register_subpiece("ZMM6", 64, 0, 8),
create_float_register_subpiece("ZMM7", 64, 0, 8),
],
integer_return_register: vec![Variable::mock("RAX", 8), Variable::mock("RDX", 8)],
// ABI: XMM0-XMM1 float return register
// Ghidra: uses XMM0 only
float_return_register: vec![create_float_register_subpiece("ZMM0", 64, 0, 8)],
callee_saved_register: vec![
Variable::mock("RBP", 8),
Variable::mock("RBX", 8),
Variable::mock("RSP", 8),
Variable::mock("R12", 8),
Variable::mock("R13", 8),
Variable::mock("R14", 8),
Variable::mock("R15", 8),
],
}
}
/// Following ARM32 ABI with MVE Extention
pub fn mock_arm32() -> CallingConvention {
CallingConvention {
name: "__stdcall".to_string(), // so that the mock is useable as standard calling convention in tests
integer_parameter_register: vec![Variable::mock("r0", 4)],
float_parameter_register: vec![Expression::Var(Variable::mock("d0", 8))],
integer_return_register: vec![Variable::mock("r0", 4)],
float_return_register: vec![],
callee_saved_register: vec![Variable::mock("r4", 4)],
}
}
pub fn mock_with_parameter_registers(
integer_parameter_register: Vec<Variable>,
float_parameter_register: Vec<Variable>,
) -> CallingConvention {
let float_parameter_register = float_parameter_register
.into_iter()
.map(Expression::Var)
.collect();
CallingConvention {
name: "__stdcall".to_string(), // so that the mock is useable as standard calling convention in tests
integer_parameter_register,
float_parameter_register,
integer_return_register: vec![Variable::mock("RAX", 8)],
float_return_register: vec![],
callee_saved_register: vec![Variable::mock("RBP", 8)],
integer_parameter_register: vec![
Variable::mock("r0", 4),
Variable::mock("r1", 4),
Variable::mock("r2", 4),
Variable::mock("r3", 4),
],
// ABI: q0-q3 used for argument passing
// Ghidra: uses q0-q1 only
float_parameter_register: vec![
create_float_register_subpiece("q0", 16, 0, 4),
create_float_register_subpiece("q0", 16, 4, 4),
create_float_register_subpiece("q0", 16, 8, 4),
create_float_register_subpiece("q0", 16, 12, 4),
create_float_register_subpiece("q1", 16, 0, 4),
create_float_register_subpiece("q1", 16, 4, 4),
create_float_register_subpiece("q1", 16, 8, 4),
create_float_register_subpiece("q1", 16, 12, 4),
],
// ABI: r0-r1 used as integer return register
// Ghidra uses r0 only
integer_return_register: vec![
Variable::mock("r0", 4),
Variable::mock("r1", 4),
Variable::mock("r2", 4),
Variable::mock("r3", 4),
],
// ABI: whole q0 used as float return
// Ghidra: uses first 8 Bytes of q0 only
float_return_register: vec![create_float_register_subpiece("q0", 16, 0, 4)],
callee_saved_register: vec![
Variable::mock("r4", 4),
Variable::mock("r5", 4),
Variable::mock("r6", 4),
Variable::mock("r7", 4),
Variable::mock("r8", 4),
Variable::mock("r9", 4),
Variable::mock("r10", 4),
Variable::mock("r11", 4),
Variable::mock("r13", 4),
Variable::mock("q4", 16),
Variable::mock("q5", 16),
Variable::mock("q6", 16),
Variable::mock("q7", 16),
],
}
}
}
......@@ -301,7 +364,6 @@ mod tests {
}
pub fn mock_arm32() -> ExternSymbol {
// There is also the mock_standard_arm32() method. Only on of the two should exist!
ExternSymbol {
tid: Tid::new("mock_symbol"),
addresses: vec!["UNKNOWN".to_string()],
......
......@@ -9,36 +9,32 @@ fn mock_pi_state() -> PointerInferenceState {
}
#[test]
/// Tests extraction of format string parameters '/dev/sd%c%d' and 'cat %s'.
fn test_get_variable_parameters() {
let mem_image = RuntimeMemoryImage::mock();
let mut pi_state = mock_pi_state();
let sprintf_symbol = ExternSymbol::mock_string();
let mut format_string_index_map: HashMap<String, usize> = HashMap::new();
format_string_index_map.insert("sprintf".to_string(), 0);
format_string_index_map.insert("sprintf".to_string(), 1);
let global_address = Bitvector::from_str_radix(16, "5000").unwrap();
pi_state.set_register(
&Variable::mock("RDI", 8 as u64),
&Variable::mock("RSI", 8 as u64),
IntervalDomain::new(global_address.clone(), global_address).into(),
);
let mut project = Project::mock_empty();
let cconv = CallingConvention::mock_with_parameter_registers(
vec![Variable::mock("RDI", 8)],
vec![Variable::mock("XMM0", 16)],
);
let cconv = CallingConvention::mock_x64();
project.calling_conventions = BTreeMap::from_iter([(cconv.name.clone(), cconv)]);
let mut output: Vec<Arg> = Vec::new();
output.push(Arg::Stack {
address: Expression::Var(Variable::mock("RSP", 8)).plus_const(8),
size: ByteSize::new(4),
data_type: Some(Datatype::Char),
});
output.push(Arg::from_var(
Variable::mock("RDX", 8),
Some(Datatype::Char),
));
output.push(Arg::from_var(
Variable::mock("RCX", 8),
Some(Datatype::Integer),
));
output.push(Arg::Stack {
address: Expression::Var(Variable::mock("RSP", 8)).plus_const(12),
size: ByteSize::new(4),
data_type: Some(Datatype::Integer),
});
assert_eq!(
output,
get_variable_parameters(
......@@ -51,15 +47,14 @@ fn test_get_variable_parameters() {
.unwrap()
);
output = vec![Arg::Stack {
address: Expression::Var(Variable::mock("RSP", 8)).plus_const(8),
size: ByteSize::new(8),
data_type: Some(Datatype::Pointer),
}];
output = vec![Arg::from_var(
Variable::mock("RDX", 8),
Some(Datatype::Pointer),
)];
let global_address = Bitvector::from_str_radix(16, "500c").unwrap();
pi_state.set_register(
&Variable::mock("RDI", 8 as u64),
&Variable::mock("RSI", 8 as u64),
IntervalDomain::new(global_address.clone(), global_address).into(),
);
......@@ -164,16 +159,9 @@ fn test_parse_format_string_parameters() {
}
#[test]
/// Tests tracking of parameters according to format string
fn test_calculate_parameter_locations() {
let cconv = CallingConvention::mock_with_parameter_registers(
vec![
Variable::mock("RDI", 8),
Variable::mock("RSI", 8),
Variable::mock("R8", 8),
Variable::mock("R9", 8),
],
vec![Variable::mock("XMM0", 16)],
);
let cconv = CallingConvention::mock_x64();
let format_string_index: usize = 1;
let mut parameters: Vec<(Datatype, ByteSize)> = Vec::new();
parameters.push(("d".to_string().into(), ByteSize::new(8)));
......@@ -182,20 +170,24 @@ fn test_calculate_parameter_locations() {
let mut expected_args = vec![
Arg::Register {
expr: Expression::Var(Variable::mock("R8", ByteSize::new(8))),
expr: Expression::Var(Variable::mock("RDX", ByteSize::new(8))),
data_type: Some(Datatype::Integer),
},
Arg::Register {
expr: Expression::Var(Variable::mock("XMM0", ByteSize::new(16))),
expr: Expression::subpiece(
Expression::Var(Variable::mock("ZMM0", 64)),
ByteSize::new(0),
ByteSize::new(8),
),
data_type: Some(Datatype::Double),
},
Arg::Register {
expr: Expression::Var(Variable::mock("R9", ByteSize::new(8))),
expr: Expression::Var(Variable::mock("RCX", ByteSize::new(8))),
data_type: Some(Datatype::Pointer),
},
];
// Test Case 1: The string parameter is still written in the R9 register since 'f' is contained in the float register.
// Test Case 1: The string parameter is still written in the RCX register since 'f' is contained in the float register.
assert_eq!(
expected_args,
calculate_parameter_locations(
......@@ -208,13 +200,24 @@ fn test_calculate_parameter_locations() {
);
parameters.push(("s".to_string().into(), ByteSize::new(8)));
parameters.push(("s".to_string().into(), ByteSize::new(8)));
parameters.push(("s".to_string().into(), ByteSize::new(8)));
expected_args.push(Arg::Register {
expr: Expression::Var(Variable::mock("R8", ByteSize::new(8))),
data_type: Some(Datatype::Pointer),
});
expected_args.push(Arg::Register {
expr: Expression::Var(Variable::mock("R9", ByteSize::new(8))),
data_type: Some(Datatype::Pointer),
});
expected_args.push(Arg::Stack {
address: Expression::Var(Variable::mock("RSP", 8)).plus_const(8),
size: ByteSize::new(8),
data_type: Some(Datatype::Pointer),
});
// Test Case 2: A second string parameter does not fit into the registers anymore and is written into the stack.
// Test Case 2: Three further string parameter does not fit into the registers anymore and one is written into the stack.
assert_eq!(
expected_args,
calculate_parameter_locations(
......
......@@ -407,7 +407,7 @@ pub mod tests {
use super::*;
impl RuntimeMemoryImage {
/// Create a mock runtime memory image for unit tests.
/// Creates a mock runtime memory image with: byte series, strings and format strings.
pub fn mock() -> RuntimeMemoryImage {
RuntimeMemoryImage {
memory_segments: vec![
......
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