use super::State;
use crate::abstract_domain::*;
use crate::intermediate_representation::*;
impl State {
pub fn load_value(
&mut self,
address: DataDomain<BitvectorDomain>,
size: ByteSize,
global_memory: Option<&RuntimeMemoryImage>,
) -> DataDomain<BitvectorDomain> {
let mut loaded_value = DataDomain::new_empty(size);
for (id, offset) in address.get_relative_values() {
loaded_value = loaded_value.merge(&self.load_value_via_id_and_offset(id, offset, size));
}
if let Some(global_address) = address.get_absolute_value() {
loaded_value =
loaded_value.merge(&self.load_global_address(global_address, size, global_memory));
}
if address.contains_top() {
loaded_value.set_contains_top_flag();
}
loaded_value
}
fn load_value_via_id_and_offset(
&mut self,
id: &AbstractIdentifier,
offset: &BitvectorDomain,
size: ByteSize,
) -> DataDomain<BitvectorDomain> {
if *id == self.stack_id {
match offset.try_to_bitvec() {
Ok(stack_offset) => self.load_value_from_stack(stack_offset, size),
Err(_) => DataDomain::new_top(size),
}
} else if let (true, Ok(constant_offset)) = (
id.get_location().recursion_depth() < self.pointer_recursion_depth_limit,
offset.try_to_offset(),
) {
let new_id = AbstractIdentifier::new(
id.get_tid().clone(),
id.get_location()
.clone()
.dereferenced(size, self.stack_id.bytesize())
.with_offset_addendum(constant_offset),
);
DataDomain::from_target(new_id, Bitvector::zero(size.into()).into())
} else {
DataDomain::new_top(size)
}
}
fn load_global_address(
&mut self,
global_address: &BitvectorDomain,
size: ByteSize,
global_memory: Option<&RuntimeMemoryImage>,
) -> DataDomain<BitvectorDomain> {
if let (Ok(offset), Some(global_mem)) = (global_address.try_to_bitvec(), global_memory) {
match global_mem.read(&offset, size) {
Ok(Some(value)) => value.into(),
Ok(None) => {
let address = global_address.try_to_offset().unwrap() as u64;
let global_mem_location = AbstractLocation::GlobalAddress { address, size };
let global_mem_id = AbstractIdentifier::new(
self.get_current_function_tid().clone(),
global_mem_location,
);
DataDomain::from_target(global_mem_id, Bitvector::zero(size.into()).into())
}
Err(_) => DataDomain::new_top(size),
}
} else {
DataDomain::new_top(size)
}
}
pub fn load_value_from_stack(
&mut self,
stack_offset: Bitvector,
size: ByteSize,
) -> DataDomain<BitvectorDomain> {
if !stack_offset.sign_bit().to_bool() {
self.get_stack_param(stack_offset, size)
} else {
self.stack.get(stack_offset, size)
}
}
pub fn load_unsized_value_from_stack(
&mut self,
offset: Bitvector,
) -> DataDomain<BitvectorDomain> {
if !offset.sign_bit().to_bool() {
self.stack
.get_unsized(offset.clone())
.unwrap_or_else(|| self.get_stack_param(offset, ByteSize::new(1)))
} else {
self.stack
.get_unsized(offset)
.unwrap_or_else(|| DataDomain::new_top(ByteSize::new(1)))
}
}
pub fn write_value(
&mut self,
address: DataDomain<BitvectorDomain>,
value: DataDomain<BitvectorDomain>,
) {
if let Some(stack_offset) = self.get_offset_if_exact_stack_pointer(&address) {
if !stack_offset.sign_bit().to_bool() {
let _ = self
.generate_stack_param_id_if_nonexistent(stack_offset.clone(), value.bytesize());
}
self.stack.add(value, stack_offset);
} else if let Some(stack_offset_domain) = address.get_relative_values().get(&self.stack_id)
{
if let Ok(stack_offset) = stack_offset_domain.try_to_bitvec() {
if !stack_offset.sign_bit().to_bool() {
let _ = self.generate_stack_param_id_if_nonexistent(
stack_offset.clone(),
value.bytesize(),
);
}
let previous_value = self.stack.get(stack_offset.clone(), value.bytesize());
self.stack.add(previous_value.merge(&value), stack_offset);
} else {
self.stack.mark_all_values_as_top();
}
}
}
fn get_stack_param(
&mut self,
address: Bitvector,
size: ByteSize,
) -> DataDomain<BitvectorDomain> {
assert!(!address.sign_bit().to_bool());
if let Some(param_id) = self.generate_stack_param_id_if_nonexistent(address.clone(), size) {
let stack_param =
DataDomain::from_target(param_id, Bitvector::zero(size.into()).into());
self.stack.add(stack_param.clone(), address);
stack_param
} else {
self.stack.get(address, size)
}
}
pub fn get_offset_if_exact_stack_pointer(
&self,
address: &DataDomain<BitvectorDomain>,
) -> Option<Bitvector> {
if let Some((target, offset)) = address.get_if_unique_target() {
if *target == self.stack_id {
return offset.try_to_bitvec().ok();
}
}
None
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use crate::{bitvec, variable};
fn mock_stack_id() -> AbstractIdentifier {
AbstractIdentifier::from_var(Tid::new("mock_fn"), &variable!("sp:4"))
}
fn mock_stack_param_id(offset: i64, size: u64) -> AbstractIdentifier {
AbstractIdentifier::new(
Tid::new("mock_fn"),
AbstractLocation::from_stack_position(
mock_stack_id().unwrap_register(),
offset,
ByteSize::new(size),
),
)
}
#[test]
fn test_get_offset_if_exact_stack_pointer() {
let state = State::mock_arm32();
let stack_pointer =
DataDomain::from_target(mock_stack_id(), Bitvector::from_i32(-10).into());
assert_eq!(
state.get_offset_if_exact_stack_pointer(&stack_pointer),
Some(Bitvector::from_i32(-10))
);
}
#[test]
fn test_get_stack_param() {
let mut state = State::mock_arm32();
let stack_param = state.get_stack_param(bitvec!("0xc:4"), ByteSize::new(8));
let expected_stack_id = AbstractIdentifier::mock_nested("mock_fn", "sp:4", &[12], 8);
let expected_value =
DataDomain::from_target(expected_stack_id.clone(), bitvec!("0x0:8").into());
assert_eq!(&stack_param, &expected_value);
assert!(state.tracked_ids.contains_key(&expected_stack_id));
let stack_param = state.get_stack_param(bitvec!("0xc:4"), ByteSize::new(8));
assert_eq!(&stack_param, &expected_value);
state
.stack
.insert_at_byte_index(bitvec!("0x2a:8").into(), 12);
let value = state.get_stack_param(bitvec!("0xc:4"), ByteSize::new(8));
assert_eq!(value, bitvec!("0x2a:8").into());
}
#[test]
fn test_store_and_load_from_stack() {
let mut state = State::mock_arm32();
let address = DataDomain::from_target(mock_stack_id(), bitvec!("-4:4").into());
let value: DataDomain<BitvectorDomain> = bitvec!("0x0:4").into();
state.write_value(address.clone(), value.clone());
assert_eq!(state.stack.iter().len(), 1);
assert_eq!(
state.stack.get(bitvec!("-4:4"), ByteSize::new(4)),
value.clone()
);
assert_eq!(state.load_value(address, ByteSize::new(4), None), value);
let address = DataDomain::from_target(mock_stack_id(), bitvec!("0x4:4").into());
let stack_param_id = mock_stack_param_id(4, 4);
let stack_param = DataDomain::from_target(stack_param_id.clone(), bitvec!("0x0:4").into());
assert_eq!(state.tracked_ids.iter().len(), 6);
assert_eq!(
state.load_value(address.clone(), ByteSize::new(4), None),
stack_param
);
assert_eq!(state.tracked_ids.iter().len(), 7);
assert_eq!(
state
.tracked_ids
.get(&stack_param_id)
.unwrap()
.is_accessed(),
false
); }
#[test]
fn test_load_unsized_from_stack() {
let mut state = State::mock_arm32();
let address = DataDomain::from_target(mock_stack_id(), bitvec!("0x0:4").into());
let stack_param_id = mock_stack_param_id(0, 4);
let stack_param = DataDomain::from_target(stack_param_id.clone(), bitvec!("0x0:4").into());
state.load_value(address, ByteSize::new(4), None);
let unsized_load = state.load_unsized_value_from_stack(bitvec!("0x0:4").into());
assert_eq!(unsized_load, stack_param);
assert!(state.tracked_ids.get(&stack_param_id).is_some());
let stack_param_id = mock_stack_param_id(4, 1);
let stack_param = DataDomain::from_target(stack_param_id.clone(), bitvec!("0x0:1").into());
let unsized_load = state.load_unsized_value_from_stack(bitvec!("0x4:4"));
assert_eq!(unsized_load, stack_param);
assert!(state.tracked_ids.get(&stack_param_id).is_some());
let unsized_load = state.load_unsized_value_from_stack(bitvec!("-4:4"));
assert_eq!(unsized_load, DataDomain::new_top(ByteSize::new(1)));
}
#[test]
fn test_load_nested_pointers() {
let mut state = State::mock_arm32();
let global_memory = RuntimeMemoryImage::mock();
let parent_id = AbstractIdentifier::mock_nested("mock_fn", "r0:4", &[4], 4);
let pointer = DataDomain::from_target(parent_id.clone(), bitvec!("0x8:4").into());
let loaded_value = state.load_value(pointer, ByteSize::new(4), Some(&global_memory));
let expected_id = AbstractIdentifier::mock_nested("mock_fn", "r0:4", &[4, 8], 4);
let expected_value = DataDomain::from_target(expected_id.clone(), bitvec!("0x0:4").into());
assert_eq!(loaded_value, expected_value);
}
}