Unverified Commit 8fb5f559 by Enkelmann Committed by GitHub

Refactor DataDomain (#209)

parent 9a0ae7a3
FROM rust:1.49 AS builder
FROM rust:1.53 AS builder
WORKDIR /cwe_checker
......
......@@ -44,7 +44,7 @@ If you want to build the docker image yourself, just run `docker build -t cwe_ch
### Local installation ###
The following dependencies must be installed in order to build and install the *cwe_checker* locally:
- [Rust](https://www.rust-lang.org) >= 1.49
- [Rust](https://www.rust-lang.org) >= 1.53
- [Ghidra](https://ghidra-sre.org/) >= 9.2
Run `make all GHIDRA_PATH=/path/to/ghidra_folder` (with the correct path to the local Ghidra installation inserted) to compile and install the cwe_checker.
......
......@@ -137,7 +137,7 @@ fn run_with_ghidra(args: &CmdlineArgs) {
// Generate the representation of the runtime memory image of the binary
let mut runtime_memory_image = if let Some(bare_metal_config) = bare_metal_config_opt.as_ref() {
RuntimeMemoryImage::new_from_bare_metal(&binary, &bare_metal_config).unwrap_or_else(|err| {
RuntimeMemoryImage::new_from_bare_metal(&binary, bare_metal_config).unwrap_or_else(|err| {
panic!("Error while generating runtime memory image: {}", err);
})
} else {
......
use super::*;
/// Compute the intersection of relative targets for two `DataDomain` instances.
fn intersect_relative_values<T: SpecializeByConditional + RegisterDomain>(
values_left: &BTreeMap<AbstractIdentifier, T>,
values_right: &BTreeMap<AbstractIdentifier, T>,
) -> BTreeMap<AbstractIdentifier, T> {
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()
})
.collect()
}
impl<T: SpecializeByConditional + RegisterDomain> SpecializeByConditional for DataDomain<T> {
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();
if self.is_empty() {
Err(anyhow!("Empty value"))
} else {
Ok(self)
}
}
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();
if self.is_empty() {
Err(anyhow!("Empty value"))
} else {
Ok(self)
}
}
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();
if self.is_empty() {
Err(anyhow!("Empty value"))
} else {
Ok(self)
}
}
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();
if self.is_empty() {
Err(anyhow!("Empty value"))
} else {
Ok(self)
}
}
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();
if self.is_empty() {
Err(anyhow!("Empty value"))
} else {
Ok(self)
}
}
fn intersect(self, other: &Self) -> Result<Self, Error> {
let result = match (self.contains_top_values, other.contains_top_values) {
// If only one input value contains top elements, then the other input is the best approximation for the intersection.
(true, false) => other.clone(),
(false, true) => self,
// Else we can compute the intersection field-wise.
(true, true) | (false, false) => {
let relative_values =
intersect_relative_values(&self.relative_values, &other.relative_values);
let absolute_value = if let (Some(value), Some(other_value)) =
(&self.absolute_value, &other.absolute_value)
{
value.clone().intersect(other_value).ok()
} else {
None
};
DataDomain {
size: self.bytesize(),
relative_values,
absolute_value,
contains_top_values: self.contains_top_values && other.contains_top_values,
}
}
};
if result.is_empty() {
Err(anyhow!("Domain is empty."))
} else {
Ok(result)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::abstract_domain::*;
fn new_id(name: &str) -> AbstractIdentifier {
AbstractIdentifier::new(
Tid::new("time0"),
AbstractLocation::Register(name.into(), ByteSize::new(8)),
)
}
#[test]
fn intersect() {
let mut targets = BTreeMap::new();
targets.insert(new_id("Rax"), IntervalDomain::mock(1, 1));
targets.insert(new_id("Rbx"), IntervalDomain::mock(1, 10));
let mut data_left = DataDomain::mock_from_target_map(targets);
data_left.set_absolute_value(Some(IntervalDomain::mock(1, 10)));
let mut targets = BTreeMap::new();
targets.insert(new_id("Rax"), IntervalDomain::mock(3, 3));
targets.insert(new_id("Rbx"), IntervalDomain::mock(5, 15));
targets.insert(new_id("Rcx"), IntervalDomain::mock(1, 1));
let mut data_right = DataDomain::mock_from_target_map(targets);
data_right.set_absolute_value(Some(IntervalDomain::mock(10, 20)));
// Element-wise intersection
let intersection = data_left.intersect(&data_right).unwrap();
assert_eq!(intersection.relative_values.len(), 1);
assert_eq!(
*intersection.relative_values.get(&new_id("Rbx")).unwrap(),
IntervalDomain::mock(5, 10)
);
assert_eq!(
intersection.absolute_value,
Some(IntervalDomain::mock(10, 10))
);
assert_eq!(intersection.contains_top_values, false);
// Intersection where exactly one side contains top elements
let mut data_with_top = DataDomain::new_top(ByteSize::new(8));
data_with_top.set_absolute_value(Some(IntervalDomain::mock(15, 100)));
let intersection = data_right.clone().intersect(&data_with_top).unwrap();
assert_eq!(intersection, data_right);
// Empty intersection
let data_absolute_val = IntervalDomain::mock(100, 100).into();
assert!(data_right.intersect(&data_absolute_val).is_err());
}
}
use super::*;
impl<T: RegisterDomain> SizedDomain for DataDomain<T> {
/// Return the bytesize of `self`.
fn bytesize(&self) -> ByteSize {
self.size
}
/// Return a new *Top* element with the given bytesize.
///
/// Note that `DataDomain` technically does not have a `Top` element with respect to the partial order.
/// Instead a `Top` element here represents a non-empty value
/// for which nothing is known about the contained values.
fn new_top(bytesize: ByteSize) -> Self {
DataDomain {
size: bytesize,
relative_values: BTreeMap::new(),
absolute_value: None,
contains_top_values: true,
}
}
}
impl<T: RegisterDomain> HasTop for DataDomain<T> {
/// Generate a new *Top* element with the same bytesize as `self`.
fn top(&self) -> Self {
DataDomain::new_top(self.bytesize())
}
}
impl<T: RegisterDomain> AbstractDomain for DataDomain<T> {
// Merge `self` with `other`.
fn merge(&self, other: &Self) -> Self {
let mut relative_values = self.relative_values.clone();
for (id, offset_other) in other.relative_values.iter() {
relative_values
.entry(id.clone())
.and_modify(|offset| *offset = offset.merge(offset_other))
.or_insert_with(|| offset_other.clone());
}
let absolute_value = match (&self.absolute_value, &other.absolute_value) {
(Some(left), Some(right)) => Some(left.merge(right)),
(Some(val), None) | (None, Some(val)) => Some(val.clone()),
(None, None) => None,
};
DataDomain {
size: self.bytesize(),
relative_values,
absolute_value,
contains_top_values: self.contains_top_values || other.contains_top_values,
}
}
/// Return whether the element represents a top element or not.
///
/// Note that `DataDomain` technically does not have a `Top` element with respect to the partial order.
/// Instead a `Top` element here represents a non-empty value
/// for which nothing is known about the contained values.
fn is_top(&self) -> bool {
self.relative_values.is_empty() && self.absolute_value.is_none() && self.contains_top_values
}
}
impl<T: RegisterDomain> From<T> for DataDomain<T> {
fn from(value: T) -> Self {
Self {
size: value.bytesize(),
relative_values: BTreeMap::new(),
absolute_value: Some(value),
contains_top_values: false,
}
}
}
impl<T: RegisterDomain + From<Bitvector>> From<Bitvector> for DataDomain<T> {
fn from(bitvector: Bitvector) -> Self {
let val: T = bitvector.into();
val.into()
}
}
impl<T: RegisterDomain + TryToBitvec> TryToBitvec for DataDomain<T> {
/// If the domain represents a single, absolute value, return it.
fn try_to_bitvec(&self) -> Result<Bitvector, Error> {
if !self.relative_values.is_empty() || self.contains_top_values {
Err(anyhow!("May contain non-absolute values."))
} else if let Some(val) = &self.absolute_value {
val.try_to_bitvec()
} else {
Err(anyhow!("Domain is empty."))
}
}
}
impl<T: RegisterDomain + TryToInterval> TryToInterval for DataDomain<T> {
/// If the domain represents (or can be widened to) an interval of absolute values, return the interval.
fn try_to_interval(&self) -> Result<Interval, Error> {
if !self.relative_values.is_empty() || self.contains_top_values {
Err(anyhow!("May contain non-absolute values."))
} else if let Some(val) = &self.absolute_value {
val.try_to_interval()
} else {
Err(anyhow!("Domain is empty."))
}
}
}
#[cfg(test)]
mod tests {
use super::super::*;
use super::*;
use crate::abstract_domain::*;
type Data = DataDomain<BitvectorDomain>;
fn bv(value: i64) -> BitvectorDomain {
BitvectorDomain::Value(Bitvector::from_i64(value))
}
fn new_id(name: &str) -> AbstractIdentifier {
AbstractIdentifier::new(
Tid::new("time0"),
AbstractLocation::Register(name.into(), ByteSize::new(8)),
)
}
fn new_pointer(location: &str, offset: i64) -> Data {
DataDomain {
size: ByteSize::new(8),
relative_values: BTreeMap::from_iter([(new_id(location), bv(offset))]),
absolute_value: None,
contains_top_values: false,
}
}
fn new_value(value: i64) -> Data {
Data::from(bv(value))
}
#[test]
fn data_merge() {
let pointer = new_pointer("RAX".into(), 0);
let value = new_value(42);
let merged_data = pointer.merge(&value);
assert_eq!(pointer.merge(&pointer), pointer);
assert_eq!(merged_data.relative_values, pointer.relative_values);
assert_eq!(merged_data.absolute_value, value.absolute_value);
let other_value = new_value(-1);
let merged_data = value.merge(&other_value);
assert!(merged_data.relative_values.is_empty());
assert_eq!(
merged_data.absolute_value,
Some(BitvectorDomain::new_top(ByteSize::new(8)))
);
let other_pointer = new_pointer("RBX".into(), 10);
let merged_data = pointer.merge(&other_pointer);
assert_eq!(
merged_data.relative_values.get(&new_id("RAX")),
Some(&bv(0))
);
assert_eq!(
merged_data.relative_values.get(&new_id("RBX")),
Some(&bv(10))
);
}
}
......@@ -251,29 +251,6 @@ impl IntervalDomain {
}
}
/// Compute the intersection of two intervals.
/// Return an error if the intersection is empty.
pub fn intersect(&self, other: &Self) -> Result<Self, Error> {
let mut intersected_domain: IntervalDomain =
self.interval.signed_intersect(&other.interval)?.into();
intersected_domain.update_widening_lower_bound(&self.widening_lower_bound);
intersected_domain.update_widening_lower_bound(&other.widening_lower_bound);
intersected_domain.update_widening_upper_bound(&self.widening_upper_bound);
intersected_domain.update_widening_upper_bound(&other.widening_upper_bound);
intersected_domain.widening_delay =
std::cmp::max(self.widening_delay, other.widening_delay);
if let Ok(interval_length) = (intersected_domain.interval.end.clone()
- &intersected_domain.interval.start)
.try_to_u64()
{
intersected_domain.widening_delay =
std::cmp::min(intersected_domain.widening_delay, interval_length);
}
Ok(intersected_domain)
}
/// Check whether all values in the interval are representable by bitvectors of the given `size`.
/// Does not check whether this is also true for the widening hints.
pub fn fits_into_size(&self, size: ByteSize) -> bool {
......@@ -503,6 +480,29 @@ impl SpecializeByConditional for IntervalDomain {
Ok(self)
}
}
/// Compute the intersection of two intervals.
/// Return an error if the intersection is empty.
fn intersect(self, other: &Self) -> Result<Self, Error> {
let mut intersected_domain: IntervalDomain =
self.interval.signed_intersect(&other.interval)?.into();
intersected_domain.update_widening_lower_bound(&self.widening_lower_bound);
intersected_domain.update_widening_lower_bound(&other.widening_lower_bound);
intersected_domain.update_widening_upper_bound(&self.widening_upper_bound);
intersected_domain.update_widening_upper_bound(&other.widening_upper_bound);
intersected_domain.widening_delay =
std::cmp::max(self.widening_delay, other.widening_delay);
if let Ok(interval_length) = (intersected_domain.interval.end.clone()
- &intersected_domain.interval.start)
.try_to_u64()
{
intersected_domain.widening_delay =
std::cmp::min(intersected_domain.widening_delay, interval_length);
}
Ok(intersected_domain)
}
}
impl AbstractDomain for IntervalDomain {
......
......@@ -591,7 +591,7 @@ fn add_not_equal_bounds() {
fn intersection() {
let interval1 = IntervalDomain::mock_with_bounds(Some(-100), -10, 10, Some(100));
let interval2 = IntervalDomain::mock_with_bounds(Some(-20), 2, 30, None);
let intersection = interval1.intersect(&interval2).unwrap();
let intersection = interval1.clone().intersect(&interval2).unwrap();
assert_eq!(
intersection,
IntervalDomain::mock_with_bounds(Some(-20), 2, 10, Some(100))
......
......@@ -185,7 +185,7 @@ impl<T: AbstractDomain + SizedDomain + HasTop + std::fmt::Debug> MemRegionData<T
for (pos_left, elem_left) in self.values.iter() {
if let Some((_pos_right, elem_right)) = other.values.get_key_value(pos_left) {
if elem_left.bytesize() == elem_right.bytesize() {
let merged_val = elem_left.merge(&elem_right);
let merged_val = elem_left.merge(elem_right);
if !merged_val.is_top() {
// we discard top()-values, as they don't contain information
merged_values.insert(*pos_left, merged_val);
......
......@@ -10,9 +10,6 @@ pub use bitvector::*;
mod identifier;
pub use identifier::*;
mod pointer;
pub use pointer::*;
mod data;
pub use data::*;
......@@ -153,4 +150,7 @@ pub trait SpecializeByConditional: Sized {
/// Return the restriction of `self` to values satisfying `self != bound`
/// Returns an error if `self` only represents one value for which `self == bound` holds.
fn add_not_equal_bound(self, bound: &Bitvector) -> Result<Self, Error>;
/// Return the intersection of two values or an error if the intersection is empty.
fn intersect(self, other: &Self) -> Result<Self, Error>;
}
use super::{AbstractDomain, AbstractIdentifier, RegisterDomain, SizedDomain};
use crate::intermediate_representation::{BinOpType, ByteSize};
use crate::prelude::*;
use std::collections::BTreeMap;
use std::fmt::Display;
/// An abstract value representing a pointer given as a map from an abstract identifier
/// to the offset in the pointed to object. The offset itself is also a member of an abstract domain.
///
/// If the map contains more than one key,
/// it indicates that the pointer may point to any of the contained objects.
///
/// A `PointerDomain` value always has at least one target.
/// Trying to create a pointer without targets should always lead to panics.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct PointerDomain<T: RegisterDomain>(BTreeMap<AbstractIdentifier, T>);
impl<T: RegisterDomain> AbstractDomain for PointerDomain<T> {
/// Merge two pointers.
///
/// The merged pointer contains all targets of `self` and `other`.
/// For targets, that are contained in both, the offsets are merged.
fn merge(&self, other: &Self) -> Self {
let mut merged_map = self.0.clone();
for (location, offset) in other.0.iter() {
if merged_map.contains_key(location) {
merged_map.insert(location.clone(), merged_map[location].merge(offset));
} else {
merged_map.insert(location.clone(), offset.clone());
}
}
PointerDomain(merged_map)
}
/// Returns false, as PointerDomain has no *Top* element.
fn is_top(&self) -> bool {
false
}
}
impl<T: RegisterDomain> SizedDomain for PointerDomain<T> {
/// Return the bitsize of the pointer.
/// Should always equal the pointer size of the CPU architecture.
fn bytesize(&self) -> ByteSize {
self.0
.values()
.next()
.expect("Pointer without targets encountered")
.bytesize()
}
/// PointerDomain has no explicit `Top` element, thus calling this function will panic.
fn new_top(_bytesize: ByteSize) -> Self {
panic!()
}
}
impl<T: RegisterDomain> PointerDomain<T> {
/// Create a new pointer with exactly one target.
pub fn new(target: AbstractIdentifier, offset: T) -> PointerDomain<T> {
let mut map = BTreeMap::new();
map.insert(target, offset);
PointerDomain(map)
}
/// Create a new pointer with a set of targets. Panics if no targets are provided.
pub fn with_targets(targets: BTreeMap<AbstractIdentifier, T>) -> PointerDomain<T> {
assert!(!targets.is_empty());
PointerDomain(targets)
}
/// Add a new target to the pointer.
/// If the pointer already contains a target with the same abstract identifier, the offsets of both targets get merged.
pub fn add_target(&mut self, target: AbstractIdentifier, offset: T) {
if let Some(old_offset) = self.0.get(&target) {
let merged_offset = old_offset.merge(&offset);
self.0.insert(target, merged_offset);
} else {
self.0.insert(target, offset);
}
}
/// Replace an abstract identifier with another one and add the offset_adjustment to the pointer offset.
/// This is needed to adjust stack pointer on call and return instructions.
pub fn replace_abstract_id(
&mut self,
old_id: &AbstractIdentifier,
new_id: &AbstractIdentifier,
offset_adjustment: &T,
) {
if let Some(old_offset) = self.0.get(&old_id) {
let new_offset = old_offset.bin_op(BinOpType::IntAdd, offset_adjustment);
self.0.remove(old_id);
self.0.insert(new_id.clone(), new_offset);
}
}
/// add a value to the offset
pub fn add_to_offset(&self, value: &T) -> PointerDomain<T> {
let mut result = self.clone();
for offset in result.0.values_mut() {
*offset = offset.bin_op(BinOpType::IntAdd, value);
}
result
}
/// subtract a value from the offset
pub fn sub_from_offset(&self, value: &T) -> PointerDomain<T> {
let mut result = self.clone();
for offset in result.0.values_mut() {
*offset = offset.bin_op(BinOpType::IntSub, value);
}
result
}
/// Get all possible abstract targets (together with the offset in the target) the pointer may point to.
pub fn targets(&self) -> &BTreeMap<AbstractIdentifier, T> {
&self.0
}
/// Get an iterator over all abstract IDs that the pointer may target.
pub fn ids(&self) -> std::collections::btree_map::Keys<AbstractIdentifier, T> {
self.0.keys()
}
/// Return the target and offset of the pointer if it points to an unique ID.
pub fn unwrap_if_unique_target(&self) -> Option<(&AbstractIdentifier, &T)> {
if self.0.len() == 1 {
return self.0.iter().next();
} else {
None
}
}
}
impl<T: RegisterDomain + Display> PointerDomain<T> {
/// Get a more compact json-representation of the pointer.
/// Intended for pretty printing, not useable for serialization/deserialization.
pub fn to_json_compact(&self) -> serde_json::Value {
serde_json::Value::Object(
self.0
.iter()
.map(|(id, offset)| {
(
format!("{}", id),
serde_json::Value::String(format!("{}", offset)),
)
})
.collect(),
)
}
}
#[cfg(test)]
mod tests {
use super::super::{AbstractLocation, BitvectorDomain};
use super::*;
fn bv(value: i64) -> BitvectorDomain {
BitvectorDomain::Value(Bitvector::from_i64(value))
}
fn new_id(name: &str) -> AbstractIdentifier {
AbstractIdentifier::new(
Tid::new("time0"),
AbstractLocation::Register(name.into(), ByteSize::new(8)),
)
}
fn new_pointer_domain(location: &str, offset: i64) -> PointerDomain<BitvectorDomain> {
let id = new_id(location);
PointerDomain::new(id, bv(offset))
}
#[test]
fn pointer_domain() {
let pointer = new_pointer_domain("Rax".into(), 0);
let offset = bv(3);
let pointer_plus = new_pointer_domain("Rax".into(), 3);
let pointer_minus = new_pointer_domain("Rax".into(), -3);
assert_eq!(pointer.add_to_offset(&offset), pointer_plus);
assert_eq!(pointer.sub_from_offset(&offset), pointer_minus);
let other_pointer = new_pointer_domain("Rbx".into(), 5);
let merged = pointer.merge(&other_pointer);
assert_eq!(merged.0.len(), 2);
assert_eq!(merged.0.get(&new_id("Rax".into())), Some(&bv(0)));
assert_eq!(merged.0.get(&new_id("Rbx".into())), Some(&bv(5)));
}
#[test]
fn replace_abstract_id() {
let mut targets = BTreeMap::new();
targets.insert(new_id("Rax"), bv(5));
targets.insert(new_id("Rbx"), bv(7));
let mut pointer = PointerDomain::with_targets(targets);
pointer.replace_abstract_id(&new_id("Rax"), &new_id("replacement"), &bv(5));
let mut new_targets = BTreeMap::new();
new_targets.insert(new_id("replacement"), bv(10));
new_targets.insert(new_id("Rbx"), bv(7));
assert_eq!(pointer.0, new_targets);
}
}
......@@ -132,7 +132,7 @@ impl<T: Context> Computation<T> {
/// Get the value of a node.
pub fn get_node_value(&self, node: NodeIndex) -> Option<&T::NodeValue> {
if let Some(ref value) = self.node_values.get(&node) {
if let Some(value) = self.node_values.get(&node) {
Some(value)
} else {
self.default_value.as_ref()
......
......@@ -334,7 +334,7 @@ impl<'a> GraphBuilder<'a> {
}
} else {
let mut call_source_node: Option<NodeIndex> = None;
if let Some((target_node, _)) = self.call_targets.get(&target) {
if let Some((target_node, _)) = self.call_targets.get(target) {
let (target_block, target_sub) = match self.graph[*target_node] {
Node::BlkStart(target_block, target_sub) => (target_block, target_sub),
_ => panic!(),
......
......@@ -142,7 +142,7 @@ fn context_problem_implementation() {
// test update_def
state = context.update_def(&state, &def).unwrap();
let stack_pointer = Data::Pointer(PointerDomain::new(new_id("main", "RSP"), bv(-16)));
let stack_pointer = Data::from_target(new_id("main", "RSP"), bv(-16));
assert_eq!(state.eval(&Var(register("RSP"))), stack_pointer);
state = context.update_def(&state, &store_term).unwrap();
......@@ -175,8 +175,8 @@ fn context_problem_implementation() {
callee_state
.memory
.set_value(
PointerDomain::new(new_id("func", "RSP"), bv(-30)),
Data::Value(bv(33).into()),
Data::from_target(new_id("func", "RSP"), bv(-30)),
bv(33).into(),
)
.unwrap();
// Emulate removing the return pointer from the stack for x64
......@@ -213,28 +213,25 @@ fn context_problem_implementation() {
.bin_op(BinOpType::IntAdd, &Bitvector::from_i64(8).into())
);
state.set_register(&register("callee_saved_reg"), Data::Value(bv(13)));
state.set_register(&register("other_reg"), Data::Value(bv(14)));
state.set_register(&register("callee_saved_reg"), bv(13).into());
state.set_register(&register("other_reg"), bv(14).into());
let malloc = call_term("extern_malloc");
let mut state_after_malloc = context.update_call_stub(&state, &malloc).unwrap();
assert_eq!(
state_after_malloc.get_register(&register("RDX")),
Data::Pointer(PointerDomain::new(
new_id("call_extern_malloc", "RDX"),
bv(0)
))
Data::from_target(new_id("call_extern_malloc", "RDX"), bv(0))
);
assert_eq!(state_after_malloc.memory.get_num_objects(), 2);
assert_eq!(
state_after_malloc.get_register(&register("RSP")),
state
.get_register(&register("RSP"))
.bin_op(BinOpType::IntAdd, &Data::Value(bv(8)))
.bin_op(BinOpType::IntAdd, &bv(8).into())
);
assert_eq!(
state_after_malloc.get_register(&register("callee_saved_reg")),
Data::Value(bv(13))
bv(13).into()
);
assert!(state_after_malloc
.get_register(&register("other_reg"))
......@@ -242,10 +239,7 @@ fn context_problem_implementation() {
state_after_malloc.set_register(
&register("callee_saved_reg"),
Data::Pointer(PointerDomain::new(
new_id("call_extern_malloc", "RDX"),
bv(0),
)),
Data::from_target(new_id("call_extern_malloc", "RDX"), bv(0)),
);
let free = call_term("extern_free");
let state_after_free = context
......@@ -255,10 +249,7 @@ fn context_problem_implementation() {
assert_eq!(state_after_free.memory.get_num_objects(), 2);
assert_eq!(
state_after_free.get_register(&register("callee_saved_reg")),
Data::Pointer(PointerDomain::new(
new_id("call_extern_malloc", "RDX"),
bv(0)
))
Data::from_target(new_id("call_extern_malloc", "RDX"), bv(0))
);
let other_extern_fn = call_term("extern_other");
......@@ -268,11 +259,11 @@ fn context_problem_implementation() {
state_after_other_fn.get_register(&register("RSP")),
state
.get_register(&register("RSP"))
.bin_op(BinOpType::IntAdd, &Data::Value(bv(8)))
.bin_op(BinOpType::IntAdd, &bv(8).into())
);
assert_eq!(
state_after_other_fn.get_register(&register("callee_saved_reg")),
Data::Value(bv(13))
bv(13).into()
);
assert!(state_after_other_fn
.get_register(&register("other_reg"))
......@@ -326,10 +317,7 @@ fn update_return() {
.insert(other_callsite_id.clone());
state_before_return.set_register(
&register("RDX"),
Data::Pointer(PointerDomain::new(
new_id("call_callee_other", "RSP"),
bv(-32),
)),
Data::from_target(new_id("call_callee_other", "RSP"), bv(-32)),
);
let state_before_call = State::new(&register("RSP"), Tid::new("original_caller_id"));
......@@ -379,10 +367,7 @@ fn update_return() {
.get_all_object_ids()
.get(&new_id("caller_caller", "RSP"))
.is_some());
let expected_rsp = Data::Pointer(PointerDomain::new(
new_id("original_caller_id", "RSP"),
bv(-8),
));
let expected_rsp = Data::from_target(new_id("original_caller_id", "RSP"), bv(-8));
assert_eq!(state.get_register(&register("RSP")), expected_rsp);
}
......
......@@ -5,7 +5,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
/// Get the underlying graph on which the analysis operates.
fn get_graph(&self) -> &Graph<'a> {
&self.graph
self.graph
}
/// Merge two state values.
......@@ -77,7 +77,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
}
Def::Load { var, address } => {
self.log_debug(
new_state.handle_load(var, address, &self.runtime_memory_image),
new_state.handle_load(var, address, self.runtime_memory_image),
Some(&def.tid),
);
Some(new_state)
......@@ -158,11 +158,10 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
// At the beginning of a function this is the only known pointer to the new stack frame.
callee_state.set_register(
&self.project.stack_pointer_register,
PointerDomain::new(
Data::from_target(
callee_stack_id.clone(),
Bitvector::zero(apint::BitWidth::from(address_bytesize)).into(),
)
.into(),
),
);
// set the list of caller stack ids to only this caller id
callee_state.caller_stack_ids = BTreeSet::new();
......@@ -202,7 +201,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
(Some(state_call), None) => {
if self.is_indirect_call_with_top_target(state_call, call_term) {
// We know nothing about the call target.
return self.handle_call_to_generic_unknown_function(&state_call);
return self.handle_call_to_generic_unknown_function(state_call);
} else {
// We know at least something about the call target.
// Since we don't have a return value,
......@@ -294,7 +293,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
Jmp::CallInd { .. } => {
if self.is_indirect_call_with_top_target(state, call) {
// We know nothing about the call target.
return self.handle_call_to_generic_unknown_function(&state);
return self.handle_call_to_generic_unknown_function(state);
} else {
return None;
}
......@@ -318,7 +317,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
);
}
// Clear non-callee-saved registers from the state.
let cconv = extern_symbol.get_calling_convention(&self.project);
let cconv = extern_symbol.get_calling_convention(self.project);
new_state.clear_non_callee_saved_register(&cconv.callee_saved_register[..]);
// Adjust stack register value (for x86 architecture).
self.adjust_stack_register_on_extern_call(state, &mut new_state);
......
......@@ -360,7 +360,7 @@ impl<'a> PointerInference<'a> {
for jmp in block.term.jmps.iter() {
match &jmp.term {
Jmp::BranchInd(target_expr) => {
let address = state.eval(&target_expr);
let address = state.eval(target_expr);
println!(
"{}: Indirect jump to {}",
jmp.tid,
......@@ -368,7 +368,7 @@ impl<'a> PointerInference<'a> {
);
}
Jmp::CallInd { target, return_ } => {
let address = state.eval(&target);
let address = state.eval(target);
println!(
"{}: Indirect call to {}. HasReturn: {}",
jmp.tid,
......
......@@ -131,9 +131,7 @@ impl AbstractObjectInfo {
/// If the abstract object is not unique (i.e. may represent more than one actual object),
/// merge the old value at the given offset with the new value.
pub fn set_value(&mut self, value: Data, offset: &ValueDomain) -> Result<(), Error> {
if let Data::Pointer(ref pointer) = value {
self.pointer_targets.extend(pointer.ids().cloned());
};
self.pointer_targets.extend(value.referenced_ids().cloned());
if let Ok(concrete_offset) = offset.try_to_bitvec() {
if self.is_unique {
self.memory.add(value, concrete_offset);
......@@ -155,9 +153,7 @@ impl AbstractObjectInfo {
/// Merge `value` at position `offset` with the value currently saved at that position.
pub fn merge_value(&mut self, value: Data, offset: &ValueDomain) {
if let Data::Pointer(ref pointer) = value {
self.pointer_targets.extend(pointer.ids().cloned());
};
self.pointer_targets.extend(value.referenced_ids().cloned());
if let Ok(concrete_offset) = offset.try_to_bitvec() {
let merged_value = self
.memory
......@@ -184,7 +180,7 @@ impl AbstractObjectInfo {
pub fn get_referenced_ids_underapproximation(&self) -> BTreeSet<AbstractIdentifier> {
let mut referenced_ids = BTreeSet::new();
for data in self.memory.values() {
referenced_ids.append(&mut data.referenced_ids())
referenced_ids.extend(data.referenced_ids().cloned())
}
referenced_ids
}
......@@ -201,8 +197,8 @@ impl AbstractObjectInfo {
elem.replace_abstract_id(old_id, new_id, offset_adjustment);
}
self.memory.clear_top_values();
if self.pointer_targets.get(&old_id).is_some() {
self.pointer_targets.remove(&old_id);
if self.pointer_targets.get(old_id).is_some() {
self.pointer_targets.remove(old_id);
self.pointer_targets.insert(new_id.clone());
}
}
......@@ -218,6 +214,8 @@ impl AbstractObjectInfo {
/// Remove the provided IDs from the target lists of all pointers in the memory object.
/// Also remove them from the pointer_targets list.
///
/// If this operation would produce an empty value, it replaces it with a `Top` value instead.
pub fn remove_ids(&mut self, ids_to_remove: &BTreeSet<AbstractIdentifier>) {
self.pointer_targets = self
.pointer_targets
......@@ -226,6 +224,9 @@ impl AbstractObjectInfo {
.collect();
for value in self.memory.values_mut() {
value.remove_ids(ids_to_remove);
if value.is_empty() {
*value = value.top();
}
}
self.memory.clear_top_values(); // In case the previous operation left *Top* values in the memory struct.
}
......@@ -425,7 +426,7 @@ mod tests {
}
fn new_data(number: i64) -> Data {
Data::Value(bv(number))
bv(number).into()
}
fn bv(number: i64) -> ValueDomain {
......@@ -447,7 +448,7 @@ mod tests {
object.set_value(three, &offset).unwrap();
assert_eq!(
object.get_value(Bitvector::from_i64(-16), ByteSize::new(8)),
Data::Top(ByteSize::new(8))
Data::new_top(ByteSize::new(8))
);
assert_eq!(
object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)),
......@@ -456,12 +457,12 @@ mod tests {
object.set_value(new_data(4), &bv(-12)).unwrap();
assert_eq!(
object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)),
Data::Top(ByteSize::new(8))
Data::new_top(ByteSize::new(8))
);
object.merge_value(new_data(23), &bv(-12));
assert_eq!(
object.get_value(Bitvector::from_i64(-12), ByteSize::new(8)),
Data::Value(IntervalDomain::mock(4, 23).with_stride(19))
IntervalDomain::mock(4, 23).with_stride(19).into()
);
let mut other_object = new_abstract_object();
......@@ -470,7 +471,7 @@ mod tests {
let merged_object = object.merge(&other_object);
assert_eq!(
merged_object.get_value(Bitvector::from_i64(-12), ByteSize::new(8)),
Data::Top(ByteSize::new(8))
Data::new_top(ByteSize::new(8))
);
assert_eq!(
merged_object.get_value(Bitvector::from_i64(0), ByteSize::new(8)),
......@@ -486,8 +487,8 @@ mod tests {
target_map.insert(new_id("time_1", "RAX"), bv(20));
target_map.insert(new_id("time_234", "RAX"), bv(30));
target_map.insert(new_id("time_1", "RBX"), bv(40));
let pointer = PointerDomain::with_targets(target_map.clone());
object.set_value(pointer.into(), &bv(-15)).unwrap();
let pointer = DataDomain::mock_from_target_map(target_map.clone());
object.set_value(pointer, &bv(-15)).unwrap();
assert_eq!(object.get_referenced_ids_overapproximation().len(), 3);
object.replace_abstract_id(
......@@ -496,10 +497,10 @@ mod tests {
&bv(10),
);
target_map.remove(&new_id("time_1", "RAX"));
let modified_pointer = PointerDomain::with_targets(target_map);
let modified_pointer = DataDomain::mock_from_target_map(target_map);
assert_eq!(
object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)),
modified_pointer.into()
modified_pointer
);
object.replace_abstract_id(
......@@ -510,10 +511,10 @@ mod tests {
let mut target_map = BTreeMap::new();
target_map.insert(new_id("time_234", "RAX"), bv(30));
target_map.insert(new_id("time_234", "RBX"), bv(50));
let modified_pointer = PointerDomain::with_targets(target_map);
let modified_pointer = DataDomain::mock_from_target_map(target_map);
assert_eq!(
object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)),
modified_pointer.into()
modified_pointer
);
}
......@@ -525,8 +526,8 @@ mod tests {
target_map.insert(new_id("time_1", "RAX"), bv(20));
target_map.insert(new_id("time_234", "RAX"), bv(30));
target_map.insert(new_id("time_1", "RBX"), bv(40));
let pointer = PointerDomain::with_targets(target_map.clone());
object.set_value(pointer.into(), &bv(-15)).unwrap();
let pointer = DataDomain::mock_from_target_map(target_map.clone());
object.set_value(pointer, &bv(-15)).unwrap();
assert_eq!(object.get_referenced_ids_overapproximation().len(), 3);
let ids_to_remove = vec![new_id("time_1", "RAX"), new_id("time_23", "RBX")]
......
......@@ -48,11 +48,10 @@ impl State {
let mut register: BTreeMap<Variable, Data> = BTreeMap::new();
register.insert(
stack_register.clone(),
PointerDomain::new(
Data::from_target(
stack_id.clone(),
Bitvector::zero(apint::BitWidth::from(stack_register.size)).into(),
)
.into(),
),
);
State {
register,
......@@ -153,7 +152,7 @@ impl State {
// get all referenced IDs
let mut referenced_ids = BTreeSet::new();
for (_reg_name, data) in self.register.iter() {
referenced_ids.append(&mut data.referenced_ids());
referenced_ids.extend(data.referenced_ids().cloned());
}
referenced_ids.insert(self.stack_id.clone());
referenced_ids.append(&mut self.caller_stack_ids.clone());
......@@ -239,7 +238,7 @@ impl State {
/// an error with the list of possibly already freed objects is returned.
pub fn mark_mem_object_as_freed(
&mut self,
object_pointer: &PointerDomain<ValueDomain>,
object_pointer: &Data,
) -> Result<(), Vec<(AbstractIdentifier, Error)>> {
self.memory.mark_mem_object_as_freed(object_pointer)
}
......@@ -263,6 +262,9 @@ impl State {
ids_to_remove.remove(caller_id);
for register_value in self.register.values_mut() {
register_value.remove_ids(&ids_to_remove);
if register_value.is_empty() {
*register_value = register_value.top();
}
}
self.memory.remove_ids(&ids_to_remove);
self.caller_stack_ids = BTreeSet::new();
......@@ -341,31 +343,7 @@ impl State {
result: Data,
) -> Result<(), Error> {
if let Expression::Var(var) = expression {
match (self.eval(expression), result) {
(Data::Value(old_value), Data::Value(result_value)) => {
self.set_register(var, old_value.intersect(&result_value)?.into())
}
(Data::Pointer(old_pointer), Data::Pointer(result_pointer)) => {
let mut specialized_targets = BTreeMap::new();
for (id, offset) in result_pointer.targets() {
if let Some(old_offset) = old_pointer.targets().get(id) {
if let Ok(specialized_offset) = old_offset.intersect(offset) {
specialized_targets.insert(id.clone(), specialized_offset);
}
}
}
if !specialized_targets.is_empty() {
self.set_register(
var,
PointerDomain::with_targets(specialized_targets).into(),
);
} else {
return Err(anyhow!("Pointer with no targets is unsatisfiable"));
}
}
(Data::Top(_), result) => self.set_register(var, result),
_ => (),
}
self.set_register(var, self.eval(expression).intersect(&result)?);
Ok(())
} else if let Expression::BinOp { op, lhs, rhs } = expression {
self.specialize_by_binop_expression_result(op, lhs, rhs, result)
......@@ -413,7 +391,7 @@ impl State {
arg,
} => {
if *low_byte == ByteSize::new(0) {
if let Data::Value(arg_value) = self.eval(expression) {
if let Some(arg_value) = self.eval(expression).get_if_absolute_value() {
if arg_value.fits_into_size(*size) {
let intermediate_result =
result.cast(CastOpType::IntSExt, arg.bytesize());
......@@ -598,45 +576,42 @@ impl State {
lhs: &Expression,
rhs: &Expression,
) -> Result<(), Error> {
if let (Data::Pointer(lhs_pointer), Data::Pointer(rhs_pointer)) =
(self.eval(lhs), self.eval(rhs))
{
match (
lhs_pointer.unwrap_if_unique_target(),
rhs_pointer.unwrap_if_unique_target(),
) {
(Some((lhs_id, lhs_offset)), Some((rhs_id, rhs_offset))) if lhs_id == rhs_id => {
if !(self.memory.is_unique_object(lhs_id)?) {
// Since the pointers may or may not point to different instances referenced by the same ID we cannot compare them.
return Ok(());
let (lhs_pointer, rhs_pointer) = (self.eval(lhs), self.eval(rhs));
match (
lhs_pointer.get_if_unique_target(),
rhs_pointer.get_if_unique_target(),
) {
(Some((lhs_id, lhs_offset)), Some((rhs_id, rhs_offset))) if lhs_id == rhs_id => {
if !(self.memory.is_unique_object(lhs_id)?) {
// Since the pointers may or may not point to different instances referenced by the same ID we cannot compare them.
return Ok(());
}
if *op == BinOpType::IntEqual {
let specialized_offset = lhs_offset.clone().intersect(rhs_offset)?;
let specialized_domain: Data =
Data::from_target(lhs_id.clone(), specialized_offset);
self.specialize_by_expression_result(lhs, specialized_domain.clone())?;
self.specialize_by_expression_result(rhs, specialized_domain)?;
} else if *op == BinOpType::IntNotEqual {
if let Ok(rhs_offset_bitvec) = rhs_offset.try_to_bitvec() {
let new_lhs_offset =
lhs_offset.clone().add_not_equal_bound(&rhs_offset_bitvec)?;
self.specialize_by_expression_result(
lhs,
Data::from_target(lhs_id.clone(), new_lhs_offset),
)?;
}
if *op == BinOpType::IntEqual {
let specialized_offset = lhs_offset.intersect(rhs_offset)?;
let specialized_domain: Data =
PointerDomain::new(lhs_id.clone(), specialized_offset).into();
self.specialize_by_expression_result(lhs, specialized_domain.clone())?;
self.specialize_by_expression_result(rhs, specialized_domain)?;
} else if *op == BinOpType::IntNotEqual {
if let Ok(rhs_offset_bitvec) = rhs_offset.try_to_bitvec() {
let new_lhs_offset =
lhs_offset.clone().add_not_equal_bound(&rhs_offset_bitvec)?;
self.specialize_by_expression_result(
lhs,
PointerDomain::new(lhs_id.clone(), new_lhs_offset).into(),
)?;
}
if let Ok(lhs_offset_bitvec) = lhs_offset.try_to_bitvec() {
let new_rhs_offset =
rhs_offset.clone().add_not_equal_bound(&lhs_offset_bitvec)?;
self.specialize_by_expression_result(
rhs,
PointerDomain::new(rhs_id.clone(), new_rhs_offset).into(),
)?;
}
if let Ok(lhs_offset_bitvec) = lhs_offset.try_to_bitvec() {
let new_rhs_offset =
rhs_offset.clone().add_not_equal_bound(&lhs_offset_bitvec)?;
self.specialize_by_expression_result(
rhs,
Data::from_target(rhs_id.clone(), new_rhs_offset),
)?;
}
}
_ => (), // Other cases not handled, since it depends on the meaning of pointer IDs, which may change in the future.
}
_ => (), // Other cases not handled, since it depends on the meaning of pointer IDs, which may change in the future.
}
Ok(())
}
......
......@@ -88,7 +88,7 @@ pub fn check_cwe(
let general_context = Context::new(
project,
analysis_results.runtime_memory_image,
&pointer_inference_results,
pointer_inference_results,
cwe_sender,
);
......
......@@ -216,7 +216,7 @@ impl<'a> Context<'a> {
if let Ok(stack_param) = pi_state.eval_parameter_arg(
parameter,
&self.project.stack_pointer_register,
&self.runtime_memory_image,
self.runtime_memory_image,
) {
if state.check_if_address_points_to_taint(stack_param, pi_state) {
return true;
......
......@@ -150,13 +150,11 @@ impl State {
/// Return whether the value at the given address (with the given size) is tainted.
pub fn load_taint_from_memory(&self, address: &Data, size: ByteSize) -> Taint {
let mut taint = Taint::Top(size);
if let Data::Pointer(pointer) = address {
for (mem_id, offset) in pointer.targets().iter() {
if let (Some(mem_region), Ok(position)) =
(self.memory_taint.get(mem_id), offset.try_to_bitvec())
{
taint = taint.merge(&mem_region.get(position.clone(), size));
}
for (mem_id, offset) in address.get_relative_values() {
if let (Some(mem_region), Ok(position)) =
(self.memory_taint.get(mem_id), offset.try_to_bitvec())
{
taint = taint.merge(&mem_region.get(position.clone(), size));
}
}
taint
......@@ -168,30 +166,26 @@ impl State {
/// we merge the taint object with the object at the targets,
/// possibly tainting all possible targets.
pub fn save_taint_to_memory(&mut self, address: &Data, taint: Taint) {
if let Data::Pointer(pointer) = address {
if pointer.targets().len() == 1 {
for (mem_id, offset) in pointer.targets().iter() {
if let Ok(position) = offset.try_to_bitvec() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
mem_region.add(taint, position.clone());
} else {
let mut mem_region = MemRegion::new(address.bytesize());
mem_region.add(taint, position.clone());
self.memory_taint.insert(mem_id.clone(), mem_region);
}
}
if let Some((mem_id, offset)) = address.get_if_unique_target() {
if let Ok(position) = offset.try_to_bitvec() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
mem_region.add(taint, position);
} else {
let mut mem_region = MemRegion::new(address.bytesize());
mem_region.add(taint, position);
self.memory_taint.insert(mem_id.clone(), mem_region);
}
} else {
for (mem_id, offset) in pointer.targets().iter() {
if let Ok(position) = offset.try_to_bitvec() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
let old_taint = mem_region.get(position.clone(), taint.bytesize());
mem_region.add(old_taint.merge(&taint), position.clone());
} else {
let mut mem_region = MemRegion::new(address.bytesize());
mem_region.add(taint, position.clone());
self.memory_taint.insert(mem_id.clone(), mem_region);
}
}
} else {
for (mem_id, offset) in address.get_relative_values() {
if let Ok(position) = offset.try_to_bitvec() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
let old_taint = mem_region.get(position.clone(), taint.bytesize());
mem_region.add(old_taint.merge(&taint), position.clone());
} else {
let mut mem_region = MemRegion::new(address.bytesize());
mem_region.add(taint, position.clone());
self.memory_taint.insert(mem_id.clone(), mem_region);
}
}
}
......@@ -214,7 +208,7 @@ impl State {
/// Return true if the memory object with the given ID contains a tainted value.
pub fn check_mem_id_for_taint(&self, id: &AbstractIdentifier) -> bool {
if let Some(mem_object) = self.memory_taint.get(&id) {
if let Some(mem_object) = self.memory_taint.get(id) {
for elem in mem_object.values() {
if elem.is_tainted() {
return true;
......@@ -234,27 +228,26 @@ impl State {
pi_state: &PointerInferenceState,
) -> bool {
use crate::analysis::pointer_inference::object::ObjectType;
if let Data::Pointer(pointer) = address {
for (target, offset) in pointer.targets() {
if let Ok(Some(ObjectType::Stack)) = pi_state.memory.get_object_type(target) {
// Only check if the value at the address is tainted
if let (Some(mem_object), Ok(target_offset)) =
(self.memory_taint.get(target), offset.try_to_bitvec())
{
if let Some(taint) = mem_object.get_unsized(target_offset.clone()) {
if taint.is_tainted() {
return true;
}
for (target, offset) in address.get_relative_values() {
if let Ok(Some(ObjectType::Stack)) = pi_state.memory.get_object_type(target) {
// Only check if the value at the address is tainted
if let (Some(mem_object), Ok(target_offset)) =
(self.memory_taint.get(target), offset.try_to_bitvec())
{
if let Some(taint) = mem_object.get_unsized(target_offset.clone()) {
if taint.is_tainted() {
return true;
}
}
} else {
// Check whether the memory object contains any taint.
if self.check_mem_id_for_taint(target) {
return true;
}
}
} else {
// Check whether the memory object contains any taint.
if self.check_mem_id_for_taint(target) {
return true;
}
}
}
false
}
......@@ -439,9 +432,9 @@ mod tests {
)
}
fn new_pointer_domain(location: &str, offset: i64) -> PointerDomain<ValueDomain> {
fn new_pointer(location: &str, offset: i64) -> DataDomain<ValueDomain> {
let id = new_id(location);
PointerDomain::new(id, bv(offset))
DataDomain::from_target(id, bv(offset))
}
#[test]
......@@ -453,7 +446,7 @@ mod tests {
state.set_register_taint(&register("RAX"), taint.clone());
let mut other_state = State::mock();
let address = Data::Pointer(new_pointer_domain("mem", 10));
let address = new_pointer("mem", 10);
other_state.save_taint_to_memory(&address, taint);
let merged_state = state.merge(&other_state);
......@@ -466,7 +459,7 @@ mod tests {
merged_state.load_taint_from_memory(&address, ByteSize::new(8)),
taint.clone()
);
let other_address = Data::Pointer(new_pointer_domain("mem", 18));
let other_address = new_pointer("mem", 18);
assert_eq!(
merged_state.load_taint_from_memory(&other_address, ByteSize::new(8)),
top.clone()
......
......@@ -53,7 +53,7 @@ pub fn get_calls<'a>(
let mut calls: Vec<(&str, &Tid, &str)> = Vec::new();
let mut symbol_map: HashMap<&Tid, &str> = HashMap::with_capacity(dangerous_symbols.len());
for symbol in dangerous_symbols.iter() {
symbol_map.insert(&symbol.tid, &symbol.name.as_str());
symbol_map.insert(&symbol.tid, symbol.name.as_str());
}
for sub in subfunctions.iter() {
calls.append(&mut get_calls_to_symbols(sub, &symbol_map));
......
use petgraph::graph::NodeIndex;
use crate::abstract_domain::{DataDomain, IntervalDomain, PointerDomain};
use crate::abstract_domain::IntervalDomain;
use crate::analysis::pointer_inference::{Data, PointerInference as PointerInferenceComputation};
use crate::intermediate_representation::{
Arg, BinOpType, Bitvector, ByteSize, CallingConvention, Expression, ExternSymbol, Tid, Variable,
......@@ -193,8 +193,7 @@ fn tainting_user_input_symbol_parameters() {
format_string_index.insert("scanf".to_string(), 0);
let global_address = Bitvector::from_str_radix(16, "500c").unwrap();
let string_address =
DataDomain::Value(IntervalDomain::new(global_address.clone(), global_address));
let string_address = IntervalDomain::new(global_address.clone(), global_address).into();
let mut pi_result_state = pi_results
.get_node_value(call_source_node)
......@@ -217,7 +216,7 @@ fn tainting_user_input_symbol_parameters() {
})),
rhs: Box::new(Expression::Const(Bitvector::from_u64(0))),
},
&Data::Pointer(PointerDomain::new(setup.pi_state.stack_id.clone(), bv(-8))),
&Data::from_target(setup.pi_state.stack_id.clone(), bv(-8)),
&mem_image,
)
.expect("Failed to write to address.");
......@@ -294,7 +293,7 @@ fn processing_scanf() {
})),
rhs: Box::new(Expression::Const(Bitvector::from_u64(0))),
},
&Data::Pointer(PointerDomain::new(setup.pi_state.stack_id.clone(), bv(-8))),
&Data::from_target(setup.pi_state.stack_id.clone(), bv(-8)),
context.runtime_memory_image,
)
.expect("Failed to write to address.");
......@@ -349,7 +348,7 @@ fn processing_sscanf() {
})),
rhs: Box::new(Expression::Const(Bitvector::from_u64(0))),
},
&Data::Pointer(PointerDomain::new(setup.pi_state.stack_id.clone(), bv(-8))),
&Data::from_target(setup.pi_state.stack_id.clone(), bv(-8)),
context.runtime_memory_image,
)
.expect("Failed to write to address.");
......@@ -418,7 +417,7 @@ fn tainting_function_arguments() {
})),
rhs: Box::new(Expression::Const(Bitvector::from_u64(24))),
},
&Data::Pointer(PointerDomain::new(setup.pi_state.stack_id.clone(), bv(32))),
&Data::from_target(setup.pi_state.stack_id.clone(), bv(32)),
context.runtime_memory_image,
)
.expect("Failed to write to address.");
......@@ -431,7 +430,7 @@ fn tainting_function_arguments() {
);
assert!(setup.state.address_points_to_taint(
Data::Pointer(PointerDomain::new(setup.pi_state.stack_id.clone(), bv(32))),
Data::from_target(setup.pi_state.stack_id.clone(), bv(32)),
&setup.pi_state
));
}
......
......@@ -4,7 +4,7 @@ use super::*;
use crate::analysis::{backward_interprocedural_fixpoint::Context as BackwardContext, graph::Node};
use crate::{
abstract_domain::{DataDomain, PointerDomain, SizedDomain},
abstract_domain::{DataDomain, SizedDomain},
analysis::pointer_inference::{Data, State as PointerInferenceState, ValueDomain},
intermediate_representation::{Expression, Variable},
};
......@@ -118,8 +118,8 @@ impl Setup {
state,
pi_state,
taint_source,
base_eight_offset: Data::Pointer(PointerDomain::new(stack_id.clone(), bv(-8))),
base_sixteen_offset: Data::Pointer(PointerDomain::new(stack_id.clone(), bv(-16))),
base_eight_offset: Data::from_target(stack_id.clone(), bv(-8)),
base_sixteen_offset: Data::from_target(stack_id.clone(), bv(-16)),
}
}
}
......@@ -339,7 +339,7 @@ fn creating_pi_def_map() {
} else if *def_tid == def2 {
assert_eq!(
pi_state.get_register(&rdi_reg),
Data::Pointer(PointerDomain::new(stack_id.clone(), bv(-8))),
Data::from_target(stack_id.clone(), bv(-8)),
);
}
}
......@@ -568,10 +568,8 @@ fn handling_assign_and_load() {
new_state = context.update_def(&new_state, &mock_assign_stack).unwrap();
assert_eq!(new_state.get_register_taint(&r9_reg), None);
assert_eq!(
new_state.address_points_to_taint(
Data::Pointer(PointerDomain::new(stack_id.clone(), bv(0))),
&setup.pi_state
),
new_state
.address_points_to_taint(Data::from_target(stack_id.clone(), bv(0)), &setup.pi_state),
true
);
......@@ -653,10 +651,8 @@ fn updating_def() {
new_state = context.update_def(&new_state, &mock_assign_stack).unwrap();
assert_eq!(new_state.get_register_taint(&r9_reg), None);
assert_eq!(
new_state.address_points_to_taint(
Data::Pointer(PointerDomain::new(stack_id.clone(), bv(0))),
&setup.pi_state
),
new_state
.address_points_to_taint(Data::from_target(stack_id.clone(), bv(0)), &setup.pi_state),
true
);
......
......@@ -132,30 +132,26 @@ impl State {
/// we merge the taint object with the object at the targets,
/// possibly tainting all possible targets.
pub fn save_taint_to_memory(&mut self, address: &Data, taint: Taint) {
if let Data::Pointer(pointer) = address {
if pointer.targets().len() == 1 {
for (mem_id, offset) in pointer.targets().iter() {
if let Ok(position) = offset.try_to_bitvec() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
mem_region.add(taint, position.clone());
} else {
let mut mem_region = MemRegion::new(address.bytesize());
mem_region.add(taint, position.clone());
self.memory_taint.insert(mem_id.clone(), mem_region);
}
}
if let Some((mem_id, offset)) = address.get_if_unique_target() {
if let Ok(position) = offset.try_to_bitvec() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
mem_region.add(taint, position);
} else {
let mut mem_region = MemRegion::new(address.bytesize());
mem_region.add(taint, position);
self.memory_taint.insert(mem_id.clone(), mem_region);
}
} else {
for (mem_id, offset) in pointer.targets().iter() {
if let Ok(position) = offset.try_to_bitvec() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
let old_taint = mem_region.get(position.clone(), taint.bytesize());
mem_region.add(old_taint.merge(&taint), position.clone());
} else {
let mut mem_region = MemRegion::new(address.bytesize());
mem_region.add(taint, position.clone());
self.memory_taint.insert(mem_id.clone(), mem_region);
}
}
} else {
for (mem_id, offset) in address.get_relative_values() {
if let Ok(position) = offset.try_to_bitvec() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
let old_taint = mem_region.get(position.clone(), taint.bytesize());
mem_region.add(old_taint.merge(&taint), position.clone());
} else {
let mut mem_region = MemRegion::new(address.bytesize());
mem_region.add(taint, position.clone());
self.memory_taint.insert(mem_id.clone(), mem_region);
}
}
}
......@@ -285,7 +281,7 @@ impl State {
if let Some(pid_map) = self.pi_def_map.as_ref() {
if let Some(pi_state) = pid_map.get(def_tid) {
let address = pi_state.eval(target);
if self.address_points_to_taint(address.clone(), &pi_state) {
if self.address_points_to_taint(address.clone(), pi_state) {
self.taint_def_input_register(
value,
stack_pointer_register,
......@@ -361,15 +357,12 @@ impl State {
/// Remove the taint in the specified memory regions at the specified offsets.
pub fn remove_mem_taint_at_target(&mut self, address: &Data) {
if let Data::Pointer(pointer) = address {
for (mem_id, offset) in pointer.targets().iter() {
if let (Some(mem_region), Ok(position)) =
(self.memory_taint.get_mut(mem_id), offset.try_to_bitvec())
{
if let Some(taint) = mem_region.get_unsized(position.clone()) {
mem_region
.remove(position, Bitvector::from_u64(u64::from(taint.bytesize())));
}
for (mem_id, offset) in address.get_relative_values() {
if let (Some(mem_region), Ok(position)) =
(self.memory_taint.get_mut(mem_id), offset.try_to_bitvec())
{
if let Some(taint) = mem_region.get_unsized(position.clone()) {
mem_region.remove(position, Bitvector::from_u64(u64::from(taint.bytesize())));
}
}
}
......@@ -391,7 +384,7 @@ impl State {
/// Return true if the memory object with the given ID contains a tainted value.
pub fn check_mem_id_for_taint(&self, id: &AbstractIdentifier) -> bool {
if let Some(mem_object) = self.memory_taint.get(&id) {
if let Some(mem_object) = self.memory_taint.get(id) {
for elem in mem_object.values() {
if elem.is_tainted() {
return true;
......@@ -407,24 +400,22 @@ impl State {
/// return true if the memory object contains any tainted value (at any position).
pub fn address_points_to_taint(&self, address: Data, pi_state: &PointerInferenceState) -> bool {
use crate::analysis::pointer_inference::object::ObjectType;
if let Data::Pointer(pointer) = address {
for (target, offset) in pointer.targets() {
if let Ok(Some(ObjectType::Stack)) = pi_state.memory.get_object_type(target) {
// Only check if the value at the address is tainted
if let (Some(mem_object), Ok(target_offset)) =
(self.memory_taint.get(target), offset.try_to_bitvec())
{
if let Some(taint) = mem_object.get_unsized(target_offset.clone()) {
if taint.is_tainted() {
return true;
}
for (target, offset) in address.get_relative_values() {
if let Ok(Some(ObjectType::Stack)) = pi_state.memory.get_object_type(target) {
// Only check if the value at the address is tainted
if let (Some(mem_object), Ok(target_offset)) =
(self.memory_taint.get(target), offset.try_to_bitvec())
{
if let Some(taint) = mem_object.get_unsized(target_offset.clone()) {
if taint.is_tainted() {
return true;
}
}
} else {
// Check whether the memory object contains any taint.
if self.check_mem_id_for_taint(target) {
return true;
}
}
} else {
// Check whether the memory object contains any taint.
if self.check_mem_id_for_taint(target) {
return true;
}
}
}
......@@ -445,7 +436,7 @@ impl State {
let taints = self.register_taint.clone();
for (register, _) in taints.iter() {
if register_names.get(&register.name).is_none() {
self.register_taint.remove(&register);
self.register_taint.remove(register);
}
}
}
......
use crate::analysis::pointer_inference::ValueDomain;
use crate::{
abstract_domain::{DataDomain, PointerDomain},
intermediate_representation::CastOpType,
};
use crate::{abstract_domain::DataDomain, intermediate_representation::CastOpType};
use super::*;
......@@ -98,9 +95,9 @@ impl Setup {
constant: String::from("Hello World"),
constant_address: Bitvector::from_u32(12290),
def_tid: Tid::new("def"),
stack_pointer: Data::Pointer(PointerDomain::new(stack_id.clone(), bv(0))),
base_eight_offset: Data::Pointer(PointerDomain::new(stack_id.clone(), bv(-8))),
base_sixteen_offset: Data::Pointer(PointerDomain::new(stack_id.clone(), bv(-16))),
stack_pointer: Data::from_target(stack_id.clone(), bv(0)),
base_eight_offset: Data::from_target(stack_id.clone(), bv(-8)),
base_sixteen_offset: Data::from_target(stack_id.clone(), bv(-16)),
}
}
}
......
......@@ -37,7 +37,7 @@ pub static CWE_MODULE: crate::CweModule = crate::CweModule {
/// check whether the ioctl symbol is called by any subroutine. If so, generate the cwe warning.
pub fn handle_sub(sub: &Term<Sub>, symbol: &HashMap<&Tid, &str>) -> Vec<CweWarning> {
let calls: Vec<(&str, &Tid, &str)> = get_calls_to_symbols(sub, &symbol);
let calls: Vec<(&str, &Tid, &str)> = get_calls_to_symbols(sub, symbol);
if !calls.is_empty() {
return generate_cwe_warning(&calls);
}
......
......@@ -52,7 +52,7 @@ impl Term<Blk> {
.indirect_jmp_targets
.iter()
.filter_map(|target| {
if known_block_tids.get(&target).is_some() {
if known_block_tids.get(target).is_some() {
Some(target.clone())
} else {
let error_msg =
......
......@@ -131,7 +131,7 @@ impl Variable {
match (&self.name, &self.value) {
(None, Some(hex_value)) => {
assert!(u64::from(self.size) <= 8);
let val: u64 = u64::from_str_radix(&hex_value, 16).unwrap();
let val: u64 = u64::from_str_radix(hex_value, 16).unwrap();
val.into()
}
_ => panic!(),
......
......@@ -514,12 +514,11 @@ impl ExternSymbol {
let mut symbol = self.clone();
let mut parameters = Vec::new();
let mut return_values = Vec::new();
let input_args: Vec<&Arg> = symbol
let symbol_has_input_args = symbol
.arguments
.iter()
.filter(|arg| matches!(arg.intent, ArgIntent::INPUT))
.collect();
if symbol.is_scanf_or_sscanf() && input_args.is_empty() {
.any(|arg| matches!(arg.intent, ArgIntent::INPUT));
if symbol.is_scanf_or_sscanf() && !symbol_has_input_args {
symbol.create_format_string_args_for_scanf_and_sscanf(
conventions,
stack_pointer,
......
......@@ -7,7 +7,7 @@ use crate::{intermediate_representation::Datatype, prelude::*};
use regex::Regex;
use crate::{
abstract_domain::{DataDomain, IntervalDomain, TryToBitvec},
abstract_domain::{IntervalDomain, TryToBitvec},
analysis::pointer_inference::State as PointerInferenceState,
intermediate_representation::{
Arg, ByteSize, CallingConvention, DatatypeProperties, ExternSymbol, Project, Variable,
......@@ -37,13 +37,13 @@ pub fn get_input_format_string(
runtime_memory_image: &RuntimeMemoryImage,
) -> Result<String, Error> {
if let Some(format_string) = extern_symbol.parameters.get(format_string_index) {
if let Ok(DataDomain::Value(address)) = pi_state.eval_parameter_arg(
format_string,
&stack_pointer_register,
runtime_memory_image,
) {
if let Ok(Some(address)) = pi_state
.eval_parameter_arg(format_string, stack_pointer_register, runtime_memory_image)
.as_ref()
.map(|param| param.get_if_absolute_value())
{
return parse_format_string_destination_and_return_content(
address,
address.clone(),
runtime_memory_image,
);
}
......
......@@ -24,7 +24,7 @@ fn test_get_variable_parameters() {
let global_address = Bitvector::from_str_radix(16, "5000").unwrap();
pi_state.set_register(
&Variable::mock("RDI", 8 as u64),
DataDomain::Value(IntervalDomain::new(global_address.clone(), global_address)),
IntervalDomain::new(global_address.clone(), global_address).into(),
);
let mut project = Project::mock_empty();
let cconv = CallingConvention::mock_with_parameter_registers(
......@@ -66,7 +66,7 @@ fn test_get_variable_parameters() {
let global_address = Bitvector::from_str_radix(16, "500c").unwrap();
pi_state.set_register(
&Variable::mock("RDI", 8 as u64),
DataDomain::Value(IntervalDomain::new(global_address.clone(), global_address)),
IntervalDomain::new(global_address.clone(), global_address).into(),
);
assert_eq!(
......@@ -91,7 +91,7 @@ fn test_get_input_format_string() {
let global_address = Bitvector::from_str_radix(16, "3002").unwrap();
pi_state.set_register(
&Variable::mock("RSI", 8 as u64),
DataDomain::Value(IntervalDomain::new(global_address.clone(), global_address)),
IntervalDomain::new(global_address.clone(), global_address).into(),
);
assert_eq!(
......
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