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!(),
......
...@@ -89,27 +89,17 @@ impl<'a> Context<'a> { ...@@ -89,27 +89,17 @@ impl<'a> Context<'a> {
} }
_ => Bitvector::zero(apint::BitWidth::from(self.project.get_pointer_bytesize())), _ => Bitvector::zero(apint::BitWidth::from(self.project.get_pointer_bytesize())),
}; };
match state_before_return.get_register(&self.project.stack_pointer_register) { match state_before_return
Data::Pointer(pointer) => { .get_register(&self.project.stack_pointer_register)
if pointer.targets().len() == 1 { .get_if_unique_target()
let (id, offset) = pointer.targets().iter().next().unwrap(); {
if *id != state_before_return.stack_id Some((id, offset))
|| *offset != expected_stack_pointer_offset.into() if *id == state_before_return.stack_id
&& *offset == expected_stack_pointer_offset.into() =>
{ {
Err(anyhow!("Unexpected stack register value on return"))
} else {
Ok(()) Ok(())
} }
} else { _ => Err(anyhow!("Unexpected stack register value on return")),
Err(anyhow!(
"Unexpected number of stack register targets on return"
))
}
}
Data::Top(_) => Err(anyhow!(
"Stack register value lost during function execution"
)),
Data::Value(_) => Err(anyhow!("Unexpected stack register value on return")),
} }
} }
...@@ -166,10 +156,10 @@ impl<'a> Context<'a> { ...@@ -166,10 +156,10 @@ impl<'a> Context<'a> {
} }
_ => DataDomain::new_top(address_bytesize), _ => DataDomain::new_top(address_bytesize),
}; };
match object_size { object_size
Data::Value(val) => val, .get_if_absolute_value()
_ => ValueDomain::new_top(address_bytesize), .cloned()
} .unwrap_or_else(|| ValueDomain::new_top(address_bytesize))
} }
/// Add a new abstract object and a pointer to it in the return register of an extern call. /// Add a new abstract object and a pointer to it in the return register of an extern call.
...@@ -183,7 +173,7 @@ impl<'a> Context<'a> { ...@@ -183,7 +173,7 @@ impl<'a> Context<'a> {
extern_symbol: &ExternSymbol, extern_symbol: &ExternSymbol,
) -> State { ) -> State {
let address_bytesize = self.project.get_pointer_bytesize(); let address_bytesize = self.project.get_pointer_bytesize();
let object_size = self.get_allocation_size_of_alloc_call(&state, extern_symbol); let object_size = self.get_allocation_size_of_alloc_call(state, extern_symbol);
match extern_symbol.get_unique_return_register() { match extern_symbol.get_unique_return_register() {
Ok(return_register) => { Ok(return_register) => {
...@@ -205,11 +195,11 @@ impl<'a> Context<'a> { ...@@ -205,11 +195,11 @@ impl<'a> Context<'a> {
&object_id, &object_id,
&(object_size - Bitvector::one(address_bytesize.into()).into()), &(object_size - Bitvector::one(address_bytesize.into()).into()),
); );
let pointer = PointerDomain::new( let pointer = Data::from_target(
object_id, object_id,
Bitvector::zero(apint::BitWidth::from(address_bytesize)).into(), Bitvector::zero(apint::BitWidth::from(address_bytesize)).into(),
); );
new_state.set_register(return_register, pointer.into()); new_state.set_register(return_register, pointer);
new_state new_state
} }
Err(err) => { Err(err) => {
...@@ -235,13 +225,12 @@ impl<'a> Context<'a> { ...@@ -235,13 +225,12 @@ impl<'a> Context<'a> {
let parameter_value = state.eval_parameter_arg( let parameter_value = state.eval_parameter_arg(
parameter, parameter,
&self.project.stack_pointer_register, &self.project.stack_pointer_register,
&self.runtime_memory_image, self.runtime_memory_image,
); );
match parameter_value { match parameter_value {
Ok(memory_object_pointer) => { Ok(memory_object_pointer) => {
if let Data::Pointer(pointer) = memory_object_pointer {
if let Err(possible_double_frees) = if let Err(possible_double_frees) =
new_state.mark_mem_object_as_freed(&pointer) new_state.mark_mem_object_as_freed(&memory_object_pointer)
{ {
let warning = CweWarning { let warning = CweWarning {
name: "CWE415".to_string(), name: "CWE415".to_string(),
...@@ -260,12 +249,6 @@ impl<'a> Context<'a> { ...@@ -260,12 +249,6 @@ impl<'a> Context<'a> {
}; };
let _ = self.log_collector.send(LogThreadMsg::Cwe(warning)); let _ = self.log_collector.send(LogThreadMsg::Cwe(warning));
} }
} else {
self.log_debug(
Err(anyhow!("Free on a non-pointer value called.")),
Some(&call.tid),
);
}
new_state.remove_unreferenced_objects(); new_state.remove_unreferenced_objects();
new_state new_state
} }
...@@ -294,7 +277,7 @@ impl<'a> Context<'a> { ...@@ -294,7 +277,7 @@ impl<'a> Context<'a> {
match state.eval_parameter_arg( match state.eval_parameter_arg(
parameter, parameter,
&self.project.stack_pointer_register, &self.project.stack_pointer_register,
&self.runtime_memory_image, self.runtime_memory_image,
) { ) {
Ok(value) => { Ok(value) => {
if state.memory.is_dangling_pointer(&value, true) { if state.memory.is_dangling_pointer(&value, true) {
...@@ -340,7 +323,7 @@ impl<'a> Context<'a> { ...@@ -340,7 +323,7 @@ impl<'a> Context<'a> {
match state.eval_parameter_arg( match state.eval_parameter_arg(
parameter, parameter,
&self.project.stack_pointer_register, &self.project.stack_pointer_register,
&self.runtime_memory_image, self.runtime_memory_image,
) { ) {
Ok(data) => { Ok(data) => {
if state.pointer_contains_out_of_bounds_target(&data, self.runtime_memory_image) if state.pointer_contains_out_of_bounds_target(&data, self.runtime_memory_image)
...@@ -425,7 +408,7 @@ impl<'a> Context<'a> { ...@@ -425,7 +408,7 @@ impl<'a> Context<'a> {
), ),
Some(&call.tid), Some(&call.tid),
); );
let calling_conv = extern_symbol.get_calling_convention(&self.project); let calling_conv = extern_symbol.get_calling_convention(self.project);
let mut possible_referenced_ids = BTreeSet::new(); let mut possible_referenced_ids = BTreeSet::new();
if extern_symbol.parameters.is_empty() && extern_symbol.return_values.is_empty() { if extern_symbol.parameters.is_empty() && extern_symbol.return_values.is_empty() {
// We assume here that we do not know the parameters and approximate them by all possible parameter registers. // We assume here that we do not know the parameters and approximate them by all possible parameter registers.
...@@ -437,7 +420,7 @@ impl<'a> Context<'a> { ...@@ -437,7 +420,7 @@ impl<'a> Context<'a> {
.chain(calling_conv.float_parameter_register.iter()) .chain(calling_conv.float_parameter_register.iter())
{ {
if let Some(register_value) = state.get_register_by_name(parameter_register_name) { if let Some(register_value) = state.get_register_by_name(parameter_register_name) {
possible_referenced_ids.append(&mut register_value.referenced_ids()); possible_referenced_ids.extend(register_value.referenced_ids().cloned());
} }
} }
} else { } else {
...@@ -445,9 +428,9 @@ impl<'a> Context<'a> { ...@@ -445,9 +428,9 @@ impl<'a> Context<'a> {
if let Ok(data) = state.eval_parameter_arg( if let Ok(data) = state.eval_parameter_arg(
parameter, parameter,
&self.project.stack_pointer_register, &self.project.stack_pointer_register,
&self.runtime_memory_image, self.runtime_memory_image,
) { ) {
possible_referenced_ids.append(&mut data.referenced_ids()); possible_referenced_ids.extend(data.referenced_ids().cloned());
} }
} }
} }
...@@ -484,7 +467,7 @@ impl<'a> Context<'a> { ...@@ -484,7 +467,7 @@ impl<'a> Context<'a> {
if let Some(register_value) = if let Some(register_value) =
state_before_call.get_register_by_name(parameter_register_name) state_before_call.get_register_by_name(parameter_register_name)
{ {
possible_referenced_ids.append(&mut register_value.referenced_ids()); possible_referenced_ids.extend(register_value.referenced_ids().cloned());
} }
} }
possible_referenced_ids = possible_referenced_ids =
...@@ -503,17 +486,14 @@ impl<'a> Context<'a> { ...@@ -503,17 +486,14 @@ impl<'a> Context<'a> {
/// Get the offset of the current stack pointer to the base of the current stack frame. /// Get the offset of the current stack pointer to the base of the current stack frame.
fn get_current_stack_offset(&self, state: &State) -> ValueDomain { fn get_current_stack_offset(&self, state: &State) -> ValueDomain {
if let Data::Pointer(ref stack_pointer) = if let Some((stack_id, stack_offset_domain)) = state
state.get_register(&self.project.stack_pointer_register) .get_register(&self.project.stack_pointer_register)
.get_if_unique_target()
{ {
if stack_pointer.targets().len() == 1 {
let (stack_id, stack_offset_domain) =
stack_pointer.targets().iter().next().unwrap();
if *stack_id == state.stack_id { if *stack_id == state.stack_id {
return stack_offset_domain.clone(); return stack_offset_domain.clone();
} }
} }
}
ValueDomain::new_top(self.project.stack_pointer_register.size) ValueDomain::new_top(self.project.stack_pointer_register.size)
} }
} }
......
...@@ -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")]
......
...@@ -51,9 +51,7 @@ impl State { ...@@ -51,9 +51,7 @@ impl State {
let caller_addresses: Vec<_> = self let caller_addresses: Vec<_> = self
.caller_stack_ids .caller_stack_ids
.iter() .iter()
.map(|caller_stack_id| { .map(|caller_stack_id| Data::from_target(caller_stack_id.clone(), offset.clone()))
PointerDomain::new(caller_stack_id.clone(), offset.clone()).into()
})
.collect(); .collect();
let mut result = Ok(()); let mut result = Ok(());
for address in caller_addresses { for address in caller_addresses {
...@@ -64,12 +62,9 @@ impl State { ...@@ -64,12 +62,9 @@ impl State {
// Note that this only returns the last error that was detected. // Note that this only returns the last error that was detected.
result result
} else { } else {
match self.adjust_pointer_for_read(address) { let pointer = self.adjust_pointer_for_read(address);
Data::Pointer(pointer) => { self.memory.set_value(pointer.clone(), value.clone())?;
self.memory.set_value(pointer, value.clone())?; if let Some(absolute_address) = pointer.get_absolute_value() {
Ok(())
}
Data::Value(absolute_address) => {
if let Ok(address_to_global_data) = absolute_address.try_to_bitvec() { if let Ok(address_to_global_data) = absolute_address.try_to_bitvec() {
match global_memory.is_address_writeable(&address_to_global_data) { match global_memory.is_address_writeable(&address_to_global_data) {
Ok(true) => Ok(()), Ok(true) => Ok(()),
...@@ -86,8 +81,8 @@ impl State { ...@@ -86,8 +81,8 @@ impl State {
// We assume inexactness of the algorithm instead of a possible CWE here. // We assume inexactness of the algorithm instead of a possible CWE here.
Ok(()) Ok(())
} }
} } else {
Data::Top(_) => Ok(()), Ok(())
} }
} }
} }
...@@ -122,28 +117,38 @@ impl State { ...@@ -122,28 +117,38 @@ impl State {
global_memory: &RuntimeMemoryImage, global_memory: &RuntimeMemoryImage,
) -> Result<Data, Error> { ) -> Result<Data, Error> {
let address = self.adjust_pointer_for_read(&self.eval(address)); let address = self.adjust_pointer_for_read(&self.eval(address));
match address { let mut result = if let Some(global_address) = address.get_absolute_value() {
Data::Value(global_address) => {
if let Ok(address_bitvector) = global_address.try_to_bitvec() { if let Ok(address_bitvector) = global_address.try_to_bitvec() {
if let Some(loaded_value) = global_memory.read(&address_bitvector, size)? { match global_memory.read(&address_bitvector, size) {
Ok(Data::Value(loaded_value.into())) Ok(Some(loaded_value)) => loaded_value.into(),
} else { Ok(None) => Data::new_top(size),
Ok(Data::Top(size)) Err(_) => Data::new_empty(size),
} }
} else if let Ok((start, end)) = global_address.try_to_offset_interval() { } else if let Ok((start, end)) = global_address.try_to_offset_interval() {
if global_memory if global_memory
.is_interval_readable(start as u64, end as u64 + u64::from(size))? .is_interval_readable(start as u64, end as u64 + u64::from(size))
.ok()
== Some(true)
{ {
Ok(Data::new_top(size)) Data::new_top(size)
} else { } else {
Err(anyhow!("Target address is not readable.")) Data::new_empty(size)
} }
} else { } else {
Ok(Data::new_top(size)) Data::new_top(size)
} }
} else {
Data::new_empty(size)
};
result = result.merge(&self.memory.get_value(&address, size));
if address.contains_top() {
result.set_contains_top_flag()
} }
Data::Top(_) => Ok(Data::new_top(size)), if result.is_empty() {
Data::Pointer(_) => Ok(self.memory.get_value(&address, size)?), Err(anyhow!("Could not read from address"))
} else {
Ok(result)
} }
} }
...@@ -169,14 +174,12 @@ impl State { ...@@ -169,14 +174,12 @@ impl State {
/// If the pointer contains a reference to the stack with offset >= 0, replace it with a pointer /// If the pointer contains a reference to the stack with offset >= 0, replace it with a pointer
/// pointing to all possible caller IDs. /// pointing to all possible caller IDs.
fn adjust_pointer_for_read(&self, address: &Data) -> Data { fn adjust_pointer_for_read(&self, address: &Data) -> Data {
if let Data::Pointer(pointer) = address { let mut adjusted_address = address.clone();
let mut new_targets = BTreeMap::new(); let mut new_targets = BTreeMap::new();
for (id, offset) in pointer.targets() { for (id, offset) in address.get_relative_values() {
if *id == self.stack_id { if *id == self.stack_id {
if let Ok((interval_start, interval_end)) = offset.try_to_offset_interval() { if let Ok((interval_start, interval_end)) = offset.try_to_offset_interval() {
if interval_start >= 0 if interval_start >= 0 && interval_end >= 0 && !self.caller_stack_ids.is_empty()
&& interval_end >= 0
&& !self.caller_stack_ids.is_empty()
{ {
for caller_id in self.caller_stack_ids.iter() { for caller_id in self.caller_stack_ids.iter() {
new_targets.insert(caller_id.clone(), offset.clone()); new_targets.insert(caller_id.clone(), offset.clone());
...@@ -196,17 +199,15 @@ impl State { ...@@ -196,17 +199,15 @@ impl State {
new_targets.insert(id.clone(), offset.clone()); new_targets.insert(id.clone(), offset.clone());
} }
} }
Data::Pointer(PointerDomain::with_targets(new_targets)) adjusted_address.set_relative_values(new_targets);
} else { adjusted_address
address.clone()
}
} }
/// Evaluate the value of an expression in the current state /// Evaluate the value of an expression in the current state
pub fn eval(&self, expression: &Expression) -> Data { pub fn eval(&self, expression: &Expression) -> Data {
use Expression::*; use Expression::*;
match expression { match expression {
Var(variable) => self.get_register(&variable), Var(variable) => self.get_register(variable),
Const(bitvector) => bitvector.clone().into(), Const(bitvector) => bitvector.clone().into(),
BinOp { op, lhs, rhs } => { BinOp { op, lhs, rhs } => {
if *op == BinOpType::IntXOr && lhs == rhs { if *op == BinOpType::IntXOr && lhs == rhs {
...@@ -294,19 +295,16 @@ impl State { ...@@ -294,19 +295,16 @@ impl State {
data: &Data, data: &Data,
global_data: &RuntimeMemoryImage, global_data: &RuntimeMemoryImage,
) -> bool { ) -> bool {
let data = self.adjust_pointer_for_read(data); let mut data = self.adjust_pointer_for_read(data);
matches!(data, Data::Pointer(_)) data.set_absolute_value(None); // Do not check absolute_values
&& self self.memory
.memory
.is_out_of_bounds_mem_access(&data, ByteSize::new(1), global_data) .is_out_of_bounds_mem_access(&data, ByteSize::new(1), global_data)
} }
/// Return `true` if `data` is a pointer to the current stack frame with a constant positive address, /// Return `true` if `data` is a pointer to the current stack frame with a constant positive address,
/// i.e. if it accesses a stack parameter (or the return-to address for x86) of the current function. /// i.e. if it accesses a stack parameter (or the return-to address for x86) of the current function.
pub fn is_stack_pointer_with_nonnegative_offset(&self, data: &Data) -> bool { pub fn is_stack_pointer_with_nonnegative_offset(&self, data: &Data) -> bool {
if let Data::Pointer(pointer) = data { if let Some((target, offset)) = data.get_if_unique_target() {
if pointer.targets().len() == 1 {
let (target, offset) = pointer.targets().iter().next().unwrap();
if *target == self.stack_id { if *target == self.stack_id {
if let Ok(offset_val) = offset.try_to_offset() { if let Ok(offset_val) = offset.try_to_offset() {
if offset_val >= 0 { if offset_val >= 0 {
...@@ -315,7 +313,6 @@ impl State { ...@@ -315,7 +313,6 @@ impl State {
} }
} }
} }
}
false false
} }
...@@ -327,17 +324,14 @@ impl State { ...@@ -327,17 +324,14 @@ impl State {
if self.caller_stack_ids.is_empty() { if self.caller_stack_ids.is_empty() {
return None; return None;
} }
if let Data::Pointer(pointer) = address { if let Some((id, offset)) = address.get_if_unique_target() {
match (pointer.targets().len(), pointer.targets().iter().next()) { if self.stack_id == *id {
(1, Some((id, offset))) if self.stack_id == *id => {
if let Ok((interval_start, _interval_end)) = offset.try_to_offset_interval() { if let Ok((interval_start, _interval_end)) = offset.try_to_offset_interval() {
if interval_start >= 0 { if interval_start >= 0 {
return Some(offset.clone()); return Some(offset.clone());
} }
} }
} }
_ => (),
}
} }
None None
} }
......
...@@ -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,12 +576,10 @@ impl State { ...@@ -598,12 +576,10 @@ 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 ( match (
lhs_pointer.unwrap_if_unique_target(), lhs_pointer.get_if_unique_target(),
rhs_pointer.unwrap_if_unique_target(), rhs_pointer.get_if_unique_target(),
) { ) {
(Some((lhs_id, lhs_offset)), Some((rhs_id, rhs_offset))) if lhs_id == rhs_id => { (Some((lhs_id, lhs_offset)), Some((rhs_id, rhs_offset))) if lhs_id == rhs_id => {
if !(self.memory.is_unique_object(lhs_id)?) { if !(self.memory.is_unique_object(lhs_id)?) {
...@@ -611,9 +587,9 @@ impl State { ...@@ -611,9 +587,9 @@ impl State {
return Ok(()); return Ok(());
} }
if *op == BinOpType::IntEqual { if *op == BinOpType::IntEqual {
let specialized_offset = lhs_offset.intersect(rhs_offset)?; let specialized_offset = lhs_offset.clone().intersect(rhs_offset)?;
let specialized_domain: Data = let specialized_domain: Data =
PointerDomain::new(lhs_id.clone(), specialized_offset).into(); Data::from_target(lhs_id.clone(), specialized_offset);
self.specialize_by_expression_result(lhs, specialized_domain.clone())?; self.specialize_by_expression_result(lhs, specialized_domain.clone())?;
self.specialize_by_expression_result(rhs, specialized_domain)?; self.specialize_by_expression_result(rhs, specialized_domain)?;
} else if *op == BinOpType::IntNotEqual { } else if *op == BinOpType::IntNotEqual {
...@@ -622,7 +598,7 @@ impl State { ...@@ -622,7 +598,7 @@ impl State {
lhs_offset.clone().add_not_equal_bound(&rhs_offset_bitvec)?; lhs_offset.clone().add_not_equal_bound(&rhs_offset_bitvec)?;
self.specialize_by_expression_result( self.specialize_by_expression_result(
lhs, lhs,
PointerDomain::new(lhs_id.clone(), new_lhs_offset).into(), Data::from_target(lhs_id.clone(), new_lhs_offset),
)?; )?;
} }
if let Ok(lhs_offset_bitvec) = lhs_offset.try_to_bitvec() { if let Ok(lhs_offset_bitvec) = lhs_offset.try_to_bitvec() {
...@@ -630,14 +606,13 @@ impl State { ...@@ -630,14 +606,13 @@ impl State {
rhs_offset.clone().add_not_equal_bound(&lhs_offset_bitvec)?; rhs_offset.clone().add_not_equal_bound(&lhs_offset_bitvec)?;
self.specialize_by_expression_result( self.specialize_by_expression_result(
rhs, rhs,
PointerDomain::new(rhs_id.clone(), new_rhs_offset).into(), 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(()) 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,15 +150,13 @@ impl State { ...@@ -150,15 +150,13 @@ 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,21 +166,18 @@ impl State { ...@@ -168,21 +166,18 @@ 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 {
for (mem_id, offset) in pointer.targets().iter() {
if let Ok(position) = offset.try_to_bitvec() { if let Ok(position) = offset.try_to_bitvec() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) { if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
mem_region.add(taint, position.clone()); mem_region.add(taint, position);
} else { } else {
let mut mem_region = MemRegion::new(address.bytesize()); let mut mem_region = MemRegion::new(address.bytesize());
mem_region.add(taint, position.clone()); mem_region.add(taint, position);
self.memory_taint.insert(mem_id.clone(), mem_region); self.memory_taint.insert(mem_id.clone(), mem_region);
} }
} }
}
} else { } else {
for (mem_id, offset) in pointer.targets().iter() { for (mem_id, offset) in address.get_relative_values() {
if let Ok(position) = offset.try_to_bitvec() { if let Ok(position) = offset.try_to_bitvec() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) { if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
let old_taint = mem_region.get(position.clone(), taint.bytesize()); let old_taint = mem_region.get(position.clone(), taint.bytesize());
...@@ -196,7 +191,6 @@ impl State { ...@@ -196,7 +191,6 @@ impl State {
} }
} }
} }
}
/// Remove all knowledge about taints contained in memory objects. /// Remove all knowledge about taints contained in memory objects.
pub fn remove_all_memory_taints(&mut self) { pub fn remove_all_memory_taints(&mut self) {
...@@ -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,8 +228,7 @@ impl State { ...@@ -234,8 +228,7 @@ 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)) =
...@@ -254,7 +247,7 @@ impl State { ...@@ -254,7 +247,7 @@ impl State {
} }
} }
} }
}
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,21 +132,18 @@ impl State { ...@@ -132,21 +132,18 @@ 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 {
for (mem_id, offset) in pointer.targets().iter() {
if let Ok(position) = offset.try_to_bitvec() { if let Ok(position) = offset.try_to_bitvec() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) { if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
mem_region.add(taint, position.clone()); mem_region.add(taint, position);
} else { } else {
let mut mem_region = MemRegion::new(address.bytesize()); let mut mem_region = MemRegion::new(address.bytesize());
mem_region.add(taint, position.clone()); mem_region.add(taint, position);
self.memory_taint.insert(mem_id.clone(), mem_region); self.memory_taint.insert(mem_id.clone(), mem_region);
} }
} }
}
} else { } else {
for (mem_id, offset) in pointer.targets().iter() { for (mem_id, offset) in address.get_relative_values() {
if let Ok(position) = offset.try_to_bitvec() { if let Ok(position) = offset.try_to_bitvec() {
if let Some(mem_region) = self.memory_taint.get_mut(mem_id) { if let Some(mem_region) = self.memory_taint.get_mut(mem_id) {
let old_taint = mem_region.get(position.clone(), taint.bytesize()); let old_taint = mem_region.get(position.clone(), taint.bytesize());
...@@ -160,7 +157,6 @@ impl State { ...@@ -160,7 +157,6 @@ impl State {
} }
} }
} }
}
/// Returns the sub of the currently analysed nodes. /// Returns the sub of the currently analysed nodes.
pub fn get_current_sub(&self) -> &Option<Term<Sub>> { pub fn get_current_sub(&self) -> &Option<Term<Sub>> {
...@@ -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 mem_region.remove(position, Bitvector::from_u64(u64::from(taint.bytesize())));
.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,8 +400,7 @@ impl State { ...@@ -407,8 +400,7 @@ 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)) =
...@@ -427,7 +419,6 @@ impl State { ...@@ -427,7 +419,6 @@ impl State {
} }
} }
} }
}
false false
} }
...@@ -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