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 WORKDIR /cwe_checker
......
...@@ -44,7 +44,7 @@ If you want to build the docker image yourself, just run `docker build -t cwe_ch ...@@ -44,7 +44,7 @@ If you want to build the docker image yourself, just run `docker build -t cwe_ch
### Local installation ### ### Local installation ###
The following dependencies must be installed in order to build and install the *cwe_checker* locally: 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 - [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. 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) { ...@@ -137,7 +137,7 @@ fn run_with_ghidra(args: &CmdlineArgs) {
// Generate the representation of the runtime memory image of the binary // 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() { 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); panic!("Error while generating runtime memory image: {}", err);
}) })
} else { } 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 { ...@@ -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`. /// 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. /// Does not check whether this is also true for the widening hints.
pub fn fits_into_size(&self, size: ByteSize) -> bool { pub fn fits_into_size(&self, size: ByteSize) -> bool {
...@@ -503,6 +480,29 @@ impl SpecializeByConditional for IntervalDomain { ...@@ -503,6 +480,29 @@ impl SpecializeByConditional for IntervalDomain {
Ok(self) 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 { impl AbstractDomain for IntervalDomain {
......
...@@ -591,7 +591,7 @@ fn add_not_equal_bounds() { ...@@ -591,7 +591,7 @@ fn add_not_equal_bounds() {
fn intersection() { fn intersection() {
let interval1 = IntervalDomain::mock_with_bounds(Some(-100), -10, 10, Some(100)); let interval1 = IntervalDomain::mock_with_bounds(Some(-100), -10, 10, Some(100));
let interval2 = IntervalDomain::mock_with_bounds(Some(-20), 2, 30, None); 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!( assert_eq!(
intersection, intersection,
IntervalDomain::mock_with_bounds(Some(-20), 2, 10, Some(100)) IntervalDomain::mock_with_bounds(Some(-20), 2, 10, Some(100))
......
...@@ -185,7 +185,7 @@ impl<T: AbstractDomain + SizedDomain + HasTop + std::fmt::Debug> MemRegionData<T ...@@ -185,7 +185,7 @@ impl<T: AbstractDomain + SizedDomain + HasTop + std::fmt::Debug> MemRegionData<T
for (pos_left, elem_left) in self.values.iter() { for (pos_left, elem_left) in self.values.iter() {
if let Some((_pos_right, elem_right)) = other.values.get_key_value(pos_left) { if let Some((_pos_right, elem_right)) = other.values.get_key_value(pos_left) {
if elem_left.bytesize() == elem_right.bytesize() { 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() { if !merged_val.is_top() {
// we discard top()-values, as they don't contain information // we discard top()-values, as they don't contain information
merged_values.insert(*pos_left, merged_val); merged_values.insert(*pos_left, merged_val);
......
...@@ -10,9 +10,6 @@ pub use bitvector::*; ...@@ -10,9 +10,6 @@ pub use bitvector::*;
mod identifier; mod identifier;
pub use identifier::*; pub use identifier::*;
mod pointer;
pub use pointer::*;
mod data; mod data;
pub use data::*; pub use data::*;
...@@ -153,4 +150,7 @@ pub trait SpecializeByConditional: Sized { ...@@ -153,4 +150,7 @@ pub trait SpecializeByConditional: Sized {
/// Return the restriction of `self` to values satisfying `self != bound` /// Return the restriction of `self` to values satisfying `self != bound`
/// Returns an error if `self` only represents one value for which `self == bound` holds. /// 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>; 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> { ...@@ -132,7 +132,7 @@ impl<T: Context> Computation<T> {
/// Get the value of a node. /// Get the value of a node.
pub fn get_node_value(&self, node: NodeIndex) -> Option<&T::NodeValue> { 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) Some(value)
} else { } else {
self.default_value.as_ref() self.default_value.as_ref()
......
...@@ -334,7 +334,7 @@ impl<'a> GraphBuilder<'a> { ...@@ -334,7 +334,7 @@ impl<'a> GraphBuilder<'a> {
} }
} else { } else {
let mut call_source_node: Option<NodeIndex> = None; 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] { let (target_block, target_sub) = match self.graph[*target_node] {
Node::BlkStart(target_block, target_sub) => (target_block, target_sub), Node::BlkStart(target_block, target_sub) => (target_block, target_sub),
_ => panic!(), _ => panic!(),
......
...@@ -142,7 +142,7 @@ fn context_problem_implementation() { ...@@ -142,7 +142,7 @@ fn context_problem_implementation() {
// test update_def // test update_def
state = context.update_def(&state, &def).unwrap(); 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); assert_eq!(state.eval(&Var(register("RSP"))), stack_pointer);
state = context.update_def(&state, &store_term).unwrap(); state = context.update_def(&state, &store_term).unwrap();
...@@ -175,8 +175,8 @@ fn context_problem_implementation() { ...@@ -175,8 +175,8 @@ fn context_problem_implementation() {
callee_state callee_state
.memory .memory
.set_value( .set_value(
PointerDomain::new(new_id("func", "RSP"), bv(-30)), Data::from_target(new_id("func", "RSP"), bv(-30)),
Data::Value(bv(33).into()), bv(33).into(),
) )
.unwrap(); .unwrap();
// Emulate removing the return pointer from the stack for x64 // Emulate removing the return pointer from the stack for x64
...@@ -213,28 +213,25 @@ fn context_problem_implementation() { ...@@ -213,28 +213,25 @@ fn context_problem_implementation() {
.bin_op(BinOpType::IntAdd, &Bitvector::from_i64(8).into()) .bin_op(BinOpType::IntAdd, &Bitvector::from_i64(8).into())
); );
state.set_register(&register("callee_saved_reg"), Data::Value(bv(13))); state.set_register(&register("callee_saved_reg"), bv(13).into());
state.set_register(&register("other_reg"), Data::Value(bv(14))); state.set_register(&register("other_reg"), bv(14).into());
let malloc = call_term("extern_malloc"); let malloc = call_term("extern_malloc");
let mut state_after_malloc = context.update_call_stub(&state, &malloc).unwrap(); let mut state_after_malloc = context.update_call_stub(&state, &malloc).unwrap();
assert_eq!( assert_eq!(
state_after_malloc.get_register(&register("RDX")), state_after_malloc.get_register(&register("RDX")),
Data::Pointer(PointerDomain::new( Data::from_target(new_id("call_extern_malloc", "RDX"), bv(0))
new_id("call_extern_malloc", "RDX"),
bv(0)
))
); );
assert_eq!(state_after_malloc.memory.get_num_objects(), 2); assert_eq!(state_after_malloc.memory.get_num_objects(), 2);
assert_eq!( assert_eq!(
state_after_malloc.get_register(&register("RSP")), state_after_malloc.get_register(&register("RSP")),
state state
.get_register(&register("RSP")) .get_register(&register("RSP"))
.bin_op(BinOpType::IntAdd, &Data::Value(bv(8))) .bin_op(BinOpType::IntAdd, &bv(8).into())
); );
assert_eq!( assert_eq!(
state_after_malloc.get_register(&register("callee_saved_reg")), state_after_malloc.get_register(&register("callee_saved_reg")),
Data::Value(bv(13)) bv(13).into()
); );
assert!(state_after_malloc assert!(state_after_malloc
.get_register(&register("other_reg")) .get_register(&register("other_reg"))
...@@ -242,10 +239,7 @@ fn context_problem_implementation() { ...@@ -242,10 +239,7 @@ fn context_problem_implementation() {
state_after_malloc.set_register( state_after_malloc.set_register(
&register("callee_saved_reg"), &register("callee_saved_reg"),
Data::Pointer(PointerDomain::new( Data::from_target(new_id("call_extern_malloc", "RDX"), bv(0)),
new_id("call_extern_malloc", "RDX"),
bv(0),
)),
); );
let free = call_term("extern_free"); let free = call_term("extern_free");
let state_after_free = context let state_after_free = context
...@@ -255,10 +249,7 @@ fn context_problem_implementation() { ...@@ -255,10 +249,7 @@ fn context_problem_implementation() {
assert_eq!(state_after_free.memory.get_num_objects(), 2); assert_eq!(state_after_free.memory.get_num_objects(), 2);
assert_eq!( assert_eq!(
state_after_free.get_register(&register("callee_saved_reg")), state_after_free.get_register(&register("callee_saved_reg")),
Data::Pointer(PointerDomain::new( Data::from_target(new_id("call_extern_malloc", "RDX"), bv(0))
new_id("call_extern_malloc", "RDX"),
bv(0)
))
); );
let other_extern_fn = call_term("extern_other"); let other_extern_fn = call_term("extern_other");
...@@ -268,11 +259,11 @@ fn context_problem_implementation() { ...@@ -268,11 +259,11 @@ fn context_problem_implementation() {
state_after_other_fn.get_register(&register("RSP")), state_after_other_fn.get_register(&register("RSP")),
state state
.get_register(&register("RSP")) .get_register(&register("RSP"))
.bin_op(BinOpType::IntAdd, &Data::Value(bv(8))) .bin_op(BinOpType::IntAdd, &bv(8).into())
); );
assert_eq!( assert_eq!(
state_after_other_fn.get_register(&register("callee_saved_reg")), state_after_other_fn.get_register(&register("callee_saved_reg")),
Data::Value(bv(13)) bv(13).into()
); );
assert!(state_after_other_fn assert!(state_after_other_fn
.get_register(&register("other_reg")) .get_register(&register("other_reg"))
...@@ -326,10 +317,7 @@ fn update_return() { ...@@ -326,10 +317,7 @@ fn update_return() {
.insert(other_callsite_id.clone()); .insert(other_callsite_id.clone());
state_before_return.set_register( state_before_return.set_register(
&register("RDX"), &register("RDX"),
Data::Pointer(PointerDomain::new( Data::from_target(new_id("call_callee_other", "RSP"), bv(-32)),
new_id("call_callee_other", "RSP"),
bv(-32),
)),
); );
let state_before_call = State::new(&register("RSP"), Tid::new("original_caller_id")); let state_before_call = State::new(&register("RSP"), Tid::new("original_caller_id"));
...@@ -379,10 +367,7 @@ fn update_return() { ...@@ -379,10 +367,7 @@ fn update_return() {
.get_all_object_ids() .get_all_object_ids()
.get(&new_id("caller_caller", "RSP")) .get(&new_id("caller_caller", "RSP"))
.is_some()); .is_some());
let expected_rsp = Data::Pointer(PointerDomain::new( let expected_rsp = Data::from_target(new_id("original_caller_id", "RSP"), bv(-8));
new_id("original_caller_id", "RSP"),
bv(-8),
));
assert_eq!(state.get_register(&register("RSP")), expected_rsp); assert_eq!(state.get_register(&register("RSP")), expected_rsp);
} }
......
...@@ -5,7 +5,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont ...@@ -5,7 +5,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
/// Get the underlying graph on which the analysis operates. /// Get the underlying graph on which the analysis operates.
fn get_graph(&self) -> &Graph<'a> { fn get_graph(&self) -> &Graph<'a> {
&self.graph self.graph
} }
/// Merge two state values. /// Merge two state values.
...@@ -77,7 +77,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont ...@@ -77,7 +77,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
} }
Def::Load { var, address } => { Def::Load { var, address } => {
self.log_debug( 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(&def.tid),
); );
Some(new_state) Some(new_state)
...@@ -158,11 +158,10 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont ...@@ -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. // At the beginning of a function this is the only known pointer to the new stack frame.
callee_state.set_register( callee_state.set_register(
&self.project.stack_pointer_register, &self.project.stack_pointer_register,
PointerDomain::new( Data::from_target(
callee_stack_id.clone(), callee_stack_id.clone(),
Bitvector::zero(apint::BitWidth::from(address_bytesize)).into(), Bitvector::zero(apint::BitWidth::from(address_bytesize)).into(),
) ),
.into(),
); );
// set the list of caller stack ids to only this caller id // set the list of caller stack ids to only this caller id
callee_state.caller_stack_ids = BTreeSet::new(); callee_state.caller_stack_ids = BTreeSet::new();
...@@ -202,7 +201,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont ...@@ -202,7 +201,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
(Some(state_call), None) => { (Some(state_call), None) => {
if self.is_indirect_call_with_top_target(state_call, call_term) { if self.is_indirect_call_with_top_target(state_call, call_term) {
// We know nothing about the call target. // 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 { } else {
// We know at least something about the call target. // We know at least something about the call target.
// Since we don't have a return value, // Since we don't have a return value,
...@@ -294,7 +293,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont ...@@ -294,7 +293,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
Jmp::CallInd { .. } => { Jmp::CallInd { .. } => {
if self.is_indirect_call_with_top_target(state, call) { if self.is_indirect_call_with_top_target(state, call) {
// We know nothing about the call target. // 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 { } else {
return None; return None;
} }
...@@ -318,7 +317,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont ...@@ -318,7 +317,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
); );
} }
// Clear non-callee-saved registers from the state. // 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[..]); new_state.clear_non_callee_saved_register(&cconv.callee_saved_register[..]);
// Adjust stack register value (for x86 architecture). // Adjust stack register value (for x86 architecture).
self.adjust_stack_register_on_extern_call(state, &mut new_state); self.adjust_stack_register_on_extern_call(state, &mut new_state);
......
...@@ -360,7 +360,7 @@ impl<'a> PointerInference<'a> { ...@@ -360,7 +360,7 @@ impl<'a> PointerInference<'a> {
for jmp in block.term.jmps.iter() { for jmp in block.term.jmps.iter() {
match &jmp.term { match &jmp.term {
Jmp::BranchInd(target_expr) => { Jmp::BranchInd(target_expr) => {
let address = state.eval(&target_expr); let address = state.eval(target_expr);
println!( println!(
"{}: Indirect jump to {}", "{}: Indirect jump to {}",
jmp.tid, jmp.tid,
...@@ -368,7 +368,7 @@ impl<'a> PointerInference<'a> { ...@@ -368,7 +368,7 @@ impl<'a> PointerInference<'a> {
); );
} }
Jmp::CallInd { target, return_ } => { Jmp::CallInd { target, return_ } => {
let address = state.eval(&target); let address = state.eval(target);
println!( println!(
"{}: Indirect call to {}. HasReturn: {}", "{}: Indirect call to {}. HasReturn: {}",
jmp.tid, jmp.tid,
......
...@@ -131,9 +131,7 @@ impl AbstractObjectInfo { ...@@ -131,9 +131,7 @@ impl AbstractObjectInfo {
/// If the abstract object is not unique (i.e. may represent more than one actual object), /// 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. /// merge the old value at the given offset with the new value.
pub fn set_value(&mut self, value: Data, offset: &ValueDomain) -> Result<(), Error> { pub fn set_value(&mut self, value: Data, offset: &ValueDomain) -> Result<(), Error> {
if let Data::Pointer(ref pointer) = value { self.pointer_targets.extend(value.referenced_ids().cloned());
self.pointer_targets.extend(pointer.ids().cloned());
};
if let Ok(concrete_offset) = offset.try_to_bitvec() { if let Ok(concrete_offset) = offset.try_to_bitvec() {
if self.is_unique { if self.is_unique {
self.memory.add(value, concrete_offset); self.memory.add(value, concrete_offset);
...@@ -155,9 +153,7 @@ impl AbstractObjectInfo { ...@@ -155,9 +153,7 @@ impl AbstractObjectInfo {
/// Merge `value` at position `offset` with the value currently saved at that position. /// Merge `value` at position `offset` with the value currently saved at that position.
pub fn merge_value(&mut self, value: Data, offset: &ValueDomain) { pub fn merge_value(&mut self, value: Data, offset: &ValueDomain) {
if let Data::Pointer(ref pointer) = value { self.pointer_targets.extend(value.referenced_ids().cloned());
self.pointer_targets.extend(pointer.ids().cloned());
};
if let Ok(concrete_offset) = offset.try_to_bitvec() { if let Ok(concrete_offset) = offset.try_to_bitvec() {
let merged_value = self let merged_value = self
.memory .memory
...@@ -184,7 +180,7 @@ impl AbstractObjectInfo { ...@@ -184,7 +180,7 @@ impl AbstractObjectInfo {
pub fn get_referenced_ids_underapproximation(&self) -> BTreeSet<AbstractIdentifier> { pub fn get_referenced_ids_underapproximation(&self) -> BTreeSet<AbstractIdentifier> {
let mut referenced_ids = BTreeSet::new(); let mut referenced_ids = BTreeSet::new();
for data in self.memory.values() { for data in self.memory.values() {
referenced_ids.append(&mut data.referenced_ids()) referenced_ids.extend(data.referenced_ids().cloned())
} }
referenced_ids referenced_ids
} }
...@@ -201,8 +197,8 @@ impl AbstractObjectInfo { ...@@ -201,8 +197,8 @@ impl AbstractObjectInfo {
elem.replace_abstract_id(old_id, new_id, offset_adjustment); elem.replace_abstract_id(old_id, new_id, offset_adjustment);
} }
self.memory.clear_top_values(); self.memory.clear_top_values();
if self.pointer_targets.get(&old_id).is_some() { if self.pointer_targets.get(old_id).is_some() {
self.pointer_targets.remove(&old_id); self.pointer_targets.remove(old_id);
self.pointer_targets.insert(new_id.clone()); self.pointer_targets.insert(new_id.clone());
} }
} }
...@@ -218,6 +214,8 @@ impl AbstractObjectInfo { ...@@ -218,6 +214,8 @@ impl AbstractObjectInfo {
/// Remove the provided IDs from the target lists of all pointers in the memory object. /// Remove the provided IDs from the target lists of all pointers in the memory object.
/// Also remove them from the pointer_targets list. /// 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>) { pub fn remove_ids(&mut self, ids_to_remove: &BTreeSet<AbstractIdentifier>) {
self.pointer_targets = self self.pointer_targets = self
.pointer_targets .pointer_targets
...@@ -226,6 +224,9 @@ impl AbstractObjectInfo { ...@@ -226,6 +224,9 @@ impl AbstractObjectInfo {
.collect(); .collect();
for value in self.memory.values_mut() { for value in self.memory.values_mut() {
value.remove_ids(ids_to_remove); 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. self.memory.clear_top_values(); // In case the previous operation left *Top* values in the memory struct.
} }
...@@ -425,7 +426,7 @@ mod tests { ...@@ -425,7 +426,7 @@ mod tests {
} }
fn new_data(number: i64) -> Data { fn new_data(number: i64) -> Data {
Data::Value(bv(number)) bv(number).into()
} }
fn bv(number: i64) -> ValueDomain { fn bv(number: i64) -> ValueDomain {
...@@ -447,7 +448,7 @@ mod tests { ...@@ -447,7 +448,7 @@ mod tests {
object.set_value(three, &offset).unwrap(); object.set_value(three, &offset).unwrap();
assert_eq!( assert_eq!(
object.get_value(Bitvector::from_i64(-16), ByteSize::new(8)), object.get_value(Bitvector::from_i64(-16), ByteSize::new(8)),
Data::Top(ByteSize::new(8)) Data::new_top(ByteSize::new(8))
); );
assert_eq!( assert_eq!(
object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)), object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)),
...@@ -456,12 +457,12 @@ mod tests { ...@@ -456,12 +457,12 @@ mod tests {
object.set_value(new_data(4), &bv(-12)).unwrap(); object.set_value(new_data(4), &bv(-12)).unwrap();
assert_eq!( assert_eq!(
object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)), 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)); object.merge_value(new_data(23), &bv(-12));
assert_eq!( assert_eq!(
object.get_value(Bitvector::from_i64(-12), ByteSize::new(8)), 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(); let mut other_object = new_abstract_object();
...@@ -470,7 +471,7 @@ mod tests { ...@@ -470,7 +471,7 @@ mod tests {
let merged_object = object.merge(&other_object); let merged_object = object.merge(&other_object);
assert_eq!( assert_eq!(
merged_object.get_value(Bitvector::from_i64(-12), ByteSize::new(8)), merged_object.get_value(Bitvector::from_i64(-12), ByteSize::new(8)),
Data::Top(ByteSize::new(8)) Data::new_top(ByteSize::new(8))
); );
assert_eq!( assert_eq!(
merged_object.get_value(Bitvector::from_i64(0), ByteSize::new(8)), merged_object.get_value(Bitvector::from_i64(0), ByteSize::new(8)),
...@@ -486,8 +487,8 @@ mod tests { ...@@ -486,8 +487,8 @@ mod tests {
target_map.insert(new_id("time_1", "RAX"), bv(20)); 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_234", "RAX"), bv(30));
target_map.insert(new_id("time_1", "RBX"), bv(40)); target_map.insert(new_id("time_1", "RBX"), bv(40));
let pointer = PointerDomain::with_targets(target_map.clone()); let pointer = DataDomain::mock_from_target_map(target_map.clone());
object.set_value(pointer.into(), &bv(-15)).unwrap(); object.set_value(pointer, &bv(-15)).unwrap();
assert_eq!(object.get_referenced_ids_overapproximation().len(), 3); assert_eq!(object.get_referenced_ids_overapproximation().len(), 3);
object.replace_abstract_id( object.replace_abstract_id(
...@@ -496,10 +497,10 @@ mod tests { ...@@ -496,10 +497,10 @@ mod tests {
&bv(10), &bv(10),
); );
target_map.remove(&new_id("time_1", "RAX")); 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!( assert_eq!(
object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)), object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)),
modified_pointer.into() modified_pointer
); );
object.replace_abstract_id( object.replace_abstract_id(
...@@ -510,10 +511,10 @@ mod tests { ...@@ -510,10 +511,10 @@ mod tests {
let mut target_map = BTreeMap::new(); let mut target_map = BTreeMap::new();
target_map.insert(new_id("time_234", "RAX"), bv(30)); target_map.insert(new_id("time_234", "RAX"), bv(30));
target_map.insert(new_id("time_234", "RBX"), bv(50)); 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!( assert_eq!(
object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)), object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)),
modified_pointer.into() modified_pointer
); );
} }
...@@ -525,8 +526,8 @@ mod tests { ...@@ -525,8 +526,8 @@ mod tests {
target_map.insert(new_id("time_1", "RAX"), bv(20)); 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_234", "RAX"), bv(30));
target_map.insert(new_id("time_1", "RBX"), bv(40)); target_map.insert(new_id("time_1", "RBX"), bv(40));
let pointer = PointerDomain::with_targets(target_map.clone()); let pointer = DataDomain::mock_from_target_map(target_map.clone());
object.set_value(pointer.into(), &bv(-15)).unwrap(); object.set_value(pointer, &bv(-15)).unwrap();
assert_eq!(object.get_referenced_ids_overapproximation().len(), 3); assert_eq!(object.get_referenced_ids_overapproximation().len(), 3);
let ids_to_remove = vec![new_id("time_1", "RAX"), new_id("time_23", "RBX")] let ids_to_remove = vec![new_id("time_1", "RAX"), new_id("time_23", "RBX")]
......
...@@ -48,11 +48,10 @@ impl State { ...@@ -48,11 +48,10 @@ impl State {
let mut register: BTreeMap<Variable, Data> = BTreeMap::new(); let mut register: BTreeMap<Variable, Data> = BTreeMap::new();
register.insert( register.insert(
stack_register.clone(), stack_register.clone(),
PointerDomain::new( Data::from_target(
stack_id.clone(), stack_id.clone(),
Bitvector::zero(apint::BitWidth::from(stack_register.size)).into(), Bitvector::zero(apint::BitWidth::from(stack_register.size)).into(),
) ),
.into(),
); );
State { State {
register, register,
...@@ -153,7 +152,7 @@ impl State { ...@@ -153,7 +152,7 @@ impl State {
// get all referenced IDs // get all referenced IDs
let mut referenced_ids = BTreeSet::new(); let mut referenced_ids = BTreeSet::new();
for (_reg_name, data) in self.register.iter() { 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.insert(self.stack_id.clone());
referenced_ids.append(&mut self.caller_stack_ids.clone()); referenced_ids.append(&mut self.caller_stack_ids.clone());
...@@ -239,7 +238,7 @@ impl State { ...@@ -239,7 +238,7 @@ impl State {
/// an error with the list of possibly already freed objects is returned. /// an error with the list of possibly already freed objects is returned.
pub fn mark_mem_object_as_freed( pub fn mark_mem_object_as_freed(
&mut self, &mut self,
object_pointer: &PointerDomain<ValueDomain>, object_pointer: &Data,
) -> Result<(), Vec<(AbstractIdentifier, Error)>> { ) -> Result<(), Vec<(AbstractIdentifier, Error)>> {
self.memory.mark_mem_object_as_freed(object_pointer) self.memory.mark_mem_object_as_freed(object_pointer)
} }
...@@ -263,6 +262,9 @@ impl State { ...@@ -263,6 +262,9 @@ impl State {
ids_to_remove.remove(caller_id); ids_to_remove.remove(caller_id);
for register_value in self.register.values_mut() { for register_value in self.register.values_mut() {
register_value.remove_ids(&ids_to_remove); 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.memory.remove_ids(&ids_to_remove);
self.caller_stack_ids = BTreeSet::new(); self.caller_stack_ids = BTreeSet::new();
...@@ -341,31 +343,7 @@ impl State { ...@@ -341,31 +343,7 @@ impl State {
result: Data, result: Data,
) -> Result<(), Error> { ) -> Result<(), Error> {
if let Expression::Var(var) = expression { if let Expression::Var(var) = expression {
match (self.eval(expression), result) { self.set_register(var, self.eval(expression).intersect(&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),
_ => (),
}
Ok(()) Ok(())
} else if let Expression::BinOp { op, lhs, rhs } = expression { } else if let Expression::BinOp { op, lhs, rhs } = expression {
self.specialize_by_binop_expression_result(op, lhs, rhs, result) self.specialize_by_binop_expression_result(op, lhs, rhs, result)
...@@ -413,7 +391,7 @@ impl State { ...@@ -413,7 +391,7 @@ impl State {
arg, arg,
} => { } => {
if *low_byte == ByteSize::new(0) { 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) { if arg_value.fits_into_size(*size) {
let intermediate_result = let intermediate_result =
result.cast(CastOpType::IntSExt, arg.bytesize()); result.cast(CastOpType::IntSExt, arg.bytesize());
...@@ -598,45 +576,42 @@ impl State { ...@@ -598,45 +576,42 @@ impl State {
lhs: &Expression, lhs: &Expression,
rhs: &Expression, rhs: &Expression,
) -> Result<(), Error> { ) -> Result<(), Error> {
if let (Data::Pointer(lhs_pointer), Data::Pointer(rhs_pointer)) = let (lhs_pointer, rhs_pointer) = (self.eval(lhs), self.eval(rhs));
(self.eval(lhs), self.eval(rhs)) match (
{ lhs_pointer.get_if_unique_target(),
match ( rhs_pointer.get_if_unique_target(),
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)?) {
(Some((lhs_id, lhs_offset)), Some((rhs_id, rhs_offset))) if lhs_id == rhs_id => { // Since the pointers may or may not point to different instances referenced by the same ID we cannot compare them.
if !(self.memory.is_unique_object(lhs_id)?) { return Ok(());
// 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 { if let Ok(lhs_offset_bitvec) = lhs_offset.try_to_bitvec() {
let specialized_offset = lhs_offset.intersect(rhs_offset)?; let new_rhs_offset =
let specialized_domain: Data = rhs_offset.clone().add_not_equal_bound(&lhs_offset_bitvec)?;
PointerDomain::new(lhs_id.clone(), specialized_offset).into(); self.specialize_by_expression_result(
self.specialize_by_expression_result(lhs, specialized_domain.clone())?; rhs,
self.specialize_by_expression_result(rhs, specialized_domain)?; Data::from_target(rhs_id.clone(), new_rhs_offset),
} 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(),
)?;
}
} }
} }
_ => (), // 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(()) Ok(())
} }
......
...@@ -88,7 +88,7 @@ pub fn check_cwe( ...@@ -88,7 +88,7 @@ pub fn check_cwe(
let general_context = Context::new( let general_context = Context::new(
project, project,
analysis_results.runtime_memory_image, analysis_results.runtime_memory_image,
&pointer_inference_results, pointer_inference_results,
cwe_sender, cwe_sender,
); );
......
...@@ -216,7 +216,7 @@ impl<'a> Context<'a> { ...@@ -216,7 +216,7 @@ impl<'a> Context<'a> {
if let Ok(stack_param) = pi_state.eval_parameter_arg( if let Ok(stack_param) = pi_state.eval_parameter_arg(
parameter, parameter,
&self.project.stack_pointer_register, &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) { if state.check_if_address_points_to_taint(stack_param, pi_state) {
return true; return true;
......
...@@ -150,13 +150,11 @@ impl State { ...@@ -150,13 +150,11 @@ impl State {
/// Return whether the value at the given address (with the given size) is tainted. /// 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 { pub fn load_taint_from_memory(&self, address: &Data, size: ByteSize) -> Taint {
let mut taint = Taint::Top(size); let mut taint = Taint::Top(size);
if let Data::Pointer(pointer) = address { for (mem_id, offset) in address.get_relative_values() {
for (mem_id, offset) in pointer.targets().iter() { if let (Some(mem_region), Ok(position)) =
if let (Some(mem_region), Ok(position)) = (self.memory_taint.get(mem_id), offset.try_to_bitvec())
(self.memory_taint.get(mem_id), offset.try_to_bitvec()) {
{ taint = taint.merge(&mem_region.get(position.clone(), size));
taint = taint.merge(&mem_region.get(position.clone(), size));
}
} }
} }
taint taint
...@@ -168,30 +166,26 @@ impl State { ...@@ -168,30 +166,26 @@ impl State {
/// we merge the taint object with the object at the targets, /// we merge the taint object with the object at the targets,
/// possibly tainting all possible targets. /// possibly tainting all possible targets.
pub fn save_taint_to_memory(&mut self, address: &Data, taint: Taint) { pub fn save_taint_to_memory(&mut self, address: &Data, taint: Taint) {
if let Data::Pointer(pointer) = address { if let Some((mem_id, offset)) = address.get_if_unique_target() {
if pointer.targets().len() == 1 { if let Ok(position) = offset.try_to_bitvec() {
for (mem_id, offset) in pointer.targets().iter() { if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
if let Ok(position) = offset.try_to_bitvec() { mem_region.add(taint, position);
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) { } else {
mem_region.add(taint, position.clone()); let mut mem_region = MemRegion::new(address.bytesize());
} else { mem_region.add(taint, position);
let mut mem_region = MemRegion::new(address.bytesize()); self.memory_taint.insert(mem_id.clone(), mem_region);
mem_region.add(taint, position.clone());
self.memory_taint.insert(mem_id.clone(), mem_region);
}
}
} }
} else { }
for (mem_id, offset) in pointer.targets().iter() { } else {
if let Ok(position) = offset.try_to_bitvec() { for (mem_id, offset) in address.get_relative_values() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) { if let Ok(position) = offset.try_to_bitvec() {
let old_taint = mem_region.get(position.clone(), taint.bytesize()); if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
mem_region.add(old_taint.merge(&taint), position.clone()); let old_taint = mem_region.get(position.clone(), taint.bytesize());
} else { mem_region.add(old_taint.merge(&taint), position.clone());
let mut mem_region = MemRegion::new(address.bytesize()); } else {
mem_region.add(taint, position.clone()); let mut mem_region = MemRegion::new(address.bytesize());
self.memory_taint.insert(mem_id.clone(), mem_region); mem_region.add(taint, position.clone());
} self.memory_taint.insert(mem_id.clone(), mem_region);
} }
} }
} }
...@@ -214,7 +208,7 @@ impl State { ...@@ -214,7 +208,7 @@ impl State {
/// Return true if the memory object with the given ID contains a tainted value. /// 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 { 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() { for elem in mem_object.values() {
if elem.is_tainted() { if elem.is_tainted() {
return true; return true;
...@@ -234,27 +228,26 @@ impl State { ...@@ -234,27 +228,26 @@ impl State {
pi_state: &PointerInferenceState, pi_state: &PointerInferenceState,
) -> bool { ) -> bool {
use crate::analysis::pointer_inference::object::ObjectType; use crate::analysis::pointer_inference::object::ObjectType;
if let Data::Pointer(pointer) = address { for (target, offset) in address.get_relative_values() {
for (target, offset) in pointer.targets() { if let Ok(Some(ObjectType::Stack)) = pi_state.memory.get_object_type(target) {
if let Ok(Some(ObjectType::Stack)) = pi_state.memory.get_object_type(target) { // Only check if the value at the address is tainted
// Only check if the value at the address is tainted if let (Some(mem_object), Ok(target_offset)) =
if let (Some(mem_object), Ok(target_offset)) = (self.memory_taint.get(target), offset.try_to_bitvec())
(self.memory_taint.get(target), offset.try_to_bitvec()) {
{ if let Some(taint) = mem_object.get_unsized(target_offset.clone()) {
if let Some(taint) = mem_object.get_unsized(target_offset.clone()) { if taint.is_tainted() {
if taint.is_tainted() { return true;
return true;
}
} }
} }
} else { }
// Check whether the memory object contains any taint. } else {
if self.check_mem_id_for_taint(target) { // Check whether the memory object contains any taint.
return true; if self.check_mem_id_for_taint(target) {
} return true;
} }
} }
} }
false false
} }
...@@ -439,9 +432,9 @@ mod tests { ...@@ -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); let id = new_id(location);
PointerDomain::new(id, bv(offset)) DataDomain::from_target(id, bv(offset))
} }
#[test] #[test]
...@@ -453,7 +446,7 @@ mod tests { ...@@ -453,7 +446,7 @@ mod tests {
state.set_register_taint(&register("RAX"), taint.clone()); state.set_register_taint(&register("RAX"), taint.clone());
let mut other_state = State::mock(); 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); other_state.save_taint_to_memory(&address, taint);
let merged_state = state.merge(&other_state); let merged_state = state.merge(&other_state);
...@@ -466,7 +459,7 @@ mod tests { ...@@ -466,7 +459,7 @@ mod tests {
merged_state.load_taint_from_memory(&address, ByteSize::new(8)), merged_state.load_taint_from_memory(&address, ByteSize::new(8)),
taint.clone() taint.clone()
); );
let other_address = Data::Pointer(new_pointer_domain("mem", 18)); let other_address = new_pointer("mem", 18);
assert_eq!( assert_eq!(
merged_state.load_taint_from_memory(&other_address, ByteSize::new(8)), merged_state.load_taint_from_memory(&other_address, ByteSize::new(8)),
top.clone() top.clone()
......
...@@ -53,7 +53,7 @@ pub fn get_calls<'a>( ...@@ -53,7 +53,7 @@ pub fn get_calls<'a>(
let mut calls: Vec<(&str, &Tid, &str)> = Vec::new(); let mut calls: Vec<(&str, &Tid, &str)> = Vec::new();
let mut symbol_map: HashMap<&Tid, &str> = HashMap::with_capacity(dangerous_symbols.len()); let mut symbol_map: HashMap<&Tid, &str> = HashMap::with_capacity(dangerous_symbols.len());
for symbol in dangerous_symbols.iter() { 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() { for sub in subfunctions.iter() {
calls.append(&mut get_calls_to_symbols(sub, &symbol_map)); calls.append(&mut get_calls_to_symbols(sub, &symbol_map));
......
use petgraph::graph::NodeIndex; 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::analysis::pointer_inference::{Data, PointerInference as PointerInferenceComputation};
use crate::intermediate_representation::{ use crate::intermediate_representation::{
Arg, BinOpType, Bitvector, ByteSize, CallingConvention, Expression, ExternSymbol, Tid, Variable, Arg, BinOpType, Bitvector, ByteSize, CallingConvention, Expression, ExternSymbol, Tid, Variable,
...@@ -193,8 +193,7 @@ fn tainting_user_input_symbol_parameters() { ...@@ -193,8 +193,7 @@ fn tainting_user_input_symbol_parameters() {
format_string_index.insert("scanf".to_string(), 0); format_string_index.insert("scanf".to_string(), 0);
let global_address = Bitvector::from_str_radix(16, "500c").unwrap(); let global_address = Bitvector::from_str_radix(16, "500c").unwrap();
let string_address = let string_address = IntervalDomain::new(global_address.clone(), global_address).into();
DataDomain::Value(IntervalDomain::new(global_address.clone(), global_address));
let mut pi_result_state = pi_results let mut pi_result_state = pi_results
.get_node_value(call_source_node) .get_node_value(call_source_node)
...@@ -217,7 +216,7 @@ fn tainting_user_input_symbol_parameters() { ...@@ -217,7 +216,7 @@ fn tainting_user_input_symbol_parameters() {
})), })),
rhs: Box::new(Expression::Const(Bitvector::from_u64(0))), 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, &mem_image,
) )
.expect("Failed to write to address."); .expect("Failed to write to address.");
...@@ -294,7 +293,7 @@ fn processing_scanf() { ...@@ -294,7 +293,7 @@ fn processing_scanf() {
})), })),
rhs: Box::new(Expression::Const(Bitvector::from_u64(0))), 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, context.runtime_memory_image,
) )
.expect("Failed to write to address."); .expect("Failed to write to address.");
...@@ -349,7 +348,7 @@ fn processing_sscanf() { ...@@ -349,7 +348,7 @@ fn processing_sscanf() {
})), })),
rhs: Box::new(Expression::Const(Bitvector::from_u64(0))), 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, context.runtime_memory_image,
) )
.expect("Failed to write to address."); .expect("Failed to write to address.");
...@@ -418,7 +417,7 @@ fn tainting_function_arguments() { ...@@ -418,7 +417,7 @@ fn tainting_function_arguments() {
})), })),
rhs: Box::new(Expression::Const(Bitvector::from_u64(24))), 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, context.runtime_memory_image,
) )
.expect("Failed to write to address."); .expect("Failed to write to address.");
...@@ -431,7 +430,7 @@ fn tainting_function_arguments() { ...@@ -431,7 +430,7 @@ fn tainting_function_arguments() {
); );
assert!(setup.state.address_points_to_taint( 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 &setup.pi_state
)); ));
} }
......
...@@ -4,7 +4,7 @@ use super::*; ...@@ -4,7 +4,7 @@ use super::*;
use crate::analysis::{backward_interprocedural_fixpoint::Context as BackwardContext, graph::Node}; use crate::analysis::{backward_interprocedural_fixpoint::Context as BackwardContext, graph::Node};
use crate::{ use crate::{
abstract_domain::{DataDomain, PointerDomain, SizedDomain}, abstract_domain::{DataDomain, SizedDomain},
analysis::pointer_inference::{Data, State as PointerInferenceState, ValueDomain}, analysis::pointer_inference::{Data, State as PointerInferenceState, ValueDomain},
intermediate_representation::{Expression, Variable}, intermediate_representation::{Expression, Variable},
}; };
...@@ -118,8 +118,8 @@ impl Setup { ...@@ -118,8 +118,8 @@ impl Setup {
state, state,
pi_state, pi_state,
taint_source, taint_source,
base_eight_offset: Data::Pointer(PointerDomain::new(stack_id.clone(), bv(-8))), base_eight_offset: Data::from_target(stack_id.clone(), bv(-8)),
base_sixteen_offset: Data::Pointer(PointerDomain::new(stack_id.clone(), bv(-16))), base_sixteen_offset: Data::from_target(stack_id.clone(), bv(-16)),
} }
} }
} }
...@@ -339,7 +339,7 @@ fn creating_pi_def_map() { ...@@ -339,7 +339,7 @@ fn creating_pi_def_map() {
} else if *def_tid == def2 { } else if *def_tid == def2 {
assert_eq!( assert_eq!(
pi_state.get_register(&rdi_reg), 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() { ...@@ -568,10 +568,8 @@ fn handling_assign_and_load() {
new_state = context.update_def(&new_state, &mock_assign_stack).unwrap(); 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.get_register_taint(&r9_reg), None);
assert_eq!( assert_eq!(
new_state.address_points_to_taint( new_state
Data::Pointer(PointerDomain::new(stack_id.clone(), bv(0))), .address_points_to_taint(Data::from_target(stack_id.clone(), bv(0)), &setup.pi_state),
&setup.pi_state
),
true true
); );
...@@ -653,10 +651,8 @@ fn updating_def() { ...@@ -653,10 +651,8 @@ fn updating_def() {
new_state = context.update_def(&new_state, &mock_assign_stack).unwrap(); 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.get_register_taint(&r9_reg), None);
assert_eq!( assert_eq!(
new_state.address_points_to_taint( new_state
Data::Pointer(PointerDomain::new(stack_id.clone(), bv(0))), .address_points_to_taint(Data::from_target(stack_id.clone(), bv(0)), &setup.pi_state),
&setup.pi_state
),
true true
); );
......
...@@ -132,30 +132,26 @@ impl State { ...@@ -132,30 +132,26 @@ impl State {
/// we merge the taint object with the object at the targets, /// we merge the taint object with the object at the targets,
/// possibly tainting all possible targets. /// possibly tainting all possible targets.
pub fn save_taint_to_memory(&mut self, address: &Data, taint: Taint) { pub fn save_taint_to_memory(&mut self, address: &Data, taint: Taint) {
if let Data::Pointer(pointer) = address { if let Some((mem_id, offset)) = address.get_if_unique_target() {
if pointer.targets().len() == 1 { if let Ok(position) = offset.try_to_bitvec() {
for (mem_id, offset) in pointer.targets().iter() { if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
if let Ok(position) = offset.try_to_bitvec() { mem_region.add(taint, position);
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) { } else {
mem_region.add(taint, position.clone()); let mut mem_region = MemRegion::new(address.bytesize());
} else { mem_region.add(taint, position);
let mut mem_region = MemRegion::new(address.bytesize()); self.memory_taint.insert(mem_id.clone(), mem_region);
mem_region.add(taint, position.clone());
self.memory_taint.insert(mem_id.clone(), mem_region);
}
}
} }
} else { }
for (mem_id, offset) in pointer.targets().iter() { } else {
if let Ok(position) = offset.try_to_bitvec() { for (mem_id, offset) in address.get_relative_values() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) { if let Ok(position) = offset.try_to_bitvec() {
let old_taint = mem_region.get(position.clone(), taint.bytesize()); if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
mem_region.add(old_taint.merge(&taint), position.clone()); let old_taint = mem_region.get(position.clone(), taint.bytesize());
} else { mem_region.add(old_taint.merge(&taint), position.clone());
let mut mem_region = MemRegion::new(address.bytesize()); } else {
mem_region.add(taint, position.clone()); let mut mem_region = MemRegion::new(address.bytesize());
self.memory_taint.insert(mem_id.clone(), mem_region); mem_region.add(taint, position.clone());
} self.memory_taint.insert(mem_id.clone(), mem_region);
} }
} }
} }
...@@ -285,7 +281,7 @@ impl State { ...@@ -285,7 +281,7 @@ impl State {
if let Some(pid_map) = self.pi_def_map.as_ref() { if let Some(pid_map) = self.pi_def_map.as_ref() {
if let Some(pi_state) = pid_map.get(def_tid) { if let Some(pi_state) = pid_map.get(def_tid) {
let address = pi_state.eval(target); 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( self.taint_def_input_register(
value, value,
stack_pointer_register, stack_pointer_register,
...@@ -361,15 +357,12 @@ impl State { ...@@ -361,15 +357,12 @@ impl State {
/// Remove the taint in the specified memory regions at the specified offsets. /// Remove the taint in the specified memory regions at the specified offsets.
pub fn remove_mem_taint_at_target(&mut self, address: &Data) { pub fn remove_mem_taint_at_target(&mut self, address: &Data) {
if let Data::Pointer(pointer) = address { for (mem_id, offset) in address.get_relative_values() {
for (mem_id, offset) in pointer.targets().iter() { if let (Some(mem_region), Ok(position)) =
if let (Some(mem_region), Ok(position)) = (self.memory_taint.get_mut(mem_id), offset.try_to_bitvec())
(self.memory_taint.get_mut(mem_id), offset.try_to_bitvec()) {
{ if let Some(taint) = mem_region.get_unsized(position.clone()) {
if let Some(taint) = mem_region.get_unsized(position.clone()) { mem_region.remove(position, Bitvector::from_u64(u64::from(taint.bytesize())));
mem_region
.remove(position, Bitvector::from_u64(u64::from(taint.bytesize())));
}
} }
} }
} }
...@@ -391,7 +384,7 @@ impl State { ...@@ -391,7 +384,7 @@ impl State {
/// Return true if the memory object with the given ID contains a tainted value. /// 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 { 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() { for elem in mem_object.values() {
if elem.is_tainted() { if elem.is_tainted() {
return true; return true;
...@@ -407,24 +400,22 @@ impl State { ...@@ -407,24 +400,22 @@ impl State {
/// return true if the memory object contains any tainted value (at any position). /// 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 { pub fn address_points_to_taint(&self, address: Data, pi_state: &PointerInferenceState) -> bool {
use crate::analysis::pointer_inference::object::ObjectType; use crate::analysis::pointer_inference::object::ObjectType;
if let Data::Pointer(pointer) = address { for (target, offset) in address.get_relative_values() {
for (target, offset) in pointer.targets() { if let Ok(Some(ObjectType::Stack)) = pi_state.memory.get_object_type(target) {
if let Ok(Some(ObjectType::Stack)) = pi_state.memory.get_object_type(target) { // Only check if the value at the address is tainted
// Only check if the value at the address is tainted if let (Some(mem_object), Ok(target_offset)) =
if let (Some(mem_object), Ok(target_offset)) = (self.memory_taint.get(target), offset.try_to_bitvec())
(self.memory_taint.get(target), offset.try_to_bitvec()) {
{ if let Some(taint) = mem_object.get_unsized(target_offset.clone()) {
if let Some(taint) = mem_object.get_unsized(target_offset.clone()) { if taint.is_tainted() {
if taint.is_tainted() { return true;
return true;
}
} }
} }
} else { }
// Check whether the memory object contains any taint. } else {
if self.check_mem_id_for_taint(target) { // Check whether the memory object contains any taint.
return true; if self.check_mem_id_for_taint(target) {
} return true;
} }
} }
} }
...@@ -445,7 +436,7 @@ impl State { ...@@ -445,7 +436,7 @@ impl State {
let taints = self.register_taint.clone(); let taints = self.register_taint.clone();
for (register, _) in taints.iter() { for (register, _) in taints.iter() {
if register_names.get(&register.name).is_none() { 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::analysis::pointer_inference::ValueDomain;
use crate::{ use crate::{abstract_domain::DataDomain, intermediate_representation::CastOpType};
abstract_domain::{DataDomain, PointerDomain},
intermediate_representation::CastOpType,
};
use super::*; use super::*;
...@@ -98,9 +95,9 @@ impl Setup { ...@@ -98,9 +95,9 @@ impl Setup {
constant: String::from("Hello World"), constant: String::from("Hello World"),
constant_address: Bitvector::from_u32(12290), constant_address: Bitvector::from_u32(12290),
def_tid: Tid::new("def"), def_tid: Tid::new("def"),
stack_pointer: Data::Pointer(PointerDomain::new(stack_id.clone(), bv(0))), stack_pointer: Data::from_target(stack_id.clone(), bv(0)),
base_eight_offset: Data::Pointer(PointerDomain::new(stack_id.clone(), bv(-8))), base_eight_offset: Data::from_target(stack_id.clone(), bv(-8)),
base_sixteen_offset: Data::Pointer(PointerDomain::new(stack_id.clone(), bv(-16))), base_sixteen_offset: Data::from_target(stack_id.clone(), bv(-16)),
} }
} }
} }
......
...@@ -37,7 +37,7 @@ pub static CWE_MODULE: crate::CweModule = crate::CweModule { ...@@ -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. /// 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> { 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() { if !calls.is_empty() {
return generate_cwe_warning(&calls); return generate_cwe_warning(&calls);
} }
......
...@@ -52,7 +52,7 @@ impl Term<Blk> { ...@@ -52,7 +52,7 @@ impl Term<Blk> {
.indirect_jmp_targets .indirect_jmp_targets
.iter() .iter()
.filter_map(|target| { .filter_map(|target| {
if known_block_tids.get(&target).is_some() { if known_block_tids.get(target).is_some() {
Some(target.clone()) Some(target.clone())
} else { } else {
let error_msg = let error_msg =
......
...@@ -131,7 +131,7 @@ impl Variable { ...@@ -131,7 +131,7 @@ impl Variable {
match (&self.name, &self.value) { match (&self.name, &self.value) {
(None, Some(hex_value)) => { (None, Some(hex_value)) => {
assert!(u64::from(self.size) <= 8); 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() val.into()
} }
_ => panic!(), _ => panic!(),
......
...@@ -514,12 +514,11 @@ impl ExternSymbol { ...@@ -514,12 +514,11 @@ impl ExternSymbol {
let mut symbol = self.clone(); let mut symbol = self.clone();
let mut parameters = Vec::new(); let mut parameters = Vec::new();
let mut return_values = Vec::new(); let mut return_values = Vec::new();
let input_args: Vec<&Arg> = symbol let symbol_has_input_args = symbol
.arguments .arguments
.iter() .iter()
.filter(|arg| matches!(arg.intent, ArgIntent::INPUT)) .any(|arg| matches!(arg.intent, ArgIntent::INPUT));
.collect(); if symbol.is_scanf_or_sscanf() && !symbol_has_input_args {
if symbol.is_scanf_or_sscanf() && input_args.is_empty() {
symbol.create_format_string_args_for_scanf_and_sscanf( symbol.create_format_string_args_for_scanf_and_sscanf(
conventions, conventions,
stack_pointer, stack_pointer,
......
...@@ -7,7 +7,7 @@ use crate::{intermediate_representation::Datatype, prelude::*}; ...@@ -7,7 +7,7 @@ use crate::{intermediate_representation::Datatype, prelude::*};
use regex::Regex; use regex::Regex;
use crate::{ use crate::{
abstract_domain::{DataDomain, IntervalDomain, TryToBitvec}, abstract_domain::{IntervalDomain, TryToBitvec},
analysis::pointer_inference::State as PointerInferenceState, analysis::pointer_inference::State as PointerInferenceState,
intermediate_representation::{ intermediate_representation::{
Arg, ByteSize, CallingConvention, DatatypeProperties, ExternSymbol, Project, Variable, Arg, ByteSize, CallingConvention, DatatypeProperties, ExternSymbol, Project, Variable,
...@@ -37,13 +37,13 @@ pub fn get_input_format_string( ...@@ -37,13 +37,13 @@ pub fn get_input_format_string(
runtime_memory_image: &RuntimeMemoryImage, runtime_memory_image: &RuntimeMemoryImage,
) -> Result<String, Error> { ) -> Result<String, Error> {
if let Some(format_string) = extern_symbol.parameters.get(format_string_index) { if let Some(format_string) = extern_symbol.parameters.get(format_string_index) {
if let Ok(DataDomain::Value(address)) = pi_state.eval_parameter_arg( if let Ok(Some(address)) = pi_state
format_string, .eval_parameter_arg(format_string, stack_pointer_register, runtime_memory_image)
&stack_pointer_register, .as_ref()
runtime_memory_image, .map(|param| param.get_if_absolute_value())
) { {
return parse_format_string_destination_and_return_content( return parse_format_string_destination_and_return_content(
address, address.clone(),
runtime_memory_image, runtime_memory_image,
); );
} }
......
...@@ -24,7 +24,7 @@ fn test_get_variable_parameters() { ...@@ -24,7 +24,7 @@ fn test_get_variable_parameters() {
let global_address = Bitvector::from_str_radix(16, "5000").unwrap(); let global_address = Bitvector::from_str_radix(16, "5000").unwrap();
pi_state.set_register( pi_state.set_register(
&Variable::mock("RDI", 8 as u64), &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 mut project = Project::mock_empty();
let cconv = CallingConvention::mock_with_parameter_registers( let cconv = CallingConvention::mock_with_parameter_registers(
...@@ -66,7 +66,7 @@ fn test_get_variable_parameters() { ...@@ -66,7 +66,7 @@ fn test_get_variable_parameters() {
let global_address = Bitvector::from_str_radix(16, "500c").unwrap(); let global_address = Bitvector::from_str_radix(16, "500c").unwrap();
pi_state.set_register( pi_state.set_register(
&Variable::mock("RDI", 8 as u64), &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!( assert_eq!(
...@@ -91,7 +91,7 @@ fn test_get_input_format_string() { ...@@ -91,7 +91,7 @@ fn test_get_input_format_string() {
let global_address = Bitvector::from_str_radix(16, "3002").unwrap(); let global_address = Bitvector::from_str_radix(16, "3002").unwrap();
pi_state.set_register( pi_state.set_register(
&Variable::mock("RSI", 8 as u64), &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!( 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