Unverified Commit a8574343 by van den Bosch Committed by GitHub

Macros and test changes (#380)

parent 2c766f5a
...@@ -79,12 +79,12 @@ fn main() -> Result<(), Error> { ...@@ -79,12 +79,12 @@ fn main() -> Result<(), Error> {
/// Return `Ok(file_path)` only if `file_path` points to an existing file. /// Return `Ok(file_path)` only if `file_path` points to an existing file.
fn check_file_existence(file_path: &str) -> Result<String, String> { fn check_file_existence(file_path: &str) -> Result<String, String> {
if std::fs::metadata(file_path) if std::fs::metadata(file_path)
.map_err(|err| format!("{}", err))? .map_err(|err| format!("{err}"))?
.is_file() .is_file()
{ {
Ok(file_path.to_string()) Ok(file_path.to_string())
} else { } else {
Err(format!("{} is not a file.", file_path)) Err(format!("{file_path} is not a file."))
} }
} }
...@@ -95,7 +95,7 @@ fn run_with_ghidra(args: &CmdlineArgs) -> Result<(), Error> { ...@@ -95,7 +95,7 @@ fn run_with_ghidra(args: &CmdlineArgs) -> Result<(), Error> {
// Only print the module versions and then quit. // Only print the module versions and then quit.
println!("[cwe_checker] module_versions:"); println!("[cwe_checker] module_versions:");
for module in modules.iter() { for module in modules.iter() {
println!("{}", module); println!("{module}");
} }
return Ok(()); return Ok(());
} }
...@@ -235,7 +235,7 @@ fn filter_modules_for_partial_run( ...@@ -235,7 +235,7 @@ fn filter_modules_for_partial_run(
} else if module_name.is_empty() { } else if module_name.is_empty() {
None None
} else { } else {
panic!("Error: {} is not a valid module name.", module_name) panic!("Error: {module_name} is not a valid module name.")
} }
}) })
.collect(); .collect();
......
...@@ -180,11 +180,12 @@ impl std::fmt::Display for BitvectorDomain { ...@@ -180,11 +180,12 @@ impl std::fmt::Display for BitvectorDomain {
} }
#[cfg(test)] #[cfg(test)]
mod tests { pub mod tests {
use super::*; use super::*;
use crate::bitvec;
fn bv(value: i64) -> BitvectorDomain { fn bv(value: i64) -> BitvectorDomain {
BitvectorDomain::Value(Bitvector::from_i64(value)) bitvec!(format!("{}:8", value)).into()
} }
#[test] #[test]
...@@ -211,11 +212,11 @@ mod tests { ...@@ -211,11 +212,11 @@ mod tests {
assert_eq!( assert_eq!(
sixteen.bin_op(IntEqual, &bv(16)), sixteen.bin_op(IntEqual, &bv(16)),
BitvectorDomain::Value(Bitvector::from_u8(true as u8)) BitvectorDomain::Value(bitvec!(format!("{}:1", true as u8)))
); );
assert_eq!( assert_eq!(
sixteen.bin_op(IntNotEqual, &bv(16)), sixteen.bin_op(IntNotEqual, &bv(16)),
BitvectorDomain::Value(Bitvector::from_u8(false as u8)) BitvectorDomain::Value(bitvec!(format!("{}:1", false as u8)))
); );
assert_eq!(sixteen.un_op(Int2Comp), bv(-16)); assert_eq!(sixteen.un_op(Int2Comp), bv(-16));
...@@ -223,27 +224,26 @@ mod tests { ...@@ -223,27 +224,26 @@ mod tests {
assert_eq!( assert_eq!(
sixteen.subpiece(ByteSize::new(0), ByteSize::new(4)), sixteen.subpiece(ByteSize::new(0), ByteSize::new(4)),
BitvectorDomain::Value(Bitvector::from_i32(16)) BitvectorDomain::Value(bitvec!("16:4"))
); );
assert_eq!( assert_eq!(
sixteen.subpiece(ByteSize::new(4), ByteSize::new(4)), sixteen.subpiece(ByteSize::new(4), ByteSize::new(4)),
BitvectorDomain::Value(Bitvector::from_i32(0)) BitvectorDomain::Value(bitvec!("0:4"))
); );
assert_eq!( assert_eq!(
BitvectorDomain::Value(Bitvector::from_i32(2)), BitvectorDomain::Value(bitvec!("2:4")),
BitvectorDomain::Value(Bitvector::from_i64(2 << 32)) bv(2 << 32).subpiece(ByteSize::new(4), ByteSize::new(4))
.subpiece(ByteSize::new(4), ByteSize::new(4))
); );
assert_eq!( assert_eq!(
BitvectorDomain::Value(Bitvector::from_i32(-1)) BitvectorDomain::Value(bitvec!("-1:4"))
.bin_op(Piece, &BitvectorDomain::Value(Bitvector::from_i32(-1))), .bin_op(Piece, &BitvectorDomain::Value(bitvec!("-1:4"))),
bv(-1) bv(-1)
); );
assert_eq!( assert_eq!(
BitvectorDomain::Value(Bitvector::from_i32(-1)).cast(PopCount, ByteSize::new(8)), BitvectorDomain::Value(bitvec!("-1:4")).cast(PopCount, ByteSize::new(8)),
bv(32) bv(32)
) )
} }
...@@ -262,26 +262,14 @@ mod tests { ...@@ -262,26 +262,14 @@ mod tests {
#[test] #[test]
fn arshift() { fn arshift() {
use BinOpType::IntSRight; use BinOpType::IntSRight;
let positive_x = BitvectorDomain::Value(Bitvector::from_i64(31)); let positive_x = bv(31);
let negative_x = BitvectorDomain::Value(Bitvector::from_i64(-31)); let negative_x = bv(-31);
let shift_3 = BitvectorDomain::Value(Bitvector::from_u8(3)); let shift_3 = BitvectorDomain::Value(bitvec!("3:1"));
let shift_70 = BitvectorDomain::Value(Bitvector::from_u8(70)); let shift_70 = BitvectorDomain::Value(bitvec!("70:1"));
assert_eq!( assert_eq!(positive_x.bin_op(IntSRight, &shift_3), bv(3));
positive_x.bin_op(IntSRight, &shift_3), assert_eq!(positive_x.bin_op(IntSRight, &shift_70), bv(0));
BitvectorDomain::Value(Bitvector::from_i64(3)) assert_eq!(negative_x.bin_op(IntSRight, &shift_3), bv(-4));
); assert_eq!(negative_x.bin_op(IntSRight, &shift_70), bv(-1));
assert_eq!(
positive_x.bin_op(IntSRight, &shift_70),
BitvectorDomain::Value(Bitvector::from_i64(0))
);
assert_eq!(
negative_x.bin_op(IntSRight, &shift_3),
BitvectorDomain::Value(Bitvector::from_i64(-4))
);
assert_eq!(
negative_x.bin_op(IntSRight, &shift_70),
BitvectorDomain::Value(Bitvector::from_i64(-1))
);
} }
#[test] #[test]
......
...@@ -197,7 +197,7 @@ impl fmt::Display for BricksDomain { ...@@ -197,7 +197,7 @@ impl fmt::Display for BricksDomain {
BricksDomain::Value(brick_domains) => { BricksDomain::Value(brick_domains) => {
write!(f, "Bricks: ")?; write!(f, "Bricks: ")?;
for brick_domain in brick_domains.iter() { for brick_domain in brick_domains.iter() {
write!(f, "{} ", brick_domain)?; write!(f, "{brick_domain} ")?;
} }
Ok(()) Ok(())
......
...@@ -169,7 +169,7 @@ impl fmt::Display for CharacterInclusionDomain { ...@@ -169,7 +169,7 @@ impl fmt::Display for CharacterInclusionDomain {
match self { match self {
CharacterInclusionDomain::Top => write!(f, "Top"), CharacterInclusionDomain::Top => write!(f, "Top"),
CharacterInclusionDomain::Value((certain_set, possible_set)) => { CharacterInclusionDomain::Value((certain_set, possible_set)) => {
write!(f, "Certain: {}, Possible: {}", certain_set, possible_set) write!(f, "Certain: {certain_set}, Possible: {possible_set}")
} }
} }
} }
...@@ -243,7 +243,7 @@ impl fmt::Display for CharacterSet { ...@@ -243,7 +243,7 @@ impl fmt::Display for CharacterSet {
match self { match self {
CharacterSet::Top => write!(f, "Top"), CharacterSet::Top => write!(f, "Top"),
CharacterSet::Value(char_set) => { CharacterSet::Value(char_set) => {
write!(f, "{:?}", char_set) write!(f, "{char_set:?}")
} }
} }
} }
......
...@@ -215,8 +215,8 @@ impl<T: RegisterDomain + Display> DataDomain<T> { ...@@ -215,8 +215,8 @@ impl<T: RegisterDomain + Display> DataDomain<T> {
if !self.relative_values.is_empty() { if !self.relative_values.is_empty() {
let target_iter = self.relative_values.iter().map(|(id, offset)| { let target_iter = self.relative_values.iter().map(|(id, offset)| {
( (
format!("{}", id), format!("{id}"),
serde_json::Value::String(format!("{}", offset)), serde_json::Value::String(format!("{offset}")),
) )
}); });
let targets = serde_json::Value::Object(target_iter.collect()); let targets = serde_json::Value::Object(target_iter.collect());
...@@ -226,8 +226,7 @@ impl<T: RegisterDomain + Display> DataDomain<T> { ...@@ -226,8 +226,7 @@ impl<T: RegisterDomain + Display> DataDomain<T> {
} }
if let Some(absolute_value) = &self.absolute_value { if let Some(absolute_value) = &self.absolute_value {
values.push(serde_json::Value::String(format!( values.push(serde_json::Value::String(format!(
"Value: {}", "Value: {absolute_value}"
absolute_value
))); )));
} }
if self.contains_top_values { if self.contains_top_values {
...@@ -248,6 +247,7 @@ impl<T: RegisterDomain + Display> DataDomain<T> { ...@@ -248,6 +247,7 @@ impl<T: RegisterDomain + Display> DataDomain<T> {
mod tests { mod tests {
use super::super::*; use super::super::*;
use super::*; use super::*;
use crate::{bitvec, variable};
impl<T: RegisterDomain> DataDomain<T> { impl<T: RegisterDomain> DataDomain<T> {
/// Return a new domain representing a set of relative values. /// Return a new domain representing a set of relative values.
...@@ -267,13 +267,13 @@ mod tests { ...@@ -267,13 +267,13 @@ mod tests {
} }
fn bv(value: i64) -> BitvectorDomain { fn bv(value: i64) -> BitvectorDomain {
BitvectorDomain::Value(Bitvector::from_i64(value)) bitvec!(format!("{}:8", value)).into()
} }
fn new_id(name: &str) -> AbstractIdentifier { fn new_id(name: &str) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new("time0"), Tid::new("time0"),
AbstractLocation::Register(Variable::mock(name, ByteSize::new(8))), AbstractLocation::Register(variable!(format!("{}:8", name))),
) )
} }
......
...@@ -244,18 +244,18 @@ impl<T: RegisterDomain> std::ops::Sub for DataDomain<T> { ...@@ -244,18 +244,18 @@ impl<T: RegisterDomain> std::ops::Sub for DataDomain<T> {
mod tests { mod tests {
use super::super::*; use super::super::*;
use super::*; use super::*;
use crate::abstract_domain::*; use crate::{abstract_domain::*, bitvec, variable};
type Data = DataDomain<BitvectorDomain>; type Data = DataDomain<BitvectorDomain>;
fn bv(value: i64) -> BitvectorDomain { fn bv(value: i64) -> BitvectorDomain {
BitvectorDomain::Value(Bitvector::from_i64(value)) BitvectorDomain::Value(bitvec!(format!("{}:8", value)))
} }
fn new_id(name: &str) -> AbstractIdentifier { fn new_id(name: &str) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new("time0"), Tid::new("time0"),
AbstractLocation::Register(Variable::mock(name, ByteSize::new(8))), AbstractLocation::Register(variable!(format!("{}:8", name))),
) )
} }
...@@ -313,7 +313,7 @@ mod tests { ...@@ -313,7 +313,7 @@ mod tests {
assert_eq!( assert_eq!(
three.subpiece(ByteSize::new(0), ByteSize::new(4)), three.subpiece(ByteSize::new(0), ByteSize::new(4)),
BitvectorDomain::Value(Bitvector::from_i32(3)).into() BitvectorDomain::Value(bitvec!("3:4")).into()
); );
assert_eq!( assert_eq!(
...@@ -321,8 +321,8 @@ mod tests { ...@@ -321,8 +321,8 @@ mod tests {
ByteSize::new(16) ByteSize::new(16)
); );
let one: Data = BitvectorDomain::Value(Bitvector::from_i32(1)).into(); let one: Data = BitvectorDomain::Value(bitvec!("1:4")).into();
let two: Data = BitvectorDomain::Value(Bitvector::from_i32(2)).into(); let two: Data = BitvectorDomain::Value(bitvec!("2:4")).into();
let concat = new_value((1 << 32) + 2); let concat = new_value((1 << 32) + 2);
assert_eq!(one.bin_op(Piece, &two), concat); assert_eq!(one.bin_op(Piece, &two), concat);
} }
......
...@@ -118,12 +118,12 @@ impl<T: SpecializeByConditional + RegisterDomain> SpecializeByConditional for Da ...@@ -118,12 +118,12 @@ impl<T: SpecializeByConditional + RegisterDomain> SpecializeByConditional for Da
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::abstract_domain::*; use crate::{abstract_domain::*, variable};
fn new_id(name: &str) -> AbstractIdentifier { fn new_id(name: &str) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new("time0"), Tid::new("time0"),
AbstractLocation::Register(Variable::mock(name, ByteSize::new(8))), AbstractLocation::Register(variable!(format!("{}:8", name))),
) )
} }
......
...@@ -109,18 +109,18 @@ impl<T: RegisterDomain + TryToInterval> TryToInterval for DataDomain<T> { ...@@ -109,18 +109,18 @@ impl<T: RegisterDomain + TryToInterval> TryToInterval for DataDomain<T> {
mod tests { mod tests {
use super::super::*; use super::super::*;
use super::*; use super::*;
use crate::abstract_domain::*; use crate::{abstract_domain::*, bitvec, variable};
type Data = DataDomain<BitvectorDomain>; type Data = DataDomain<BitvectorDomain>;
fn bv(value: i64) -> BitvectorDomain { fn bv(value: i64) -> BitvectorDomain {
BitvectorDomain::Value(Bitvector::from_i64(value)) BitvectorDomain::Value(bitvec!(format!("{}:8", value)))
} }
fn new_id(name: &str) -> AbstractIdentifier { fn new_id(name: &str) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new("time0"), Tid::new("time0"),
AbstractLocation::Register(Variable::mock(name, ByteSize::new(8))), AbstractLocation::Register(variable!(format!("{}:8", name))),
) )
} }
......
...@@ -217,23 +217,22 @@ impl<K: Ord + Clone, V: AbstractDomain + HasTop> MapMergeStrategy<K, V> for Merg ...@@ -217,23 +217,22 @@ impl<K: Ord + Clone, V: AbstractDomain + HasTop> MapMergeStrategy<K, V> for Merg
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::bitvec;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use crate::intermediate_representation::Bitvector;
#[test] #[test]
fn test_merge_strategies() { fn test_merge_strategies() {
let map_left: BTreeMap<u64, DataDomain<BitvectorDomain>> = [ let map_left: BTreeMap<u64, DataDomain<BitvectorDomain>> = [
(0u64, Bitvector::from_i64(0).into()), (0u64, bitvec!("0:8").into()),
(1u64, Bitvector::from_i64(0).into()), (1u64, bitvec!("0:8").into()),
(5u64, DataDomain::new_top(ByteSize::new(8))), (5u64, DataDomain::new_top(ByteSize::new(8))),
] ]
.iter() .iter()
.cloned() .cloned()
.collect(); .collect();
let map_right: BTreeMap<u64, DataDomain<BitvectorDomain>> = [ let map_right: BTreeMap<u64, DataDomain<BitvectorDomain>> = [
(1u64, Bitvector::from_i64(1).into()), (1u64, bitvec!("1:8").into()),
(2u64, Bitvector::from_i64(1).into()), (2u64, bitvec!("1:8").into()),
(5u64, DataDomain::new_top(ByteSize::new(8))), (5u64, DataDomain::new_top(ByteSize::new(8))),
] ]
.iter() .iter()
...@@ -244,12 +243,12 @@ mod tests { ...@@ -244,12 +243,12 @@ mod tests {
let domain_map_left: DomainMap<_, _, UnionMergeStrategy> = map_left.clone().into(); let domain_map_left: DomainMap<_, _, UnionMergeStrategy> = map_left.clone().into();
let domain_map_right: DomainMap<_, _, UnionMergeStrategy> = map_right.clone().into(); let domain_map_right: DomainMap<_, _, UnionMergeStrategy> = map_right.clone().into();
let merged_map = domain_map_left.merge(&domain_map_right); let merged_map = domain_map_left.merge(&domain_map_right);
assert_eq!(merged_map.get(&0), Some(&Bitvector::from_i64(0).into())); assert_eq!(merged_map.get(&0), Some(&bitvec!("0:8").into()));
assert_eq!( assert_eq!(
merged_map.get(&1), merged_map.get(&1),
Some(&BitvectorDomain::new_top(ByteSize::new(8)).into()) Some(&BitvectorDomain::new_top(ByteSize::new(8)).into())
); );
assert_eq!(merged_map.get(&2), Some(&Bitvector::from_i64(1).into())); assert_eq!(merged_map.get(&2), Some(&bitvec!("1:8").into()));
assert_eq!( assert_eq!(
merged_map.get(&5), merged_map.get(&5),
Some(&DataDomain::new_top(ByteSize::new(8)).into()) Some(&DataDomain::new_top(ByteSize::new(8)).into())
...@@ -273,7 +272,7 @@ mod tests { ...@@ -273,7 +272,7 @@ mod tests {
let merged_map = domain_map_left.merge(&domain_map_right); let merged_map = domain_map_left.merge(&domain_map_right);
assert_eq!( assert_eq!(
merged_map.get(&0).unwrap().get_absolute_value(), merged_map.get(&0).unwrap().get_absolute_value(),
Some(&Bitvector::from_i64(0).into()) Some(&bitvec!("0:8").into())
); );
assert!(merged_map.get(&0).unwrap().contains_top()); assert!(merged_map.get(&0).unwrap().contains_top());
assert_eq!( assert_eq!(
...@@ -282,7 +281,7 @@ mod tests { ...@@ -282,7 +281,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
merged_map.get(&2).unwrap().get_absolute_value(), merged_map.get(&2).unwrap().get_absolute_value(),
Some(&Bitvector::from_i64(1).into()) Some(&bitvec!("1:8").into())
); );
assert!(merged_map.get(&2).unwrap().contains_top()); assert!(merged_map.get(&2).unwrap().contains_top());
assert_eq!(merged_map.get(&5), None); assert_eq!(merged_map.get(&5), None);
......
...@@ -144,7 +144,7 @@ impl std::fmt::Display for AbstractIdentifier { ...@@ -144,7 +144,7 @@ impl std::fmt::Display for AbstractIdentifier {
} else { } else {
write!(formatter, "{}(", self.0.time)?; write!(formatter, "{}(", self.0.time)?;
for hint in &self.0.path_hints { for hint in &self.0.path_hints {
write!(formatter, "->{}", hint)?; write!(formatter, "->{hint}",)?;
} }
write!(formatter, ") @ {}", self.0.location) write!(formatter, ") @ {}", self.0.location)
} }
...@@ -185,10 +185,10 @@ impl std::fmt::Display for AbstractLocation { ...@@ -185,10 +185,10 @@ impl std::fmt::Display for AbstractLocation {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { match self {
Self::Register(var) => write!(formatter, "{}", var.name), Self::Register(var) => write!(formatter, "{}", var.name),
Self::GlobalAddress { address, size: _ } => write!(formatter, "0x{:x}", address), Self::GlobalAddress { address, size: _ } => write!(formatter, "0x{address:x}"),
Self::Pointer(var, location) => write!(formatter, "{}->{}", var.name, location), Self::Pointer(var, location) => write!(formatter, "{}->{}", var.name, location),
Self::GlobalPointer(address, location) => { Self::GlobalPointer(address, location) => {
write!(formatter, "0x{:x}->{}", address, location) write!(formatter, "0x{address:x}->{location}")
} }
} }
} }
...@@ -275,8 +275,8 @@ impl AbstractMemoryLocation { ...@@ -275,8 +275,8 @@ impl AbstractMemoryLocation {
impl std::fmt::Display for AbstractMemoryLocation { impl std::fmt::Display for AbstractMemoryLocation {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { match self {
Self::Location { offset, .. } => write!(formatter, "({})", offset), Self::Location { offset, .. } => write!(formatter, "({offset})"),
Self::Pointer { offset, target } => write!(formatter, "({})->{}", offset, target), Self::Pointer { offset, target } => write!(formatter, "({offset})->{target}"),
} }
} }
} }
...@@ -284,6 +284,7 @@ impl std::fmt::Display for AbstractMemoryLocation { ...@@ -284,6 +284,7 @@ impl std::fmt::Display for AbstractMemoryLocation {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::variable;
impl AbstractIdentifier { impl AbstractIdentifier {
/// Mock an abstract identifier with the given TID name and pointing to the value in the given register name. /// Mock an abstract identifier with the given TID name and pointing to the value in the given register name.
...@@ -294,7 +295,12 @@ pub mod tests { ...@@ -294,7 +295,12 @@ pub mod tests {
) -> AbstractIdentifier { ) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new(tid.to_string()), Tid::new(tid.to_string()),
AbstractLocation::from_var(&Variable::mock(register, size_in_bytes)).unwrap(), AbstractLocation::from_var(&variable!(format!(
"{}:{}",
register.to_string(),
size_in_bytes
)))
.unwrap(),
) )
} }
} }
...@@ -311,7 +317,7 @@ pub mod tests { ...@@ -311,7 +317,7 @@ pub mod tests {
// Test uniqueness of TIDs in path hint array. // Test uniqueness of TIDs in path hint array.
let id = AbstractIdentifier::new( let id = AbstractIdentifier::new(
Tid::new("time_id"), Tid::new("time_id"),
AbstractLocation::from_var(&Variable::mock("var", 8)).unwrap(), AbstractLocation::from_var(&variable!("var:8")).unwrap(),
); );
let id = id.with_path_hint(Tid::new("first_hint")).unwrap(); let id = id.with_path_hint(Tid::new("first_hint")).unwrap();
let id = id.with_path_hint(Tid::new("second_hint")).unwrap(); let id = id.with_path_hint(Tid::new("second_hint")).unwrap();
...@@ -321,7 +327,7 @@ pub mod tests { ...@@ -321,7 +327,7 @@ pub mod tests {
#[test] #[test]
fn test_bytesize() { fn test_bytesize() {
let location = let location =
AbstractLocation::from_stack_position(&Variable::mock("RSP", 8), 10, ByteSize::new(4)); AbstractLocation::from_stack_position(&variable!("RSP:8"), 10, ByteSize::new(4));
let id = AbstractIdentifier::new(Tid::new("id"), location); let id = AbstractIdentifier::new(Tid::new("id"), location);
assert_eq!(id.bytesize(), ByteSize::new(4)); assert_eq!(id.bytesize(), ByteSize::new(4));
} }
......
use crate::bitvec;
use super::*; use super::*;
impl Interval { impl Interval {
pub fn mock(start: i64, end: i64) -> Interval { pub fn mock(start: i64, end: i64) -> Interval {
Interval::new(Bitvector::from_i64(start), Bitvector::from_i64(end), 1) Interval::new(
bitvec!(format!("{}:8", start)),
bitvec!(format!("{}:8", end)),
1,
)
} }
pub fn mock_i8(start: i8, end: i8) -> Interval { pub fn mock_i8(start: i8, end: i8) -> Interval {
Interval::new(Bitvector::from_i8(start), Bitvector::from_i8(end), 1) Interval::new(
bitvec!(format!("{}:1", start)),
bitvec!(format!("{}:1", end)),
1,
)
} }
pub fn with_stride(mut self, stride: u64) -> Interval { pub fn with_stride(mut self, stride: u64) -> Interval {
...@@ -92,7 +102,7 @@ fn subpiece_higher() { ...@@ -92,7 +102,7 @@ fn subpiece_higher() {
let val = Interval::mock(3, 21).with_stride(6); let val = Interval::mock(3, 21).with_stride(6);
assert_eq!( assert_eq!(
val.subpiece_higher(ByteSize::new(7)), val.subpiece_higher(ByteSize::new(7)),
Interval::from(Bitvector::from_i8(0)) Interval::from(bitvec!("0:1"))
) )
} }
...@@ -117,8 +127,8 @@ fn piece() { ...@@ -117,8 +127,8 @@ fn piece() {
assert_eq!( assert_eq!(
left.piece(&right), left.piece(&right),
Interval { Interval {
start: Bitvector::from_i16(256), start: bitvec!("256:2"),
end: Bitvector::from_i16(1278), end: bitvec!("1278:2"),
stride: 2, stride: 2,
} }
); );
...@@ -127,8 +137,8 @@ fn piece() { ...@@ -127,8 +137,8 @@ fn piece() {
assert_eq!( assert_eq!(
left.piece(&right), left.piece(&right),
Interval { Interval {
start: Bitvector::from_i16(259), start: bitvec!("259:2"),
end: Bitvector::from_i16(1039), end: bitvec!("1039:2"),
stride: 2, stride: 2,
} }
); );
...@@ -145,11 +155,11 @@ fn add_and_sub() { ...@@ -145,11 +155,11 @@ fn add_and_sub() {
#[test] #[test]
fn contains() { fn contains() {
let interval = Interval::mock(2, 10).with_stride(4); let interval = Interval::mock(2, 10).with_stride(4);
let elem = Bitvector::from_i64(4); let elem = bitvec!("4:8");
assert!(!interval.contains(&elem)); assert!(!interval.contains(&elem));
let elem = Bitvector::from_i64(6); let elem = bitvec!("6:8");
assert!(interval.contains(&elem)); assert!(interval.contains(&elem));
let elem = Bitvector::from_i64(14); let elem = bitvec!("14:8");
assert!(!interval.contains(&elem)); assert!(!interval.contains(&elem));
} }
......
use crate::bitvec;
use super::*; use super::*;
impl IntervalDomain { impl IntervalDomain {
/// Return a new interval domain of 8-byte integers. /// Return a new interval domain of 8-byte integers.
pub fn mock(start: i64, end: i64) -> IntervalDomain { pub fn mock(start: i64, end: i64) -> IntervalDomain {
IntervalDomain::new(Bitvector::from_i64(start), Bitvector::from_i64(end)) IntervalDomain::new(
bitvec!(format!("{}:8", start)),
bitvec!(format!("{}:8", end)),
)
} }
/// Return a new interval domain of 1-byte integers. /// Return a new interval domain of 1-byte integers.
pub fn mock_i8(start: i8, end: i8) -> IntervalDomain { pub fn mock_i8(start: i8, end: i8) -> IntervalDomain {
IntervalDomain::new(Bitvector::from_i8(start), Bitvector::from_i8(end)) IntervalDomain::new(
bitvec!(format!("{}:1", start)),
bitvec!(format!("{}:1", end)),
)
} }
/// Return a new interval domain of 4-byte integers. /// Return a new interval domain of 4-byte integers.
pub fn mock_i32(start: i32, end: i32) -> IntervalDomain { pub fn mock_i32(start: i32, end: i32) -> IntervalDomain {
IntervalDomain::new(Bitvector::from_i32(start), Bitvector::from_i32(end)) IntervalDomain::new(
bitvec!(format!("{}:4", start)),
bitvec!(format!("{}:4", end)),
)
} }
pub fn mock_with_bounds( pub fn mock_with_bounds(
...@@ -23,8 +34,8 @@ impl IntervalDomain { ...@@ -23,8 +34,8 @@ impl IntervalDomain {
upper_bound: Option<i64>, upper_bound: Option<i64>,
) -> IntervalDomain { ) -> IntervalDomain {
let mut domain = IntervalDomain::mock(start, end); let mut domain = IntervalDomain::mock(start, end);
domain.update_widening_lower_bound(&lower_bound.map(|b| Bitvector::from_i64(b))); domain.update_widening_lower_bound(&lower_bound.map(|b| bitvec!(format!("{}:8", b))));
domain.update_widening_upper_bound(&upper_bound.map(|b| Bitvector::from_i64(b))); domain.update_widening_upper_bound(&upper_bound.map(|b| bitvec!(format!("{}:8", b))));
domain domain
} }
...@@ -35,8 +46,8 @@ impl IntervalDomain { ...@@ -35,8 +46,8 @@ impl IntervalDomain {
upper_bound: Option<i8>, upper_bound: Option<i8>,
) -> IntervalDomain { ) -> IntervalDomain {
let mut domain = IntervalDomain::mock_i8(start, end); let mut domain = IntervalDomain::mock_i8(start, end);
domain.update_widening_lower_bound(&lower_bound.map(|b| Bitvector::from_i8(b))); domain.update_widening_lower_bound(&lower_bound.map(|b| bitvec!(format!("{}:1", b))));
domain.update_widening_upper_bound(&upper_bound.map(|b| Bitvector::from_i8(b))); domain.update_widening_upper_bound(&upper_bound.map(|b| bitvec!(format!("{}:1", b))));
domain domain
} }
...@@ -396,11 +407,11 @@ fn shift_left() { ...@@ -396,11 +407,11 @@ fn shift_left() {
#[test] #[test]
fn simple_interval_contains() { fn simple_interval_contains() {
let domain = IntervalDomain::mock(-10, 5); let domain = IntervalDomain::mock(-10, 5);
assert!(!domain.interval.contains(&Bitvector::from_i64(-11))); assert!(!domain.interval.contains(&bitvec!("-11:8")));
assert!(domain.interval.contains(&Bitvector::from_i64(-10))); assert!(domain.interval.contains(&bitvec!("-10:8")));
assert!(domain.interval.contains(&Bitvector::from_i64(-4))); assert!(domain.interval.contains(&bitvec!("-4:8")));
assert!(domain.interval.contains(&Bitvector::from_i64(5))); assert!(domain.interval.contains(&bitvec!("5:8")));
assert!(!domain.interval.contains(&Bitvector::from_i64(6))); assert!(!domain.interval.contains(&bitvec!("6:8")));
} }
#[test] #[test]
...@@ -410,57 +421,57 @@ fn add_signed_bounds() { ...@@ -410,57 +421,57 @@ fn add_signed_bounds() {
// signed_less_equal // signed_less_equal
let x = interval let x = interval
.clone() .clone()
.add_signed_less_equal_bound(&Bitvector::from_i64(20)); .add_signed_less_equal_bound(&bitvec!("20:8"));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(Some(-100), -10, 10, Some(20)) IntervalDomain::mock_with_bounds(Some(-100), -10, 10, Some(20))
); );
let x = interval let x = interval
.clone() .clone()
.add_signed_less_equal_bound(&Bitvector::from_i64(-5)); .add_signed_less_equal_bound(&bitvec!("-5:8"));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(Some(-100), -10, -5, None) IntervalDomain::mock_with_bounds(Some(-100), -10, -5, None)
); );
let x = interval let x = interval
.clone() .clone()
.add_signed_less_equal_bound(&Bitvector::from_i64(-20)); .add_signed_less_equal_bound(&bitvec!("-20:8"));
assert!(x.is_err()); assert!(x.is_err());
let x = IntervalDomain::mock(0, 10) let x = IntervalDomain::mock(0, 10)
.with_stride(10) .with_stride(10)
.add_signed_less_equal_bound(&Bitvector::from_i64(15)); .add_signed_less_equal_bound(&bitvec!("15:8"));
assert_eq!(x.unwrap(), IntervalDomain::mock(0, 10).with_stride(10)); assert_eq!(x.unwrap(), IntervalDomain::mock(0, 10).with_stride(10));
let x = IntervalDomain::mock(0, 10) let x = IntervalDomain::mock(0, 10)
.with_stride(10) .with_stride(10)
.add_signed_less_equal_bound(&Bitvector::from_i64(5)); .add_signed_less_equal_bound(&bitvec!("5:8"));
assert_eq!(x.unwrap(), IntervalDomain::mock(0, 0)); assert_eq!(x.unwrap(), IntervalDomain::mock(0, 0));
//signed_greater_equal //signed_greater_equal
let x = interval let x = interval
.clone() .clone()
.add_signed_greater_equal_bound(&Bitvector::from_i64(20)); .add_signed_greater_equal_bound(&bitvec!("20:8"));
assert!(x.is_err()); assert!(x.is_err());
let x = interval let x = interval
.clone() .clone()
.add_signed_greater_equal_bound(&Bitvector::from_i64(-5)); .add_signed_greater_equal_bound(&bitvec!("-5:8"));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(None, -5, 10, Some(100)) IntervalDomain::mock_with_bounds(None, -5, 10, Some(100))
); );
let x = interval let x = interval
.clone() .clone()
.add_signed_greater_equal_bound(&Bitvector::from_i64(-20)); .add_signed_greater_equal_bound(&bitvec!("-20:8"));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(Some(-20), -10, 10, Some(100)) IntervalDomain::mock_with_bounds(Some(-20), -10, 10, Some(100))
); );
let x = IntervalDomain::mock(0, 10) let x = IntervalDomain::mock(0, 10)
.with_stride(10) .with_stride(10)
.add_signed_greater_equal_bound(&Bitvector::from_i64(-5)); .add_signed_greater_equal_bound(&bitvec!("-5:8"));
assert_eq!(x.unwrap(), IntervalDomain::mock(0, 10).with_stride(10)); assert_eq!(x.unwrap(), IntervalDomain::mock(0, 10).with_stride(10));
let x = IntervalDomain::mock(0, 10) let x = IntervalDomain::mock(0, 10)
.with_stride(10) .with_stride(10)
.add_signed_greater_equal_bound(&Bitvector::from_i64(5)); .add_signed_greater_equal_bound(&bitvec!("5:8"));
assert_eq!(x.unwrap(), IntervalDomain::mock(10, 10)); assert_eq!(x.unwrap(), IntervalDomain::mock(10, 10));
} }
...@@ -473,67 +484,67 @@ fn add_unsigned_bounds() { ...@@ -473,67 +484,67 @@ fn add_unsigned_bounds() {
// unsigned_less_equal // unsigned_less_equal
let x = positive_interval let x = positive_interval
.clone() .clone()
.add_unsigned_less_equal_bound(&Bitvector::from_i64(35)); .add_unsigned_less_equal_bound(&bitvec!("35:8"));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(Some(10), 20, 30, Some(35)) IntervalDomain::mock_with_bounds(Some(10), 20, 30, Some(35))
); );
let x = positive_interval let x = positive_interval
.clone() .clone()
.add_unsigned_less_equal_bound(&Bitvector::from_i64(15)); .add_unsigned_less_equal_bound(&bitvec!("15:8"));
assert!(x.is_err()); assert!(x.is_err());
let x = wrapped_interval let x = wrapped_interval
.clone() .clone()
.add_unsigned_less_equal_bound(&Bitvector::from_i64(35)); .add_unsigned_less_equal_bound(&bitvec!("35:8"));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(None, 0, 10, Some(35)) IntervalDomain::mock_with_bounds(None, 0, 10, Some(35))
); );
let x = wrapped_interval let x = wrapped_interval
.clone() .clone()
.add_unsigned_less_equal_bound(&Bitvector::from_i64(-5)); .add_unsigned_less_equal_bound(&bitvec!("-5:8"));
assert_eq!(x.unwrap(), wrapped_interval); // Cannot remove a subinterval from the domain assert_eq!(x.unwrap(), wrapped_interval); // Cannot remove a subinterval from the domain
let x = negative_interval let x = negative_interval
.clone() .clone()
.add_unsigned_less_equal_bound(&Bitvector::from_i64(-25)); .add_unsigned_less_equal_bound(&bitvec!("-25:8"));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(Some(-40), -30, -25, None) IntervalDomain::mock_with_bounds(Some(-40), -30, -25, None)
); );
let x = negative_interval let x = negative_interval
.clone() .clone()
.add_unsigned_less_equal_bound(&Bitvector::from_i64(-35)); .add_unsigned_less_equal_bound(&bitvec!("-35:8"));
assert!(x.is_err()); assert!(x.is_err());
// unsigned_greater_equal // unsigned_greater_equal
let x = positive_interval let x = positive_interval
.clone() .clone()
.add_unsigned_greater_equal_bound(&Bitvector::from_i64(25)); .add_unsigned_greater_equal_bound(&bitvec!("25:8"));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(None, 25, 30, Some(40)) IntervalDomain::mock_with_bounds(None, 25, 30, Some(40))
); );
let x = positive_interval let x = positive_interval
.clone() .clone()
.add_unsigned_greater_equal_bound(&Bitvector::from_i64(35)); .add_unsigned_greater_equal_bound(&bitvec!("35:8"));
assert!(x.is_err()); assert!(x.is_err());
let x = wrapped_interval let x = wrapped_interval
.clone() .clone()
.add_unsigned_greater_equal_bound(&Bitvector::from_i64(5)); .add_unsigned_greater_equal_bound(&bitvec!("5:8"));
assert_eq!(x.unwrap(), wrapped_interval); assert_eq!(x.unwrap(), wrapped_interval);
let x = wrapped_interval let x = wrapped_interval
.clone() .clone()
.add_unsigned_greater_equal_bound(&Bitvector::from_i64(35)); .add_unsigned_greater_equal_bound(&bitvec!("35:8"));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(Some(-100), -10, -1, None) IntervalDomain::mock_with_bounds(Some(-100), -10, -1, None)
); );
let x = wrapped_interval let x = wrapped_interval
.clone() .clone()
.add_unsigned_greater_equal_bound(&Bitvector::from_i64(-50)); .add_unsigned_greater_equal_bound(&bitvec!("-50:8"));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(Some(-50), -10, -1, None) IntervalDomain::mock_with_bounds(Some(-50), -10, -1, None)
...@@ -541,11 +552,11 @@ fn add_unsigned_bounds() { ...@@ -541,11 +552,11 @@ fn add_unsigned_bounds() {
let x = negative_interval let x = negative_interval
.clone() .clone()
.add_unsigned_greater_equal_bound(&Bitvector::from_i64(25)); .add_unsigned_greater_equal_bound(&bitvec!("25:8"));
assert_eq!(x.unwrap(), negative_interval); assert_eq!(x.unwrap(), negative_interval);
let x = negative_interval let x = negative_interval
.clone() .clone()
.add_unsigned_greater_equal_bound(&Bitvector::from_i64(-25)); .add_unsigned_greater_equal_bound(&bitvec!("-25:8"));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(None, -25, -20, Some(-10)) IntervalDomain::mock_with_bounds(None, -25, -20, Some(-10))
...@@ -556,36 +567,28 @@ fn add_unsigned_bounds() { ...@@ -556,36 +567,28 @@ fn add_unsigned_bounds() {
fn add_not_equal_bounds() { fn add_not_equal_bounds() {
let interval = IntervalDomain::mock_with_bounds(None, -10, 10, None); let interval = IntervalDomain::mock_with_bounds(None, -10, 10, None);
let x = interval let x = interval.clone().add_not_equal_bound(&bitvec!("-20:8"));
.clone()
.add_not_equal_bound(&Bitvector::from_i64(-20));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(Some(-19), -10, 10, None) IntervalDomain::mock_with_bounds(Some(-19), -10, 10, None)
); );
let x = interval let x = interval.clone().add_not_equal_bound(&bitvec!("0:8"));
.clone()
.add_not_equal_bound(&Bitvector::from_i64(-0));
assert_eq!(x.unwrap(), interval); assert_eq!(x.unwrap(), interval);
let x = interval let x = interval.clone().add_not_equal_bound(&bitvec!("20:8"));
.clone()
.add_not_equal_bound(&Bitvector::from_i64(20));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(None, -10, 10, Some(19)) IntervalDomain::mock_with_bounds(None, -10, 10, Some(19))
); );
let interval = IntervalDomain::mock(5, 5); let interval = IntervalDomain::mock(5, 5);
let x = interval let x = interval.clone().add_not_equal_bound(&bitvec!("5:8"));
.clone()
.add_not_equal_bound(&Bitvector::from_i64(5));
assert!(x.is_err()); assert!(x.is_err());
let interval = IntervalDomain::mock(5, 6); let interval = IntervalDomain::mock(5, 6);
let x = interval.add_not_equal_bound(&Bitvector::from_i64(5)); let x = interval.add_not_equal_bound(&bitvec!("5:8"));
assert_eq!(x.unwrap(), IntervalDomain::mock(6, 6)); assert_eq!(x.unwrap(), IntervalDomain::mock(6, 6));
let interval = IntervalDomain::mock_with_bounds(None, 5, 6, Some(100)); let interval = IntervalDomain::mock_with_bounds(None, 5, 6, Some(100));
let x = interval.add_not_equal_bound(&Bitvector::from_i64(10)); let x = interval.add_not_equal_bound(&bitvec!("10:8"));
assert_eq!( assert_eq!(
x.unwrap(), x.unwrap(),
IntervalDomain::mock_with_bounds(None, 5, 6, Some(9)) IntervalDomain::mock_with_bounds(None, 5, 6, Some(9))
...@@ -624,39 +627,28 @@ fn float_nan_bytesize() { ...@@ -624,39 +627,28 @@ fn float_nan_bytesize() {
fn stride_rounding() { fn stride_rounding() {
let interval = Interval::mock(3, 13).with_stride(10); let interval = Interval::mock(3, 13).with_stride(10);
assert_eq!( assert_eq!(
Bitvector::from_i64(5) bitvec!("5:8").round_up_to_stride_of(&interval).unwrap(),
.round_up_to_stride_of(&interval) bitvec!("13:8")
.unwrap(),
Bitvector::from_i64(13)
); );
assert_eq!( assert_eq!(
Bitvector::from_i64(5) bitvec!("5:8").round_down_to_stride_of(&interval).unwrap(),
.round_down_to_stride_of(&interval) bitvec!("3:8")
.unwrap(),
Bitvector::from_i64(3)
); );
assert_eq!( assert_eq!(
Bitvector::from_i64(-277) bitvec!("-277:8").round_up_to_stride_of(&interval).unwrap(),
.round_up_to_stride_of(&interval) bitvec!("-277:8")
.unwrap(),
Bitvector::from_i64(-277)
); );
assert_eq!( assert_eq!(
Bitvector::from_i64(-277) bitvec!("-277:8")
.round_down_to_stride_of(&interval) .round_down_to_stride_of(&interval)
.unwrap(), .unwrap(),
Bitvector::from_i64(-277) bitvec!("-277:8")
); );
let interval = Interval::mock_i8(100, 110).with_stride(10); let interval = Interval::mock_i8(100, 110).with_stride(10);
assert_eq!( assert_eq!(
Bitvector::from_i8(-123) bitvec!("-123:1").round_up_to_stride_of(&interval).unwrap(),
.round_up_to_stride_of(&interval) bitvec!("-120:1")
.unwrap(),
Bitvector::from_i8(-120)
);
assert_eq!(
Bitvector::from_i8(-123).round_down_to_stride_of(&interval),
None
); );
assert_eq!(bitvec!("-123:1").round_down_to_stride_of(&interval), None);
} }
...@@ -2,6 +2,7 @@ use super::*; ...@@ -2,6 +2,7 @@ use super::*;
use crate::abstract_domain::DataDomain; use crate::abstract_domain::DataDomain;
use crate::abstract_domain::IntervalDomain; use crate::abstract_domain::IntervalDomain;
use crate::abstract_domain::RegisterDomain; use crate::abstract_domain::RegisterDomain;
use crate::bitvec;
use crate::intermediate_representation::*; use crate::intermediate_representation::*;
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash, PartialOrd, Ord)] #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash, PartialOrd, Ord)]
...@@ -60,94 +61,111 @@ fn mock(val: i64, bytesize: impl Into<ByteSize>) -> MockDomain { ...@@ -60,94 +61,111 @@ fn mock(val: i64, bytesize: impl Into<ByteSize>) -> MockDomain {
MockDomain(val, bytesize.into()) MockDomain(val, bytesize.into())
} }
fn bv(val: i64) -> Bitvector {
Bitvector::from_i64(val)
}
#[test] #[test]
fn mem_region() { fn mem_region() {
let mut region: MemRegion<MockDomain> = MemRegion::new(ByteSize::from(8u64)); let mut region: MemRegion<MockDomain> = MemRegion::new(ByteSize::from(8u64));
region.add(mock(5, 3u64), bv(5)); region.add(mock(5, 3u64), bitvec!("5:8"));
assert_eq!(region.get(bv(5), ByteSize::from(3u64)), mock(5, 3u64)); assert_eq!(
region.add(mock(7, 2u64), bv(8)); region.get(bitvec!("5:8"), ByteSize::from(3u64)),
assert_eq!(region.get(bv(8), ByteSize::from(2u64)), mock(7, 2u64)); mock(5, 3u64)
assert_eq!(region.get(bv(5), ByteSize::from(3u64)), mock(5, 3u64)); );
region.add(mock(7, 2u64), bitvec!("8:8"));
assert_eq!(
region.get(bitvec!("8:8"), ByteSize::from(2u64)),
mock(7, 2u64)
);
assert_eq!( assert_eq!(
region.get(bv(5), ByteSize::from(2u64)), region.get(bitvec!("5:8"), ByteSize::from(3u64)),
mock(5, 3u64)
);
assert_eq!(
region.get(bitvec!("5:8"), ByteSize::from(2u64)),
MockDomain::new_top(ByteSize::new(2)) MockDomain::new_top(ByteSize::new(2))
); );
region.add(mock(9, 2u64), bv(6)); region.add(mock(9, 2u64), bitvec!("6:8"));
assert_eq!(region.get(bv(6), ByteSize::from(2u64)), mock(9, 2u64)); assert_eq!(
region.get(bitvec!("6:8"), ByteSize::from(2u64)),
mock(9, 2u64)
);
assert_eq!( assert_eq!(
region.get(bv(5), ByteSize::from(3u64)), region.get(bitvec!("5:8"), ByteSize::from(3u64)),
MockDomain::new_top(ByteSize::new(3)) MockDomain::new_top(ByteSize::new(3))
); );
assert_eq!(region.get(bv(8), ByteSize::from(2u64)), mock(7, 2u64));
region.add(mock(9, 11u64), bv(-3));
assert_eq!(region.get(bv(-3), ByteSize::from(11u64)), mock(9, 11u64));
assert_eq!( assert_eq!(
region.get(bv(6), ByteSize::from(2u64)), region.get(bitvec!("8:8"), ByteSize::from(2u64)),
mock(7, 2u64)
);
region.add(mock(9, 11u64), bitvec!("-3:8"));
assert_eq!(
region.get(bitvec!("-3:8"), ByteSize::from(11u64)),
mock(9, 11u64)
);
assert_eq!(
region.get(bitvec!("6:8"), ByteSize::from(2u64)),
MockDomain::new_top(ByteSize::new(2)) MockDomain::new_top(ByteSize::new(2))
); );
assert_eq!(region.get(bv(8), ByteSize::from(2u64)), mock(7, 2u64)); assert_eq!(
region.get(bitvec!("8:8"), ByteSize::from(2u64)),
mock(7, 2u64)
);
let mut other_region = MemRegion::new(ByteSize::from(8u64)); let mut other_region = MemRegion::new(ByteSize::from(8u64));
other_region.add(mock(7, 2u64), bv(8)); other_region.add(mock(7, 2u64), bitvec!("8:8"));
assert!(region != other_region); assert!(region != other_region);
let merged_region = region.merge(&other_region); let merged_region = region.merge(&other_region);
assert_eq!( assert_eq!(
merged_region.get(bv(8), ByteSize::from(2u64)), merged_region.get(bitvec!("8:8"), ByteSize::from(2u64)),
mock(7, 2u64) mock(7, 2u64)
); );
assert_eq!( assert_eq!(
merged_region.get(bv(-3), ByteSize::from(11u64)), merged_region.get(bitvec!("-3:8"), ByteSize::from(11u64)),
MockDomain::new_top(ByteSize::from(11u64)) MockDomain::new_top(ByteSize::from(11u64))
); );
other_region.add(mock(9, 11u64), bv(-3)); other_region.add(mock(9, 11u64), bitvec!("-3:8"));
assert_eq!(region, other_region); assert_eq!(region, other_region);
} }
#[test] #[test]
fn merge_test() { fn merge_test() {
let data: fn(u64) -> DataDomain<IntervalDomain> = let data: fn(u64) -> DataDomain<IntervalDomain> =
|val| DataDomain::from(Bitvector::from_u64(val)); |val| DataDomain::from(bitvec!(format!("{}:8", val)));
let mut region: MemRegion<DataDomain<IntervalDomain>> = MemRegion::new(ByteSize::new(8)); let mut region: MemRegion<DataDomain<IntervalDomain>> = MemRegion::new(ByteSize::new(8));
region.add(data(0), Bitvector::from_u64(0)); region.add(data(0), bitvec!("0:8"));
region.add(data(8), Bitvector::from_u64(8)); region.add(data(8), bitvec!("8:8"));
region.add(data(22), Bitvector::from_u64(32)); region.add(data(22), bitvec!("32:8"));
region.add(data(42), Bitvector::from_u64(50)); region.add(data(42), bitvec!("50:8"));
region.add(data(70), Bitvector::from_u64(70)); region.add(data(70), bitvec!("70:8"));
let mut other_region: MemRegion<DataDomain<IntervalDomain>> = MemRegion::new(ByteSize::new(8)); let mut other_region: MemRegion<DataDomain<IntervalDomain>> = MemRegion::new(ByteSize::new(8));
other_region.add(data(1), Bitvector::from_u64(0)); other_region.add(data(1), bitvec!("0:8"));
other_region.add(data(15), Bitvector::from_u64(15)); other_region.add(data(15), bitvec!("15:8"));
other_region.add(data(26), Bitvector::from_u64(25)); other_region.add(data(26), bitvec!("25:8"));
other_region.add(data(42), Bitvector::from_u64(58)); other_region.add(data(42), bitvec!("58:8"));
other_region.add(Bitvector::from_u8(70).into(), Bitvector::from_u64(70)); other_region.add(bitvec!("70:1").into(), bitvec!("70:8"));
let merged_region = region.merge(&&other_region); let merged_region = region.merge(&&other_region);
// Merge elements at target address. // Merge elements at target address.
assert_eq!( assert_eq!(
merged_region.get_unsized(Bitvector::from_u64(0)), merged_region.get_unsized(bitvec!("0:8")),
Some(IntervalDomain::mock(0, 1).into()) Some(IntervalDomain::mock(0, 1).into())
); );
// Overlapping elements are not added to the merged memory region. // Overlapping elements are not added to the merged memory region.
assert_eq!(merged_region.get_unsized(Bitvector::from_u64(8)), None); assert_eq!(merged_region.get_unsized(bitvec!("8:8")), None);
assert_eq!(merged_region.get_unsized(Bitvector::from_u64(15)), None); assert_eq!(merged_region.get_unsized(bitvec!("15:8")), None);
assert_eq!(merged_region.get_unsized(Bitvector::from_u64(25)), None); assert_eq!(merged_region.get_unsized(bitvec!("25:8")), None);
assert_eq!(merged_region.get_unsized(Bitvector::from_u64(32)), None); assert_eq!(merged_region.get_unsized(bitvec!("32:8")), None);
// Elements only contained in one region are merged with `Top`. // Elements only contained in one region are merged with `Top`.
let mut elem_plus_top: DataDomain<IntervalDomain> = Bitvector::from_u64(42).into(); let mut elem_plus_top: DataDomain<IntervalDomain> = bitvec!("42:8").into();
elem_plus_top.set_contains_top_flag(); elem_plus_top.set_contains_top_flag();
assert!(!elem_plus_top.is_top()); assert!(!elem_plus_top.is_top());
assert_eq!( assert_eq!(
merged_region.get_unsized(Bitvector::from_u64(50)), merged_region.get_unsized(bitvec!("50:8")),
Some(elem_plus_top.clone()) Some(elem_plus_top.clone())
); );
assert_eq!( assert_eq!(
merged_region.get_unsized(Bitvector::from_u64(58)), merged_region.get_unsized(bitvec!("58:8")),
Some(elem_plus_top) Some(elem_plus_top)
); );
// Elements with differing bytesizes are not added to the merged domain. // Elements with differing bytesizes are not added to the merged domain.
assert_eq!(merged_region.get_unsized(Bitvector::from_u64(70)), None); assert_eq!(merged_region.get_unsized(bitvec!("70:8")), None);
// Check that no other unexpected elements are contained in the merged region. // Check that no other unexpected elements are contained in the merged region.
assert_eq!(merged_region.values().len(), 3); assert_eq!(merged_region.values().len(), 3);
} }
...@@ -155,12 +173,12 @@ fn merge_test() { ...@@ -155,12 +173,12 @@ fn merge_test() {
#[test] #[test]
fn do_not_save_top_elements() { fn do_not_save_top_elements() {
let mut region: MemRegion<MockDomain> = MemRegion::new(ByteSize::from(8u64)); let mut region: MemRegion<MockDomain> = MemRegion::new(ByteSize::from(8u64));
region.add(MockDomain::new_top(ByteSize::from(4u64)), bv(5)); region.add(MockDomain::new_top(ByteSize::from(4u64)), bitvec!("5:8"));
assert_eq!(region.values().len(), 0); assert_eq!(region.values().len(), 0);
let mut other_region: MemRegion<MockDomain> = MemRegion::new(ByteSize::from(8u64)); let mut other_region: MemRegion<MockDomain> = MemRegion::new(ByteSize::from(8u64));
region.add(mock(5, 4u64), bv(5)); region.add(mock(5, 4u64), bitvec!("5:8"));
other_region.add(mock(7, 4u64), bv(5)); other_region.add(mock(7, 4u64), bitvec!("5:8"));
let merged_region = region.merge(&other_region); let merged_region = region.merge(&other_region);
assert_eq!(region.values().len(), 1); assert_eq!(region.values().len(), 1);
assert_eq!(other_region.values().len(), 1); assert_eq!(other_region.values().len(), 1);
...@@ -170,18 +188,18 @@ fn do_not_save_top_elements() { ...@@ -170,18 +188,18 @@ fn do_not_save_top_elements() {
#[test] #[test]
fn value_removals() { fn value_removals() {
let mut region: MemRegion<MockDomain> = MemRegion::new(ByteSize::from(8u64)); let mut region: MemRegion<MockDomain> = MemRegion::new(ByteSize::from(8u64));
region.add(mock(1, 8u64), bv(0)); region.add(mock(1, 8u64), bitvec!("0:8"));
region.add(mock(2, 8u64), bv(8)); region.add(mock(2, 8u64), bitvec!("8:8"));
region.add(mock(3, 8u64), bv(16)); region.add(mock(3, 8u64), bitvec!("16:8"));
region.add(mock(4, 8u64), bv(24)); region.add(mock(4, 8u64), bitvec!("24:8"));
region.add(mock(5, 8u64), bv(32)); region.add(mock(5, 8u64), bitvec!("32:8"));
assert_eq!(region.values().len(), 5); assert_eq!(region.values().len(), 5);
region.remove(bv(2), bv(3)); region.remove(bitvec!("2:8"), bitvec!("3:8"));
assert_eq!(region.values().len(), 4); assert_eq!(region.values().len(), 4);
region.remove(bv(7), bv(1)); region.remove(bitvec!("7:8"), bitvec!("1:8"));
assert_eq!(region.values().len(), 4); assert_eq!(region.values().len(), 4);
region.remove(bv(7), bv(2)); region.remove(bitvec!("7:8"), bitvec!("2:8"));
assert_eq!(region.values().len(), 3); assert_eq!(region.values().len(), 3);
region.clear_interval(15, 1); region.clear_interval(15, 1);
...@@ -196,29 +214,41 @@ fn value_removals() { ...@@ -196,29 +214,41 @@ fn value_removals() {
} }
region.clear_top_values(); region.clear_top_values();
assert_eq!(region.values().len(), 1); assert_eq!(region.values().len(), 1);
assert_eq!(region.get(bv(24), ByteSize::from(8u64)), mock(4, 8u64)); assert_eq!(
region.get(bitvec!("24:8"), ByteSize::from(8u64)),
mock(4, 8u64)
);
} }
#[test] #[test]
fn merge_writes_with_top() { fn merge_writes_with_top() {
let data: DataDomain<IntervalDomain> = DataDomain::from(Bitvector::from_u64(0)); let data: DataDomain<IntervalDomain> = DataDomain::from(bitvec!("0:8"));
let mut data_with_top = data.clone(); let mut data_with_top = data.clone();
data_with_top.set_contains_top_flag(); data_with_top.set_contains_top_flag();
let mut region: MemRegion<DataDomain<IntervalDomain>> = MemRegion::new(ByteSize::new(8)); let mut region: MemRegion<DataDomain<IntervalDomain>> = MemRegion::new(ByteSize::new(8));
// Test `merge_write_top` method. // Test `merge_write_top` method.
region.add(data.clone(), bv(0)); region.add(data.clone(), bitvec!("0:8"));
region.merge_write_top(bv(0), ByteSize::new(8)); region.merge_write_top(bitvec!("0:8"), ByteSize::new(8));
assert_eq!(region.get_unsized(bv(0)), Some(data_with_top.clone())); assert_eq!(
region.get_unsized(bitvec!("0:8")),
Some(data_with_top.clone())
);
// `merge_write_top` removes intersecting values if position or size do not match. // `merge_write_top` removes intersecting values if position or size do not match.
region.add(data.clone(), bv(8)); region.add(data.clone(), bitvec!("8:8"));
region.merge_write_top(bv(5), ByteSize::new(8)); region.merge_write_top(bitvec!("5:8"), ByteSize::new(8));
assert!(region.inner.values.is_empty()); assert!(region.inner.values.is_empty());
// Test `mark_interval_values_as_top` method. // Test `mark_interval_values_as_top` method.
region.add(data.clone(), bv(0)); region.add(data.clone(), bitvec!("0:8"));
region.add(data.clone(), bv(8)); region.add(data.clone(), bitvec!("8:8"));
region.add(data.clone(), bv(16)); region.add(data.clone(), bitvec!("16:8"));
region.mark_interval_values_as_top(9, 16, ByteSize::new(1)); region.mark_interval_values_as_top(9, 16, ByteSize::new(1));
assert_eq!(region.get_unsized(bv(0)), Some(data)); assert_eq!(region.get_unsized(bitvec!("0:8")), Some(data));
assert_eq!(region.get_unsized(bv(8)), Some(data_with_top.clone())); assert_eq!(
assert_eq!(region.get_unsized(bv(16)), Some(data_with_top.clone())); region.get_unsized(bitvec!("8:8")),
Some(data_with_top.clone())
);
assert_eq!(
region.get_unsized(bitvec!("16:8")),
Some(data_with_top.clone())
);
} }
use super::{create_computation, mock_context, NodeValue}; use super::{create_computation, mock_context, NodeValue};
use crate::def;
use crate::expr;
use crate::intermediate_representation::*; use crate::intermediate_representation::*;
use mock_context::Context;
use mock_context::StartEnd;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::iter::FromIterator; use std::iter::FromIterator;
use mock_context::Context;
use mock_context::StartEnd;
fn mock_program() -> Term<Program> { fn mock_program() -> Term<Program> {
let var = Variable { let def_term1 = def!["def1: RAX:8 = -(RAX:8)"];
name: String::from("RAX"), let def_term2 = def!["def2: RAX:8 = -(RAX:8)"];
size: ByteSize::new(8), let def_term3 = def!["def3: RAX:8 = -(RAX:8)"];
is_temp: false, let def_term4 = def!["def4: RAX:8 = -(RAX:8)"];
}; let def_term5 = def!["def5: RAX:8 = -(RAX:8)"];
let value = Expression::UnOp {
op: UnOpType::IntNegate,
arg: Box::new(Expression::Var(var.clone())),
};
let def_term1 = Term {
tid: Tid::new("def1".to_string()),
term: Def::Assign {
var: var.clone(),
value: value.clone(),
},
};
let def_term2 = Term {
tid: Tid::new("def2".to_string()),
term: Def::Assign {
var: var.clone(),
value: value.clone(),
},
};
let def_term3 = Term {
tid: Tid::new("def3".to_string()),
term: Def::Assign {
var: var.clone(),
value: value.clone(),
},
};
let def_term4 = Term {
tid: Tid::new("def4".to_string()),
term: Def::Assign {
var: var.clone(),
value: value.clone(),
},
};
let def_term5 = Term {
tid: Tid::new("def5".to_string()),
term: Def::Assign {
var: var.clone(),
value: value.clone(),
},
};
let call_term = Term { let call_term = Term {
tid: Tid::new("call".to_string()), tid: Tid::new("call".to_string()),
term: Jmp::Call { term: Jmp::Call {
...@@ -61,7 +24,7 @@ fn mock_program() -> Term<Program> { ...@@ -61,7 +24,7 @@ fn mock_program() -> Term<Program> {
}; };
let return_term = Term { let return_term = Term {
tid: Tid::new("return".to_string()), tid: Tid::new("return".to_string()),
term: Jmp::Return(Expression::Const(Bitvector::zero(64.into()))), // The return term does not matter term: Jmp::Return(expr!("0:8")), // The return term does not matter
}; };
let jmp = Jmp::Branch(Tid::new("sub1_blk1")); let jmp = Jmp::Branch(Tid::new("sub1_blk1"));
let jmp_term = Term { let jmp_term = Term {
...@@ -94,7 +57,7 @@ fn mock_program() -> Term<Program> { ...@@ -94,7 +57,7 @@ fn mock_program() -> Term<Program> {
}; };
let cond_jump = Jmp::CBranch { let cond_jump = Jmp::CBranch {
target: Tid::new("sub1_blk1"), target: Tid::new("sub1_blk1"),
condition: Expression::Const(Bitvector::from_u8(0)), condition: expr!("0:1"),
}; };
let cond_jump_term = Term { let cond_jump_term = Term {
tid: Tid::new("cond_jump"), tid: Tid::new("cond_jump"),
......
...@@ -127,25 +127,18 @@ pub fn remove_dead_var_assignments(project: &mut Project) { ...@@ -127,25 +127,18 @@ pub fn remove_dead_var_assignments(project: &mut Project) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::defs;
fn def_assign_term(term_index: u64, input: &str, output: &str) -> Term<Def> {
Def::assign(
&format!("def_{}", term_index),
Variable::mock(output, 8),
Expression::Var(Variable::mock(input, 8)),
)
}
#[test] #[test]
fn dead_assignment_removal() { fn dead_assignment_removal() {
let defs = vec![ let defs = defs![
def_assign_term(1, "A", "B"), "def_1: B:8 = A:8",
def_assign_term(2, "B", "C"), "def_2: C:8 = B:8",
def_assign_term(3, "C", "RAX"), // dead assignment "def_3: RAX:8 = C:8",
def_assign_term(4, "B", "RAX"), "def_4: RAX:8 = B:8",
def_assign_term(5, "C", "RBX"), "def_5: RBX:8 = C:8",
def_assign_term(6, "A", "B"), // dead assignment, since the next assignment is dead "def_6: B:8 = A:8",
def_assign_term(7, "B", "C"), // dead assignment, since C is not a physical register "def_7: C:8 = B:8"
]; ];
let block = Term { let block = Term {
tid: Tid::new("block"), tid: Tid::new("block"),
...@@ -167,11 +160,11 @@ mod tests { ...@@ -167,11 +160,11 @@ mod tests {
project.program.term.subs.insert(sub.tid.clone(), sub); project.program.term.subs.insert(sub.tid.clone(), sub);
remove_dead_var_assignments(&mut project); remove_dead_var_assignments(&mut project);
let cleaned_defs = vec![ let cleaned_defs = defs![
def_assign_term(1, "A", "B"), "def_1: B:8 = A:8",
def_assign_term(2, "B", "C"), "def_2: C:8 = B:8",
def_assign_term(4, "B", "RAX"), "def_4: RAX:8 = B:8",
def_assign_term(5, "C", "RBX"), "def_5: RBX:8 = C:8"
]; ];
assert_eq!( assert_eq!(
&project.program.term.subs[&Tid::new("sub")].term.blocks[0] &project.program.term.subs[&Tid::new("sub")].term.blocks[0]
......
use super::*; use super::*;
use crate::intermediate_representation::{Def, Expression, Variable}; use crate::{defs, expr, intermediate_representation::Def, variable};
/// Creates a specific project containing three blocks for expression propagation tests. /// Creates a specific project containing three blocks for expression propagation tests.
/// ///
...@@ -12,11 +12,7 @@ fn mock_project() -> Project { ...@@ -12,11 +12,7 @@ fn mock_project() -> Project {
let callee_block = Term { let callee_block = Term {
tid: Tid::new("callee_block"), tid: Tid::new("callee_block"),
term: Blk { term: Blk {
defs: vec![Def::assign( defs: defs!["callee_def_1: Y:8 = Z:8"],
"callee_def_1",
Variable::mock("Y", 8),
Expression::var("Z", 8),
)],
jmps: Vec::new(), jmps: Vec::new(),
indirect_jmp_targets: Vec::new(), indirect_jmp_targets: Vec::new(),
}, },
...@@ -34,17 +30,9 @@ fn mock_project() -> Project { ...@@ -34,17 +30,9 @@ fn mock_project() -> Project {
let entry_jmp_block = Term { let entry_jmp_block = Term {
tid: Tid::new("entry_jmp_block"), tid: Tid::new("entry_jmp_block"),
term: Blk { term: Blk {
defs: vec![ defs: defs![
Def::assign( "entry_jmp_def_1: X:8 = ¬(Z:8)",
"entry_jmp_def_1", "entry_jmp_def_2: Z:8 = -(Z:8)"
Variable::mock("X", 8),
Expression::var("Z", 8).un_op(UnOpType::BoolNegate),
),
Def::assign(
"entry_jmp_def_2",
Variable::mock("Z", 8),
Expression::var("Z", 8).un_op(UnOpType::IntNegate),
),
], ],
jmps: vec![Term { jmps: vec![Term {
tid: Tid::new("call_to_called_function"), tid: Tid::new("call_to_called_function"),
...@@ -92,37 +80,13 @@ fn get_mock_entry_block() -> Term<Blk> { ...@@ -92,37 +80,13 @@ fn get_mock_entry_block() -> Term<Blk> {
Term { Term {
tid: Tid::new("entry_block"), tid: Tid::new("entry_block"),
term: Blk { term: Blk {
defs: vec![ defs: defs![
Def::assign( "tid_1: Z:8 = -(42:4)",
"tid_1", "tid_2: X:8 = -(Y:8)",
Variable::mock("Z", 8), "tid_3: Y:8 = X:8 + Y:8",
Expression::const_from_i32(42).un_op(UnOpType::IntNegate), "tid_4: X:8 = -(X:8)",
), "tid_5: Y:8 = -(Y:8)",
Def::assign( "tid_6: Y:8 = X:8 + Y:8"
"tid_2",
Variable::mock("X", 8),
Expression::var("Y", 8).un_op(UnOpType::IntNegate),
),
Def::assign(
"tid_3",
Variable::mock("Y", 8),
Expression::var("X", 8).plus(Expression::var("Y", 8)),
),
Def::assign(
"tid_4",
Variable::mock("X", 8),
Expression::var("X", 8).un_op(UnOpType::IntNegate),
),
Def::assign(
"tid_5",
Variable::mock("Y", 8),
Expression::var("Y", 8).un_op(UnOpType::IntNegate),
),
Def::assign(
"tid_6",
Variable::mock("Y", 8),
Expression::var("X", 8).plus(Expression::var("Y", 8)),
),
], ],
jmps: Vec::new(), jmps: Vec::new(),
indirect_jmp_targets: Vec::new(), indirect_jmp_targets: Vec::new(),
...@@ -149,17 +113,13 @@ fn inter_block_propagation() { ...@@ -149,17 +113,13 @@ fn inter_block_propagation() {
vec![ vec![
Def::assign( Def::assign(
"entry_jmp_def_1", "entry_jmp_def_1",
Variable::mock("X", 8), variable!("X:8"),
Expression::const_from_i32(42) expr!("-(42:4)").un_op(UnOpType::BoolNegate),
.un_op(UnOpType::IntNegate)
.un_op(UnOpType::BoolNegate),
), ),
Def::assign( Def::assign(
"entry_jmp_def_2", "entry_jmp_def_2",
Variable::mock("Z", 8), variable!("Z:8"),
Expression::const_from_i32(42) expr!("-(42:4)").un_op(UnOpType::IntNegate),
.un_op(UnOpType::IntNegate)
.un_op(UnOpType::IntNegate),
) )
] ]
) )
...@@ -181,11 +141,7 @@ fn no_propagation_on_calls() { ...@@ -181,11 +141,7 @@ fn no_propagation_on_calls() {
.unwrap() .unwrap()
.term .term
.defs, .defs,
vec![Def::assign( defs!["callee_def_1: Y:8 = Z:8"]
"callee_def_1",
Variable::mock("Y", 8),
Expression::var("Z", 8),
)]
) )
} }
#[test] #[test]
...@@ -214,10 +170,7 @@ fn insertion_table_update() { ...@@ -214,10 +170,7 @@ fn insertion_table_update() {
// Assignment is inserted into table, no other changes. // Assignment is inserted into table, no other changes.
assert_eq!( assert_eq!(
update.clone().unwrap(), update.clone().unwrap(),
HashMap::from([( HashMap::from([(variable!("Z:8"), expr!("-(42:4)"))])
Variable::mock("Z", 8),
Expression::const_from_i32(42).un_op(UnOpType::IntNegate)
)])
); );
let update = crate::analysis::forward_interprocedural_fixpoint::Context::update_def( let update = crate::analysis::forward_interprocedural_fixpoint::Context::update_def(
...@@ -229,14 +182,8 @@ fn insertion_table_update() { ...@@ -229,14 +182,8 @@ fn insertion_table_update() {
assert_eq!( assert_eq!(
update.clone().unwrap(), update.clone().unwrap(),
HashMap::from([ HashMap::from([
( (variable!("Z:8"), expr!("-(42:4)")),
Variable::mock("Z", 8), (variable!("X:8"), expr!("-(Y:8)"))
Expression::const_from_i32(42).un_op(UnOpType::IntNegate)
),
(
Variable::mock("X", 8),
Expression::var("Y", 8).un_op(UnOpType::IntNegate)
)
]) ])
); );
...@@ -248,10 +195,7 @@ fn insertion_table_update() { ...@@ -248,10 +195,7 @@ fn insertion_table_update() {
// Expression for X is removed and Assignment is not inserted. // Expression for X is removed and Assignment is not inserted.
assert_eq!( assert_eq!(
update.clone().unwrap(), update.clone().unwrap(),
HashMap::from([( HashMap::from([(variable!("Z:8"), expr!("-(42:4)")),])
Variable::mock("Z", 8),
Expression::const_from_i32(42).un_op(UnOpType::IntNegate)
),])
); );
let update = crate::analysis::forward_interprocedural_fixpoint::Context::update_def( let update = crate::analysis::forward_interprocedural_fixpoint::Context::update_def(
&context, &context,
...@@ -261,10 +205,7 @@ fn insertion_table_update() { ...@@ -261,10 +205,7 @@ fn insertion_table_update() {
// Expression for Y is removed and Assignment is not inserted. // Expression for Y is removed and Assignment is not inserted.
assert_eq!( assert_eq!(
update.clone().unwrap(), update.clone().unwrap(),
HashMap::from([( HashMap::from([(variable!("Z:8"), expr!("-(42:4)")),])
Variable::mock("Z", 8),
Expression::const_from_i32(42).un_op(UnOpType::IntNegate)
),])
); );
let update = crate::analysis::forward_interprocedural_fixpoint::Context::update_def( let update = crate::analysis::forward_interprocedural_fixpoint::Context::update_def(
...@@ -275,10 +216,7 @@ fn insertion_table_update() { ...@@ -275,10 +216,7 @@ fn insertion_table_update() {
// Assignment not inserted. // Assignment not inserted.
assert_eq!( assert_eq!(
update.clone().unwrap(), update.clone().unwrap(),
HashMap::from([( HashMap::from([(variable!("Z:8"), expr!("-(42:4)")),])
Variable::mock("Z", 8),
Expression::const_from_i32(42).un_op(UnOpType::IntNegate)
),])
); );
let update = crate::analysis::forward_interprocedural_fixpoint::Context::update_def( let update = crate::analysis::forward_interprocedural_fixpoint::Context::update_def(
...@@ -289,10 +227,7 @@ fn insertion_table_update() { ...@@ -289,10 +227,7 @@ fn insertion_table_update() {
// Assignment not inserted. // Assignment not inserted.
assert_eq!( assert_eq!(
update.clone().unwrap(), update.clone().unwrap(),
HashMap::from([( HashMap::from([(variable!("Z:8"), expr!("-(42:4)")),])
Variable::mock("Z", 8),
Expression::const_from_i32(42).un_op(UnOpType::IntNegate)
),])
); );
} }
#[test] #[test]
...@@ -300,35 +235,12 @@ fn insertion_table_update() { ...@@ -300,35 +235,12 @@ fn insertion_table_update() {
fn expressions_inserted() { fn expressions_inserted() {
let mut project = mock_project(); let mut project = mock_project();
propagate_input_expression(&mut project); propagate_input_expression(&mut project);
let result_def_entry_block = vec![ let result_def_entry_block = defs![
Def::assign( "tid_1: Z:8 = -(42:4)",
"tid_1", "tid_2: X:8 = -(Y:8)",
Variable::mock("Z", 8), "tid_3: Y:8 = -(Y:8) + Y:8",
Expression::const_from_i32(42).un_op(UnOpType::IntNegate), "tid_4: X:8 = -(X:8)",
), "tid_6: Y:8 = X:8 + -(Y:8)"
Def::assign(
"tid_2",
Variable::mock("X", 8),
Expression::var("Y", 8).un_op(UnOpType::IntNegate),
),
Def::assign(
"tid_3",
Variable::mock("Y", 8),
Expression::var("Y", 8)
.un_op(UnOpType::IntNegate)
.plus(Expression::var("Y", 8)),
),
Def::assign(
"tid_4",
Variable::mock("X", 8),
Expression::var("X", 8).un_op(UnOpType::IntNegate),
),
// tid_5 is removed by merge_def_assignments_to_same_var()
Def::assign(
"tid_6",
Variable::mock("Y", 8),
Expression::var("X", 8).plus(Expression::var("Y", 8).un_op(UnOpType::IntNegate)),
),
]; ];
assert_eq!( assert_eq!(
project project
...@@ -357,17 +269,13 @@ fn expressions_inserted() { ...@@ -357,17 +269,13 @@ fn expressions_inserted() {
vec![ vec![
Def::assign( Def::assign(
"entry_jmp_def_1", "entry_jmp_def_1",
Variable::mock("X", 8), variable!("X:8"),
Expression::const_from_i32(42) expr!("-(42:4)").un_op(UnOpType::BoolNegate),
.un_op(UnOpType::IntNegate)
.un_op(UnOpType::BoolNegate),
), ),
Def::assign( Def::assign(
"entry_jmp_def_2", "entry_jmp_def_2",
Variable::mock("Z", 8), variable!("Z:8"),
Expression::const_from_i32(42) expr!("-(42:4)").un_op(UnOpType::IntNegate)
.un_op(UnOpType::IntNegate)
.un_op(UnOpType::IntNegate)
) )
] ]
); );
...@@ -382,10 +290,6 @@ fn expressions_inserted() { ...@@ -382,10 +290,6 @@ fn expressions_inserted() {
.blocks[0] .blocks[0]
.term .term
.defs, .defs,
vec![Def::assign( defs!["callee_def_1: Y:8 = Z:8"]
"callee_def_1",
Variable::mock("Y", 8),
Expression::var("Z", 8),
)]
); );
} }
use super::*; use super::*;
use crate::{bitvec, variable};
use std::collections::HashSet; use std::collections::HashSet;
#[test] #[test]
...@@ -25,20 +26,20 @@ fn test_compute_return_values_of_call() { ...@@ -25,20 +26,20 @@ fn test_compute_return_values_of_call() {
&call, &call,
); );
let expected_val = DataDomain::from_target( let expected_val = DataDomain::from_target(
AbstractIdentifier::from_var(Tid::new("call_tid"), &Variable::mock("RAX", 8)), AbstractIdentifier::from_var(Tid::new("call_tid"), &variable!("RAX:8")),
Bitvector::from_i64(0).into(), bitvec!("0x0:8").into(),
); );
assert_eq!(return_values.iter().len(), 3); assert_eq!(return_values.iter().len(), 3);
assert_eq!(return_values[0], (&Variable::mock("RAX", 8), expected_val)); assert_eq!(return_values[0], (&variable!("RAX:8"), expected_val));
// Test returning a known value. // Test returning a known value.
let param_ref = DataDomain::from_target( let param_ref = DataDomain::from_target(
AbstractIdentifier::from_var(Tid::new("callee"), &Variable::mock("RDI", 8)), AbstractIdentifier::from_var(Tid::new("callee"), &variable!("RDI:8")),
Bitvector::from_i64(0).into(), bitvec!("0x0:8").into(),
); );
callee_state.set_register(&Variable::mock("RAX", 8), param_ref); callee_state.set_register(&variable!("RAX:8"), param_ref);
let expected_val = DataDomain::from_target( let expected_val = DataDomain::from_target(
AbstractIdentifier::from_var(Tid::new("caller"), &Variable::mock("RDI", 8)), AbstractIdentifier::from_var(Tid::new("caller"), &variable!("RDI:8")),
Bitvector::from_i64(0).into(), bitvec!("0x0:8").into(),
); );
let return_values = context.compute_return_values_of_call( let return_values = context.compute_return_values_of_call(
&mut caller_state, &mut caller_state,
...@@ -47,7 +48,7 @@ fn test_compute_return_values_of_call() { ...@@ -47,7 +48,7 @@ fn test_compute_return_values_of_call() {
&call, &call,
); );
assert_eq!(return_values.iter().len(), 3); assert_eq!(return_values.iter().len(), 3);
assert_eq!(return_values[0], (&Variable::mock("RAX", 8), expected_val)); assert_eq!(return_values[0], (&variable!("RAX:8"), expected_val));
} }
#[test] #[test]
...@@ -69,17 +70,17 @@ fn test_call_stub_handling() { ...@@ -69,17 +70,17 @@ fn test_call_stub_handling() {
assert_eq!( assert_eq!(
state.get_params_of_current_function(), state.get_params_of_current_function(),
vec![( vec![(
Arg::from_var(Variable::mock("r0", 4), None), Arg::from_var(variable!("r0:4"), None),
AccessPattern::new().with_read_flag() AccessPattern::new().with_read_flag()
)] )]
); );
assert_eq!( assert_eq!(
state.get_register(&Variable::mock("r0", 4)), state.get_register(&variable!("r0:4")),
DataDomain::from_target( DataDomain::from_target(
AbstractIdentifier::mock(call_tid, "r0", 4), AbstractIdentifier::mock(call_tid, "r0", 4),
Bitvector::from_i32(0).into() bitvec!("0x0:4").into()
) )
.merge(&Bitvector::zero(ByteSize::new(4).into()).into()) .merge(&bitvec!("0x0:4").into())
); );
// Test handling of sprintf call // Test handling of sprintf call
...@@ -89,7 +90,7 @@ fn test_call_stub_handling() { ...@@ -89,7 +90,7 @@ fn test_call_stub_handling() {
project.get_standard_calling_convention().unwrap(), project.get_standard_calling_convention().unwrap(),
); );
// Set the format string param register to a pointer to the string 'cat %s %s %s %s'. // Set the format string param register to a pointer to the string 'cat %s %s %s %s'.
state.set_register(&Variable::mock("r1", 4), Bitvector::from_i32(0x6000).into()); state.set_register(&variable!("r1:4"), bitvec!("0x6000:4").into());
let extern_symbol = ExternSymbol::mock_sprintf_symbol_arm(); let extern_symbol = ExternSymbol::mock_sprintf_symbol_arm();
let call_tid = Tid::new("call_sprintf"); let call_tid = Tid::new("call_sprintf");
context.handle_extern_symbol_call(&mut state, &extern_symbol, &call_tid); context.handle_extern_symbol_call(&mut state, &extern_symbol, &call_tid);
...@@ -97,14 +98,14 @@ fn test_call_stub_handling() { ...@@ -97,14 +98,14 @@ fn test_call_stub_handling() {
assert_eq!( assert_eq!(
params[0], params[0],
( (
Arg::from_var(Variable::mock("r0", 4), None), Arg::from_var(variable!("r0:4"), None),
AccessPattern::new_unknown_access() AccessPattern::new_unknown_access()
) )
); );
assert_eq!( assert_eq!(
params[1], params[1],
( (
Arg::from_var(Variable::mock("r2", 4), None), Arg::from_var(variable!("r2:4"), None),
AccessPattern::new() AccessPattern::new()
.with_read_flag() .with_read_flag()
.with_dereference_flag() .with_dereference_flag()
...@@ -121,15 +122,15 @@ fn test_get_global_mem_address() { ...@@ -121,15 +122,15 @@ fn test_get_global_mem_address() {
let context = Context::new(&project, &graph); let context = Context::new(&project, &graph);
// Check global address from abstract ID // Check global address from abstract ID
let global_address_id: DataDomain<BitvectorDomain> = DataDomain::from_target( let global_address_id: DataDomain<BitvectorDomain> = DataDomain::from_target(
AbstractIdentifier::from_global_address(&Tid::new("fn_tid"), &Bitvector::from_i32(0x2000)), AbstractIdentifier::from_global_address(&Tid::new("fn_tid"), &bitvec!("0x2000:4")),
Bitvector::from_i32(0x2).into(), bitvec!("0x2:4").into(),
); );
let result = context.get_global_mem_address(&global_address_id); let result = context.get_global_mem_address(&global_address_id);
assert_eq!(result, Some(Bitvector::from_i32(0x2002))); assert_eq!(result, Some(bitvec!("0x2002:4")));
// Check global address from absolute value // Check global address from absolute value
let global_address_const = Bitvector::from_i32(0x2003).into(); let global_address_const = bitvec!("0x2003:4").into();
let result = context.get_global_mem_address(&global_address_const); let result = context.get_global_mem_address(&global_address_const);
assert_eq!(result, Some(Bitvector::from_i32(0x2003))); assert_eq!(result, Some(bitvec!("0x2003:4")));
// Check global address not returned if it may not be unique // Check global address not returned if it may not be unique
let value = global_address_id.merge(&global_address_const); let value = global_address_id.merge(&global_address_const);
let result = context.get_global_mem_address(&value); let result = context.get_global_mem_address(&value);
......
...@@ -277,6 +277,7 @@ impl Default for FunctionSignature { ...@@ -277,6 +277,7 @@ impl Default for FunctionSignature {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::variable;
impl FunctionSignature { impl FunctionSignature {
/// Create a mock x64 function signature with 2 parameters, one of which is accessed mutably, /// Create a mock x64 function signature with 2 parameters, one of which is accessed mutably,
...@@ -287,11 +288,11 @@ pub mod tests { ...@@ -287,11 +288,11 @@ pub mod tests {
write_access_pattern.set_unknown_access_flags(); write_access_pattern.set_unknown_access_flags();
let parameters = HashMap::from_iter([ let parameters = HashMap::from_iter([
( (
Arg::from_var(Variable::mock("RDI", 8), None), Arg::from_var(variable!("RDI:8"), None),
AccessPattern::new(), AccessPattern::new(),
), ),
( (
Arg::from_var(Variable::mock("RSI", 8), None), Arg::from_var(variable!("RSI:8"), None),
write_access_pattern, write_access_pattern,
), ),
]); ]);
......
...@@ -444,7 +444,7 @@ impl State { ...@@ -444,7 +444,7 @@ impl State {
let regs = self let regs = self
.register .register
.iter() .iter()
.map(|(var, value)| (format!("{}", var), value.to_json_compact())) .map(|(var, value)| (format!("{var}"), value.to_json_compact()))
.collect(); .collect();
json_map.insert("Register".to_string(), serde_json::Value::Object(regs)); json_map.insert("Register".to_string(), serde_json::Value::Object(regs));
let access_patterns = self let access_patterns = self
...@@ -452,8 +452,8 @@ impl State { ...@@ -452,8 +452,8 @@ impl State {
.iter() .iter()
.map(|(id, pattern)| { .map(|(id, pattern)| {
( (
format!("{}", id), format!("{id}"),
serde_json::Value::String(format!("{}", pattern)), serde_json::Value::String(format!("{pattern}")),
) )
}) })
.collect(); .collect();
......
use super::*; use super::*;
use crate::{bitvec, expr, variable};
impl State { impl State {
/// Generate a mock state for an ARM-32 state. /// Generate a mock state for an ARM-32 state.
pub fn mock_arm32() -> State { pub fn mock_arm32() -> State {
State::new( State::new(
&Tid::new("mock_fn"), &Tid::new("mock_fn"),
&Variable::mock("sp", 4), &variable!("sp:4"),
&CallingConvention::mock_arm32(), &CallingConvention::mock_arm32(),
) )
} }
...@@ -14,7 +15,7 @@ impl State { ...@@ -14,7 +15,7 @@ impl State {
pub fn mock_x64(tid_name: &str) -> State { pub fn mock_x64(tid_name: &str) -> State {
State::new( State::new(
&Tid::new(tid_name), &Tid::new(tid_name),
&Variable::mock("RSP", 8), &variable!("RSP:8"),
&CallingConvention::mock_x64(), &CallingConvention::mock_x64(),
) )
} }
...@@ -22,7 +23,7 @@ impl State { ...@@ -22,7 +23,7 @@ impl State {
/// Mock an abstract ID representing the stack. /// Mock an abstract ID representing the stack.
fn mock_stack_id() -> AbstractIdentifier { fn mock_stack_id() -> AbstractIdentifier {
AbstractIdentifier::from_var(Tid::new("mock_fn"), &Variable::mock("sp", 4)) AbstractIdentifier::from_var(Tid::new("mock_fn"), &variable!("sp:4"))
} }
/// Mock an abstract ID of a stack parameter /// Mock an abstract ID of a stack parameter
...@@ -46,11 +47,8 @@ fn test_new() { ...@@ -46,11 +47,8 @@ fn test_new() {
// Assert that the register values are as expected // Assert that the register values are as expected
assert_eq!(state.register.len(), 7); // 6 parameter register plus stack pointer assert_eq!(state.register.len(), 7); // 6 parameter register plus stack pointer
assert_eq!( assert_eq!(
state.get_register(&Variable::mock("sp", 4)), state.get_register(&variable!("sp:4")),
DataDomain::from_target( DataDomain::from_target(mock_stack_id(), bitvec!("0x0:4").into())
mock_stack_id(),
Bitvector::zero(ByteSize::new(4).into()).into()
)
); );
// Check the generated tracked IDs // Check the generated tracked IDs
assert_eq!(state.tracked_ids.len(), 6); assert_eq!(state.tracked_ids.len(), 6);
...@@ -59,7 +57,7 @@ fn test_new() { ...@@ -59,7 +57,7 @@ fn test_new() {
state.get_register(id.unwrap_register()), state.get_register(id.unwrap_register()),
DataDomain::from_target( DataDomain::from_target(
id.clone(), id.clone(),
Bitvector::zero(id.unwrap_register().size.into()).into() bitvec!(format!("0:{}", id.unwrap_register().size)).into()
) )
); );
assert_eq!(access_pattern, &AccessPattern::new()); assert_eq!(access_pattern, &AccessPattern::new());
...@@ -69,21 +67,20 @@ fn test_new() { ...@@ -69,21 +67,20 @@ fn test_new() {
#[test] #[test]
fn test_store_and_load_from_stack() { fn test_store_and_load_from_stack() {
let mut state = State::mock_arm32(); let mut state = State::mock_arm32();
let address = DataDomain::from_target(mock_stack_id(), Bitvector::from_i32(-4).into()); let address = DataDomain::from_target(mock_stack_id(), bitvec!("-4:4").into());
let value: DataDomain<BitvectorDomain> = Bitvector::from_i32(0).into(); let value: DataDomain<BitvectorDomain> = bitvec!("0x0:4").into();
// write and load a value to the current stack frame // write and load a value to the current stack frame
state.write_value(address.clone(), value.clone()); state.write_value(address.clone(), value.clone());
assert_eq!(state.stack.iter().len(), 1); assert_eq!(state.stack.iter().len(), 1);
assert_eq!( assert_eq!(
state.stack.get(Bitvector::from_i32(-4), ByteSize::new(4)), state.stack.get(bitvec!("-4:4"), ByteSize::new(4)),
value.clone() value.clone()
); );
assert_eq!(state.load_value(address, ByteSize::new(4), None), value); assert_eq!(state.load_value(address, ByteSize::new(4), None), value);
// Load a parameter register and check that the parameter gets generated // Load a parameter register and check that the parameter gets generated
let address = DataDomain::from_target(mock_stack_id(), Bitvector::from_i32(4).into()); let address = DataDomain::from_target(mock_stack_id(), bitvec!("0x4:4").into());
let stack_param_id = mock_stack_param_id(4, 4); let stack_param_id = mock_stack_param_id(4, 4);
let stack_param = let stack_param = DataDomain::from_target(stack_param_id.clone(), bitvec!("0x0:4").into());
DataDomain::from_target(stack_param_id.clone(), Bitvector::from_i32(0).into());
assert_eq!(state.tracked_ids.iter().len(), 6); assert_eq!(state.tracked_ids.iter().len(), 6);
assert_eq!( assert_eq!(
state.load_value(address.clone(), ByteSize::new(4), None), state.load_value(address.clone(), ByteSize::new(4), None),
...@@ -104,22 +101,21 @@ fn test_store_and_load_from_stack() { ...@@ -104,22 +101,21 @@ fn test_store_and_load_from_stack() {
fn test_load_unsized_from_stack() { fn test_load_unsized_from_stack() {
let mut state = State::mock_arm32(); let mut state = State::mock_arm32();
// Load an existing stack param (generated by a sized load at the same address) // Load an existing stack param (generated by a sized load at the same address)
let address = DataDomain::from_target(mock_stack_id(), Bitvector::from_i32(0).into()); let address = DataDomain::from_target(mock_stack_id(), bitvec!("0x0:4").into());
let stack_param_id = mock_stack_param_id(0, 4); let stack_param_id = mock_stack_param_id(0, 4);
let stack_param = let stack_param = DataDomain::from_target(stack_param_id.clone(), bitvec!("0x0:4").into());
DataDomain::from_target(stack_param_id.clone(), Bitvector::from_i32(0).into());
state.load_value(address, ByteSize::new(4), None); state.load_value(address, ByteSize::new(4), None);
let unsized_load = state.load_unsized_value_from_stack(Bitvector::from_i32(0)); let unsized_load = state.load_unsized_value_from_stack(bitvec!("0x0:4").into());
assert_eq!(unsized_load, stack_param); assert_eq!(unsized_load, stack_param);
assert!(state.tracked_ids.get(&stack_param_id).is_some()); assert!(state.tracked_ids.get(&stack_param_id).is_some());
// Load a non-existing stack param // Load a non-existing stack param
let stack_param_id = mock_stack_param_id(4, 1); let stack_param_id = mock_stack_param_id(4, 1);
let stack_param = DataDomain::from_target(stack_param_id.clone(), Bitvector::from_i8(0).into()); let stack_param = DataDomain::from_target(stack_param_id.clone(), bitvec!("0x0:1").into());
let unsized_load = state.load_unsized_value_from_stack(Bitvector::from_i32(4)); let unsized_load = state.load_unsized_value_from_stack(bitvec!("0x4:4"));
assert_eq!(unsized_load, stack_param); assert_eq!(unsized_load, stack_param);
assert!(state.tracked_ids.get(&stack_param_id).is_some()); assert!(state.tracked_ids.get(&stack_param_id).is_some());
// Unsized load from the current stack frame // Unsized load from the current stack frame
let unsized_load = state.load_unsized_value_from_stack(Bitvector::from_i32(-4)); let unsized_load = state.load_unsized_value_from_stack(bitvec!("-4:4"));
assert_eq!(unsized_load, DataDomain::new_top(ByteSize::new(1))); assert_eq!(unsized_load, DataDomain::new_top(ByteSize::new(1)));
} }
...@@ -127,16 +123,16 @@ fn test_load_unsized_from_stack() { ...@@ -127,16 +123,16 @@ fn test_load_unsized_from_stack() {
fn test_eval() { fn test_eval() {
let mut state = State::mock_arm32(); let mut state = State::mock_arm32();
// Test the eval method // Test the eval method
let expr = Expression::Var(Variable::mock("sp", 4)).plus_const(42); let expr = expr!("sp:4 + 42:4");
assert_eq!( assert_eq!(
state.eval(&expr), state.eval(&expr),
DataDomain::from_target(mock_stack_id(), Bitvector::from_i32(42).into()) DataDomain::from_target(mock_stack_id(), bitvec!("42:4").into())
); );
// Test the eval_parameter_arg method // Test the eval_parameter_arg method
let arg = Arg::from_var(Variable::mock("sp", 4), None); let arg = Arg::from_var(variable!("sp:4"), None);
assert_eq!( assert_eq!(
state.eval_parameter_arg(&arg), state.eval_parameter_arg(&arg),
DataDomain::from_target(mock_stack_id(), Bitvector::from_i32(0).into()) DataDomain::from_target(mock_stack_id(), bitvec!("0x0:4").into())
); );
} }
...@@ -146,9 +142,8 @@ fn test_extern_symbol_handling() { ...@@ -146,9 +142,8 @@ fn test_extern_symbol_handling() {
let extern_symbol = ExternSymbol::mock_arm32("mock_symbol"); let extern_symbol = ExternSymbol::mock_arm32("mock_symbol");
let cconv = CallingConvention::mock_arm32(); let cconv = CallingConvention::mock_arm32();
let call_tid = Tid::new("call_tid"); let call_tid = Tid::new("call_tid");
let param_id = AbstractIdentifier::from_var(Tid::new("mock_fn"), &Variable::mock("r0", 4)); let param_id = AbstractIdentifier::from_var(Tid::new("mock_fn"), &variable!("r0:4"));
let return_val_id = let return_val_id = AbstractIdentifier::from_var(Tid::new("call_tid"), &variable!("r0:4"));
AbstractIdentifier::from_var(Tid::new("call_tid"), &Variable::mock("r0", 4));
// Test extern symbol handling. // Test extern symbol handling.
state.handle_generic_extern_symbol( state.handle_generic_extern_symbol(
&call_tid, &call_tid,
...@@ -164,7 +159,7 @@ fn test_extern_symbol_handling() { ...@@ -164,7 +159,7 @@ fn test_extern_symbol_handling() {
.is_mutably_dereferenced(), .is_mutably_dereferenced(),
true true
); );
let return_val = state.get_register(&Variable::mock("r0", 4)); let return_val = state.get_register(&variable!("r0:4"));
assert_eq!(return_val.get_relative_values().iter().len(), 2); assert_eq!(return_val.get_relative_values().iter().len(), 2);
assert_eq!( assert_eq!(
return_val.get_relative_values().get(&param_id).unwrap(), return_val.get_relative_values().get(&param_id).unwrap(),
...@@ -179,7 +174,7 @@ fn test_extern_symbol_handling() { ...@@ -179,7 +174,7 @@ fn test_extern_symbol_handling() {
.get_relative_values() .get_relative_values()
.get(&return_val_id) .get(&return_val_id)
.unwrap(), .unwrap(),
&Bitvector::from_i32(0).into() &bitvec!("0:4").into()
); );
} }
...@@ -189,16 +184,16 @@ fn test_substitute_global_mem_address() { ...@@ -189,16 +184,16 @@ fn test_substitute_global_mem_address() {
let global_memory = RuntimeMemoryImage::mock(); let global_memory = RuntimeMemoryImage::mock();
// Test that addresses into non-writeable memory do not get substituted. // Test that addresses into non-writeable memory do not get substituted.
let global_address: DataDomain<BitvectorDomain> = Bitvector::from_i32(0x1000).into(); let global_address: DataDomain<BitvectorDomain> = bitvec!("0x1000:4").into();
let substituted_address = let substituted_address =
state.substitute_global_mem_address(global_address.clone(), &global_memory); state.substitute_global_mem_address(global_address.clone(), &global_memory);
assert_eq!(global_address, substituted_address); assert_eq!(global_address, substituted_address);
// Test substitution for addresses into writeable global memory. // Test substitution for addresses into writeable global memory.
let global_address: DataDomain<BitvectorDomain> = Bitvector::from_i32(0x2000).into(); let global_address: DataDomain<BitvectorDomain> = bitvec!("0x2000:4").into();
let substituted_address = state.substitute_global_mem_address(global_address, &global_memory); let substituted_address = state.substitute_global_mem_address(global_address, &global_memory);
let expected_global_id = AbstractIdentifier::from_global_address( let expected_global_id = AbstractIdentifier::from_global_address(
state.get_current_function_tid(), state.get_current_function_tid(),
&Bitvector::from_i32(0x2000), &bitvec!("0x2000:4"),
); );
assert_eq!( assert_eq!(
state.tracked_ids.get(&expected_global_id), state.tracked_ids.get(&expected_global_id),
...@@ -206,6 +201,6 @@ fn test_substitute_global_mem_address() { ...@@ -206,6 +201,6 @@ fn test_substitute_global_mem_address() {
); );
assert_eq!( assert_eq!(
substituted_address, substituted_address,
DataDomain::from_target(expected_global_id, Bitvector::from_i32(0).into()) DataDomain::from_target(expected_global_id, bitvec!("0x0:4").into())
); );
} }
...@@ -89,7 +89,7 @@ impl<'a> Context<'a> { ...@@ -89,7 +89,7 @@ impl<'a> Context<'a> {
pub fn log_debug(&self, result: Result<(), Error>, location: Option<&Tid>) { pub fn log_debug(&self, result: Result<(), Error>, location: Option<&Tid>) {
if let Err(err) = result { if let Err(err) = result {
let mut log_message = let mut log_message =
LogMessage::new_debug(format!("{}", err)).source("Pointer Inference"); LogMessage::new_debug(format!("{err}")).source("Pointer Inference");
if let Some(loc) = location { if let Some(loc) = location {
log_message = log_message.location(loc.clone()); log_message = log_message.location(loc.clone());
}; };
...@@ -286,7 +286,7 @@ impl<'a> Context<'a> { ...@@ -286,7 +286,7 @@ impl<'a> Context<'a> {
name: "CWE476".to_string(), name: "CWE476".to_string(),
version: VERSION.to_string(), version: VERSION.to_string(),
addresses: vec![tid.address.clone()], addresses: vec![tid.address.clone()],
tids: vec![format!("{}", tid)], tids: vec![format!("{tid}")],
symbols: Vec::new(), symbols: Vec::new(),
other: Vec::new(), other: Vec::new(),
description: format!( description: format!(
......
...@@ -157,8 +157,7 @@ impl<'a> PointerInference<'a> { ...@@ -157,8 +157,7 @@ impl<'a> PointerInference<'a> {
if !self.computation.has_stabilized() { if !self.computation.has_stabilized() {
let worklist_size = self.computation.get_worklist().len(); let worklist_size = self.computation.get_worklist().len();
self.log_info(format!( self.log_info(format!(
"Fixpoint did not stabilize. Remaining worklist size: {}", "Fixpoint did not stabilize. Remaining worklist size: {worklist_size}"
worklist_size,
)); ));
} }
if print_stats { if print_stats {
...@@ -172,11 +171,10 @@ impl<'a> PointerInference<'a> { ...@@ -172,11 +171,10 @@ impl<'a> PointerInference<'a> {
for (node_index, value) in self.computation.node_values().iter() { for (node_index, value) in self.computation.node_values().iter() {
let node = graph.node_weight(*node_index).unwrap(); let node = graph.node_weight(*node_index).unwrap();
if let Ok(string) = serde_yaml::to_string(&(node, value)) { if let Ok(string) = serde_yaml::to_string(&(node, value)) {
println!("{}", string); println!("{string}");
} else { } else {
println!( println!(
"Serializing failed at {:?} with {:?}", "Serializing failed at {node_index:?} with {:?}",
node_index,
serde_yaml::to_string(value) serde_yaml::to_string(value)
); );
} }
...@@ -192,7 +190,7 @@ impl<'a> PointerInference<'a> { ...@@ -192,7 +190,7 @@ impl<'a> PointerInference<'a> {
for (node_index, node_value) in self.computation.node_values().iter() { for (node_index, node_value) in self.computation.node_values().iter() {
let node = graph.node_weight(*node_index).unwrap(); let node = graph.node_weight(*node_index).unwrap();
if let NodeValue::Value(value) = node_value { if let NodeValue::Value(value) = node_value {
json_nodes.insert(format!("{}", node), value.to_json_compact()); json_nodes.insert(format!("{node}"), value.to_json_compact());
} }
} }
serde_json::Value::Object(json_nodes) serde_json::Value::Object(json_nodes)
...@@ -237,8 +235,7 @@ impl<'a> PointerInference<'a> { ...@@ -237,8 +235,7 @@ impl<'a> PointerInference<'a> {
} }
} }
self.log_info(format!( self.log_info(format!(
"Blocks with state: {} / {}", "Blocks with state: {stateful_blocks} / {all_blocks}"
stateful_blocks, all_blocks
)); ));
} }
......
...@@ -216,7 +216,7 @@ impl AbstractObject { ...@@ -216,7 +216,7 @@ impl AbstractObject {
.inner .inner
.memory .memory
.iter() .iter()
.map(|(index, value)| (format!("{}", index), value.to_json_compact())); .map(|(index, value)| (format!("{index}"), value.to_json_compact()));
elements.push(( elements.push((
"memory".to_string(), "memory".to_string(),
serde_json::Value::Object(memory.collect()), serde_json::Value::Object(memory.collect()),
......
...@@ -174,7 +174,7 @@ impl AbstractObjectList { ...@@ -174,7 +174,7 @@ impl AbstractObjectList {
use serde_json::*; use serde_json::*;
let mut object_map = Map::new(); let mut object_map = Map::new();
for (id, object) in self.objects.iter() { for (id, object) in self.objects.iter() {
object_map.insert(format!("{}", id), object.to_json_compact()); object_map.insert(format!("{id}"), object.to_json_compact());
} }
Value::Object(object_map) Value::Object(object_map)
} }
......
use super::super::ValueDomain;
use super::*;
use crate::analysis::pointer_inference::object::*;
use Expression::*;
fn bv(value: i64) -> ValueDomain {
ValueDomain::from(Bitvector::from_i64(value))
}
fn new_id(time: &str, register: &str) -> AbstractIdentifier {
AbstractIdentifier::new(
Tid::new(time),
AbstractLocation::Register(Variable::mock(register, ByteSize::new(8))),
)
}
fn register(name: &str) -> Variable {
Variable {
name: name.into(),
size: ByteSize::new(8),
is_temp: false,
}
}
fn reg_add(name: &str, value: i64) -> Expression {
Expression::Var(register(name)).plus_const(value)
}
fn reg_sub(name: &str, value: i64) -> Expression {
Expression::BinOp {
op: BinOpType::IntSub,
lhs: Box::new(Expression::Var(register(name))),
rhs: Box::new(Expression::Const(Bitvector::from_i64(value))),
}
}
#[test]
fn state() {
let global_memory = RuntimeMemoryImage::mock();
let mut state = State::new(&register("RSP"), Tid::new("time0"), BTreeSet::new());
let stack_id = new_id("time0", "RSP");
let stack_addr = Data::from_target(stack_id.clone(), bv(8));
state
.store_value(&stack_addr, &bv(42).into(), &global_memory)
.unwrap();
state.register.insert(register("RSP"), stack_addr.clone());
assert_eq!(
state
.load_value(&Var(register("RSP")), ByteSize::new(8), &global_memory)
.unwrap(),
bv(42).into()
);
let mut other_state = State::new(&register("RSP"), Tid::new("time0"), BTreeSet::new());
state.register.insert(register("RAX"), bv(42).into());
other_state
.register
.insert(register("RSP"), stack_addr.clone());
other_state.register.insert(register("RAX"), bv(42).into());
other_state.register.insert(register("RBX"), bv(35).into());
let merged_state = state.merge(&other_state);
assert_eq!(merged_state.register[&register("RAX")], bv(42).into());
assert_eq!(
merged_state
.get_register(&register("RBX"))
.get_absolute_value()
.unwrap(),
&bv(35).into()
);
assert!(merged_state.get_register(&register("RBX")).contains_top());
assert!(merged_state
.load_value(&Var(register("RSP")), ByteSize::new(8), &global_memory)
.unwrap()
.contains_top());
state.memory.add_abstract_object(
new_id("heap_time", "heap_obj"),
ByteSize::new(8),
Some(ObjectType::Heap),
);
assert_eq!(state.memory.get_num_objects(), 3);
state.remove_unreferenced_objects();
assert_eq!(state.memory.get_num_objects(), 2);
}
#[test]
fn handle_store() {
let global_memory = RuntimeMemoryImage::mock();
let mut state = State::new(&register("RSP"), Tid::new("time0"), BTreeSet::new());
let stack_id = new_id("time0", "RSP");
assert_eq!(
state.eval(&Var(register("RSP"))),
Data::from_target(stack_id.clone(), bv(0))
);
state.handle_register_assign(&register("RSP"), &reg_sub("RSP", 32));
assert_eq!(
state.eval(&Var(register("RSP"))),
Data::from_target(stack_id.clone(), bv(-32))
);
state.handle_register_assign(&register("RSP"), &reg_add("RSP", -8));
assert_eq!(
state.eval(&Var(register("RSP"))),
Data::from_target(stack_id.clone(), bv(-40))
);
state
.handle_store(
&reg_add("RSP", 8),
&Const(Bitvector::from_i64(1)),
&global_memory,
)
.unwrap();
state
.handle_store(
&reg_sub("RSP", 8),
&Const(Bitvector::from_i64(2)),
&global_memory,
)
.unwrap();
state
.handle_store(
&reg_add("RSP", -16),
&Const(Bitvector::from_i64(3)),
&global_memory,
)
.unwrap();
state.handle_register_assign(&register("RSP"), &reg_sub("RSP", 4));
assert_eq!(
state
.load_value(&reg_add("RSP", 12), ByteSize::new(8), &global_memory)
.unwrap(),
bv(1).into()
);
assert_eq!(
state
.load_value(&reg_sub("RSP", 4), ByteSize::new(8), &global_memory)
.unwrap(),
bv(2).into()
);
assert_eq!(
state
.load_value(&reg_add("RSP", -12), ByteSize::new(8), &global_memory)
.unwrap(),
bv(3).into()
);
}
#[test]
fn clear_parameters_on_the_stack_on_extern_calls() {
let global_memory = RuntimeMemoryImage::mock();
let mut state = State::new(&register("RSP"), Tid::new("time0"), BTreeSet::new());
state.register.insert(
register("RSP"),
Data::from_target(new_id("time0", "RSP"), bv(-20)),
);
// write something onto the stack
state
.handle_store(
&reg_add("RSP", 8),
&Const(Bitvector::from_i64(42)),
&global_memory,
)
.unwrap();
// create an extern symbol which uses the value on the stack as a parameter
let stack_param = Arg::Stack {
address: reg_add("RSP", 8),
size: ByteSize::new(8),
data_type: None,
};
let extern_symbol = ExternSymbol {
tid: Tid::new("symbol"),
addresses: vec![],
name: "my_extern_symbol".into(),
calling_convention: None,
parameters: vec![stack_param],
return_values: Vec::new(),
no_return: false,
has_var_args: false,
};
// check the value before
let pointer = Data::from_target(new_id("time0", "RSP"), bv(-12));
assert_eq!(
state.memory.get_value(&pointer, ByteSize::new(8)),
bv(42).into()
);
// clear stack parameter
state
.clear_stack_parameter(&extern_symbol, &global_memory)
.unwrap();
// check the value after
assert_eq!(
state.memory.get_value(&pointer, ByteSize::new(8)),
Data::new_top(ByteSize::new(8))
);
}
#[test]
fn reachable_ids_under_and_overapproximation() {
let global_memory = RuntimeMemoryImage::mock();
let mut state = State::new(&register("RSP"), Tid::new("func_tid"), BTreeSet::new());
let stack_id = new_id("func_tid", "RSP");
let heap_id = new_id("heap_obj", "RAX");
let stack_address: Data = Data::from_target(stack_id.clone(), Bitvector::from_i64(-8).into());
let heap_address: Data = Data::from_target(heap_id.clone(), Bitvector::from_i64(0).into());
// Add the heap object to the state, so that it can be recursively searched.
state
.memory
.add_abstract_object(heap_id.clone(), ByteSize::new(8), Some(ObjectType::Heap));
state
.store_value(&stack_address, &heap_address, &global_memory)
.unwrap();
let reachable_ids: BTreeSet<AbstractIdentifier> = vec![stack_id.clone()].into_iter().collect();
assert_eq!(
state.add_directly_reachable_ids_to_id_set(reachable_ids.clone()),
vec![stack_id.clone(), heap_id.clone()]
.into_iter()
.collect()
);
assert_eq!(
state.add_recursively_referenced_ids_to_id_set(reachable_ids.clone()),
vec![stack_id.clone(), heap_id.clone()]
.into_iter()
.collect()
);
let _ = state.store_value(
&Data::from_target(stack_id.clone(), ValueDomain::new_top(ByteSize::new(8))),
&Bitvector::from_i64(42).into(),
&global_memory,
);
assert_eq!(
state.add_directly_reachable_ids_to_id_set(reachable_ids.clone()),
vec![stack_id.clone(), heap_id.clone()]
.into_iter()
.collect()
);
assert_eq!(
state.add_recursively_referenced_ids_to_id_set(reachable_ids.clone()),
vec![stack_id.clone(), heap_id.clone()]
.into_iter()
.collect()
);
}
#[test]
fn global_mem_access() {
let global_memory = RuntimeMemoryImage::mock();
let mut state = State::new(
&register("RSP"),
Tid::new("func_tid"),
BTreeSet::from([0x2000]),
);
// global read-only address
let address_expr = Expression::Const(Bitvector::from_u64(0x1000));
assert_eq!(
state
.load_value(&address_expr, ByteSize::new(4), &global_memory)
.unwrap(),
Bitvector::from_u32(0xb3b2b1b0).into() // note that we read in little-endian byte order
);
assert!(state
.write_to_address(
&address_expr,
&DataDomain::new_top(ByteSize::new(4)),
&global_memory
)
.is_err());
// global writeable address
let address_expr = Expression::Const(Bitvector::from_u64(0x2000));
assert_eq!(
state
.load_value(&address_expr, ByteSize::new(4), &global_memory)
.unwrap(),
DataDomain::new_top(ByteSize::new(4))
);
assert!(state
.write_to_address(
&address_expr,
&Bitvector::from_u32(21).into(),
&global_memory
)
.is_ok());
assert_eq!(
state
.load_value(&address_expr, ByteSize::new(4), &global_memory)
.unwrap(),
Bitvector::from_u32(21).into()
);
// invalid global address
let address_expr = Expression::Const(Bitvector::from_u64(0x3456));
assert!(state
.load_value(&address_expr, ByteSize::new(4), &global_memory)
.is_err());
assert!(state
.write_to_address(
&address_expr,
&DataDomain::new_top(ByteSize::new(4)),
&global_memory
)
.is_err());
}
/// Test expression specialization except for binary operations.
#[test]
fn specialize_by_expression_results() {
let mut base_state = State::new(&register("RSP"), Tid::new("func_tid"), BTreeSet::new());
base_state.set_register(
&register("RAX"),
IntervalDomain::new(Bitvector::from_i64(5), Bitvector::from_i64(10)).into(),
);
// Expr = Var(RAX)
let mut state = base_state.clone();
let x = state
.specialize_by_expression_result(&Expression::var("RAX", 8), Bitvector::from_i64(7).into());
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
Bitvector::from_i64(7).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::var("RAX", 8),
Bitvector::from_i64(-20).into(),
);
assert!(x.is_err());
let mut state = base_state.clone();
let abstract_id = AbstractIdentifier::new(
Tid::new("heap_obj"),
AbstractLocation::from_var(&register("RAX")).unwrap(),
);
state.set_register(
&register("RAX"),
Data::from_target(abstract_id.clone(), IntervalDomain::mock(0, 50)),
);
let x = state.specialize_by_expression_result(
&Expression::var("RAX", 8),
Data::from_target(abstract_id.clone(), IntervalDomain::mock(20, 70)),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
Data::from_target(abstract_id, IntervalDomain::mock(20, 50))
);
// Expr = Const
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::Const(Bitvector::from_i64(-20)),
Bitvector::from_i64(-20).into(),
);
assert!(x.is_ok());
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::Const(Bitvector::from_i64(5)),
Bitvector::from_i64(-20).into(),
);
assert!(x.is_err());
// Expr = -Var(RAX)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::var("RAX", 8).un_op(UnOpType::Int2Comp),
Bitvector::from_i64(-7).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
Bitvector::from_i64(7).into()
);
// Expr = IntSExt(Var(EAX))
let mut state = State::new(&register("RSP"), Tid::new("func_tid"), BTreeSet::new());
let eax_register = Variable {
name: "EAX".to_string(),
size: ByteSize::new(4),
is_temp: false,
};
state.set_register(
&eax_register,
IntervalDomain::new(Bitvector::from_i32(-10), Bitvector::from_i32(-5)).into(),
);
let x = state.specialize_by_expression_result(
&Expression::Var(eax_register.clone()).cast(CastOpType::IntSExt),
Bitvector::from_i64(-7).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&eax_register),
Bitvector::from_i32(-7).into()
);
// Expr = Subpiece(Var(RAX))
let mut state = State::new(&register("RSP"), Tid::new("func_tid"), BTreeSet::new());
let rax_register = Variable {
name: "RAX".to_string(),
size: ByteSize::new(8),
is_temp: false,
};
let x = state.specialize_by_expression_result(
&Expression::Var(rax_register.clone()).subpiece(ByteSize::new(0), ByteSize::new(1)),
Bitvector::from_i8(5).into(),
);
assert!(x.is_ok());
assert!(state.get_register(&rax_register).is_top());
state.set_register(&rax_register, IntervalDomain::mock(3, 10).into());
let x = state.specialize_by_expression_result(
&Expression::Var(rax_register.clone()).subpiece(ByteSize::new(0), ByteSize::new(1)),
Bitvector::from_i8(5).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&rax_register),
IntervalDomain::mock(5, 5).into()
);
}
/// Test expression specialization for binary operations
/// except equality and inequality operations
#[test]
fn specialize_by_binop() {
let base_state = State::new(&register("RSP"), Tid::new("func_tid"), BTreeSet::new());
// Expr = RAX + Const
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::var("RAX", 8).plus_const(20),
IntervalDomain::mock(5, 7).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock(-15, -13).into()
);
// Expr = RAX - Const
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::var("RAX", 8).minus_const(20),
Bitvector::from_i64(5).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
Bitvector::from_i64(25).into()
);
// Expr = RAX xor Const
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::var("RAX", 8)),
op: BinOpType::IntXOr,
rhs: Box::new(Expression::const_from_i64(3)),
},
Bitvector::from_i64(-1).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
Bitvector::from_i64(-4).into()
);
// Expr = (RAX or RBX == 0)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::var("RAX", 8)),
op: BinOpType::IntOr,
rhs: Box::new(Expression::var("RBX", 8)),
},
Bitvector::from_i64(0).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
Bitvector::from_i64(0).into()
);
assert_eq!(
state.get_register(&register("RBX")),
Bitvector::from_i64(0).into()
);
// Expr = (RAX or 0 == Const)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::var("RAX", 8)),
op: BinOpType::IntOr,
rhs: Box::new(Expression::const_from_i64(0)),
},
Bitvector::from_i64(42).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
Bitvector::from_i64(42).into()
);
// Expr = (FLAG1 bool_and FLAG2 == 1)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::Var(Variable::mock("FLAG1", 1u64))),
op: BinOpType::BoolAnd,
rhs: Box::new(Expression::Var(Variable::mock("FLAG2", 1u64))),
},
Bitvector::from_u8(1).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&Variable::mock("FLAG1", 1u64)),
Bitvector::from_u8(1).into()
);
assert_eq!(
state.get_register(&Variable::mock("FLAG2", 1u64)),
Bitvector::from_u8(1).into()
);
// Expr = (FLAG bool_and 1 = Const)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::Const(Bitvector::from_u8(1))),
op: BinOpType::BoolAnd,
rhs: Box::new(Expression::Var(Variable::mock("FLAG", 1u64))),
},
Bitvector::from_u8(0).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&Variable::mock("FLAG", 1u64)),
Bitvector::from_u8(0).into()
);
}
/// Test expression specialization for comparison operations `==` and `!=`.
#[test]
fn specialize_by_equality_comparison() {
let mut base_state = State::new(&register("RSP"), Tid::new("func_tid"), BTreeSet::new());
base_state.set_register(&register("RAX"), IntervalDomain::mock(0, 50).into());
// Expr = RAX == Const
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::const_from_i64(23)),
op: BinOpType::IntEqual,
rhs: Box::new(Expression::var("RAX", 8)),
},
Bitvector::from_u8(1).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
Bitvector::from_i64(23).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::const_from_i64(23)),
op: BinOpType::IntNotEqual,
rhs: Box::new(Expression::var("RAX", 8)),
},
Bitvector::from_u8(0).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
Bitvector::from_i64(23).into()
);
// Expr = RAX != Const
let mut state = base_state.clone();
state.set_register(&register("RAX"), Bitvector::from_i64(23).into());
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::const_from_i64(23)),
op: BinOpType::IntNotEqual,
rhs: Box::new(Expression::var("RAX", 8)),
},
Bitvector::from_u8(1).into(),
);
assert!(x.is_err());
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::const_from_i64(100)),
op: BinOpType::IntEqual,
rhs: Box::new(Expression::var("RAX", 8)),
},
Bitvector::from_u8(0).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock_with_bounds(None, 0, 50, Some(99)).into()
);
}
/// Test expression specialization for signed comparison operations `<` and `<=`.
#[test]
fn specialize_by_signed_comparison_op() {
let mut base_state = State::new(&register("RSP"), Tid::new("func_tid"), BTreeSet::new());
let interval = IntervalDomain::mock(5, 10);
base_state.set_register(&register("RAX"), interval.into());
// Expr = RAX < Const (signed)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::const_from_i64(7)),
op: BinOpType::IntSLess,
rhs: Box::new(Expression::Var(register("RAX"))),
},
Bitvector::from_u8(1).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock(8, 10).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::const_from_i64(15)),
op: BinOpType::IntSLess,
rhs: Box::new(Expression::Var(register("RAX"))),
},
Bitvector::from_u8(0).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock_with_bounds(None, 5, 10, Some(15)).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::Var(register("RAX"))),
op: BinOpType::IntSLess,
rhs: Box::new(Expression::Const(Bitvector::signed_min_value(
ByteSize::new(8).into(),
))),
},
Bitvector::from_u8(1).into(),
);
assert!(x.is_err());
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::Var(register("RAX"))),
op: BinOpType::IntSLess,
rhs: Box::new(Expression::const_from_i64(7)),
},
Bitvector::from_u8(0).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock(7, 10).into()
);
// Expr = RAX <= Const (signed)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::const_from_i64(7)),
op: BinOpType::IntSLessEqual,
rhs: Box::new(Expression::Var(register("RAX"))),
},
Bitvector::from_u8(1).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock(7, 10).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::const_from_i64(15)),
op: BinOpType::IntSLessEqual,
rhs: Box::new(Expression::Var(register("RAX"))),
},
Bitvector::from_u8(0).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock_with_bounds(None, 5, 10, Some(14)).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::Var(register("RAX"))),
op: BinOpType::IntSLessEqual,
rhs: Box::new(Expression::Const(Bitvector::signed_min_value(
ByteSize::new(8).into(),
))),
},
Bitvector::from_u8(1).into(),
);
assert!(x.is_err());
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::Var(register("RAX"))),
op: BinOpType::IntSLessEqual,
rhs: Box::new(Expression::const_from_i64(7)),
},
Bitvector::from_u8(0).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock(8, 10).into()
);
}
/// Test expression specialization for unsigned comparison operations `<` and `<=`.
#[test]
fn specialize_by_unsigned_comparison_op() {
let mut base_state = State::new(&register("RSP"), Tid::new("func_tid"), BTreeSet::new());
let interval = IntervalDomain::mock(-5, 10);
base_state.set_register(&register("RAX"), interval.into());
// Expr = RAX < Const (unsigned)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::const_from_i64(7)),
op: BinOpType::IntLess,
rhs: Box::new(Expression::Var(register("RAX"))),
},
Bitvector::from_u8(1).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock(-5, 10).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::const_from_i64(15)),
op: BinOpType::IntLess,
rhs: Box::new(Expression::Var(register("RAX"))),
},
Bitvector::from_u8(0).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock_with_bounds(None, 0, 10, Some(15)).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::Var(register("RAX"))),
op: BinOpType::IntLess,
rhs: Box::new(Expression::const_from_i64(0)),
},
Bitvector::from_u8(1).into(),
);
assert!(x.is_err());
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::Var(register("RAX"))),
op: BinOpType::IntLess,
rhs: Box::new(Expression::const_from_i64(-20)),
},
Bitvector::from_u8(0).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock_with_bounds(Some(-20), -5, -1, None).into()
);
// Expr = RAX <= Const (unsigned)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::const_from_i64(7)),
op: BinOpType::IntLessEqual,
rhs: Box::new(Expression::Var(register("RAX"))),
},
Bitvector::from_u8(1).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock(-5, 10).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::const_from_i64(15)),
op: BinOpType::IntLessEqual,
rhs: Box::new(Expression::Var(register("RAX"))),
},
Bitvector::from_u8(0).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock_with_bounds(None, 0, 10, Some(14)).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::Var(register("RAX"))),
op: BinOpType::IntLessEqual,
rhs: Box::new(Expression::const_from_i64(0)),
},
Bitvector::from_u8(1).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
Bitvector::from_i64(0).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&Expression::BinOp {
lhs: Box::new(Expression::Var(register("RAX"))),
op: BinOpType::IntLessEqual,
rhs: Box::new(Expression::const_from_i64(-20)),
},
Bitvector::from_u8(0).into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&register("RAX")),
IntervalDomain::mock_with_bounds(Some(-19), -5, -1, None).into()
);
}
#[test]
fn specialize_pointer_comparison() {
let mut state = State::new(&register("RSP"), Tid::new("func_tid"), BTreeSet::new());
let interval = IntervalDomain::mock(-5, 10);
state.set_register(
&register("RAX"),
Data::from_target(new_id("func_tid", "RSP"), interval.into()),
);
let interval = IntervalDomain::mock(20, 20);
state.set_register(
&register("RBX"),
Data::from_target(new_id("func_tid", "RSP"), interval.into()),
);
let expression = Expression::BinOp {
op: BinOpType::IntEqual,
lhs: Box::new(Expression::Var(register("RAX"))),
rhs: Box::new(Expression::Var(register("RBX"))),
};
assert!(state
.clone()
.specialize_by_expression_result(&expression, Bitvector::from_i8(1).into())
.is_err());
let specialized_interval = IntervalDomain::mock_with_bounds(None, -5, 10, Some(19));
let specialized_pointer =
Data::from_target(new_id("func_tid", "RSP"), specialized_interval.into());
assert!(state
.specialize_by_expression_result(&expression, Bitvector::from_i8(0).into())
.is_ok());
assert_eq!(state.get_register(&register("RAX")), specialized_pointer);
}
/// Test that value specialization does not introduce unintended widening hints.
/// This is a regression test for cases where pointer comparisons introduced two-sided bounds
/// (resulting in two-sided widenings) instead of one-sided bounds.
#[test]
fn test_widening_hints_after_pointer_specialization() {
let mut state = State::new(&register("RSP"), Tid::new("func_tid"), BTreeSet::new());
state.set_register(
&register("RAX"),
Data::from_target(new_id("func_tid", "RSP"), Bitvector::from_i64(10).into()),
);
state.set_register(
&register("RBX"),
Data::from_target(new_id("func_tid", "RSP"), Bitvector::from_i64(10).into()),
);
let expression = Expression::BinOp {
op: BinOpType::IntSub,
lhs: Box::new(Expression::Var(Variable::mock("RAX", 8))),
rhs: Box::new(Expression::Var(Variable::mock("RBX", 8))),
};
let neq_expression = Expression::BinOp {
op: BinOpType::IntNotEqual,
lhs: Box::new(Expression::Const(Bitvector::from_i64(5))),
rhs: Box::new(expression),
};
state
.specialize_by_expression_result(&neq_expression, Bitvector::from_i8(1).into())
.unwrap();
state
.specialize_by_expression_result(&neq_expression, Bitvector::from_i8(1).into())
.unwrap();
let offset_with_upper_bound: IntervalDomain = Bitvector::from_i64(10).into();
let offset_with_upper_bound = offset_with_upper_bound
.add_signed_less_equal_bound(&Bitvector::from_i64(14))
.unwrap();
let expected_val = Data::from_target(new_id("func_tid", "RSP"), offset_with_upper_bound);
assert_eq!(state.get_register(&Variable::mock("RAX", 8)), expected_val);
let offset_with_lower_bound: IntervalDomain = Bitvector::from_i64(10).into();
let offset_with_lower_bound = offset_with_lower_bound
.add_signed_greater_equal_bound(&Bitvector::from_i64(6))
.unwrap();
let expected_val = Data::from_target(new_id("func_tid", "RSP"), offset_with_lower_bound);
assert_eq!(state.get_register(&Variable::mock("RBX", 8)), expected_val);
}
#[test]
fn test_check_def_for_null_dereferences() {
let mut state = State::new(&register("RSP"), Tid::new("func_tid"), BTreeSet::new());
let var_rax = Variable::mock("RAX", 8);
let def = Def::load(
"load_def",
Variable::mock("RBX", 8),
Expression::Var(var_rax.clone()),
);
state.set_register(&var_rax, Bitvector::from_i64(0).into());
assert!(state.check_def_for_null_dereferences(&def).is_err());
state.set_register(&var_rax, Bitvector::from_i64(12345).into());
assert_eq!(
state.check_def_for_null_dereferences(&def).ok(),
Some(false)
);
state.set_register(&var_rax, IntervalDomain::mock(-2000, 5).into());
assert_eq!(state.check_def_for_null_dereferences(&def).ok(), Some(true));
assert_eq!(
state.get_register(&var_rax),
IntervalDomain::mock(-2000, -1024).into()
);
let mut address = state.get_register(&register("RSP"));
address.set_contains_top_flag();
address.set_absolute_value(Some(IntervalDomain::mock(0, 0xffff)));
state.set_register(&var_rax, address);
assert_eq!(state.check_def_for_null_dereferences(&def).ok(), Some(true));
}
#[test]
fn from_fn_sig() {
let fn_sig = FunctionSignature::mock_x64();
let state = State::from_fn_sig(&fn_sig, &Variable::mock("RSP", 8), Tid::new("func"));
assert_eq!(state.memory.get_num_objects(), 3);
assert_eq!(
*state.memory.get_object(&new_id("func", "RSI")).unwrap(),
AbstractObject::new(None, ByteSize::new(8))
);
assert_eq!(
state.get_register(&Variable::mock("RSP", 8)),
Data::from_target(new_id("func", "RSP"), bv(0).into())
);
assert_eq!(
state.get_register(&Variable::mock("RDI", 8)),
Data::from_target(new_id("func", "RDI"), bv(0).into())
);
assert_eq!(
state.get_register(&Variable::mock("RSI", 8)),
Data::from_target(new_id("func", "RSI"), bv(0).into())
);
}
#[test]
fn add_param_object_from_callee() {
let global_memory = RuntimeMemoryImage::empty(true);
let mut generic_state =
State::new(&Variable::mock("RSP", 8), Tid::new("func"), BTreeSet::new());
generic_state
.write_to_address(
&Expression::Var(Variable::mock("RSP", 8)).plus_const(-8),
&bv(1).into(),
&global_memory,
)
.unwrap();
let mut param_object = AbstractObject::new(None, ByteSize::new(8));
param_object.set_value(bv(2).into(), &bv(0).into()).unwrap();
let mut param_value = Data::from_target(new_id("func", "RSP"), bv(-16).into());
// Testcase 1: param object is unique
let mut state = generic_state.clone();
state
.add_param_object_from_callee(param_object.clone(), &param_value)
.unwrap();
let value = state
.load_value(
&Expression::Var(Variable::mock("RSP", 8)).plus_const(-8),
ByteSize::new(8),
&global_memory,
)
.unwrap();
assert_eq!(value.get_absolute_value().unwrap(), &bv(1).into());
assert!(value.contains_top());
let value = state
.load_value(
&Expression::Var(Variable::mock("RSP", 8)).plus_const(-16),
ByteSize::new(8),
&global_memory,
)
.unwrap();
assert_eq!(value.get_absolute_value().unwrap(), &bv(2).into());
assert!(!value.contains_top());
// Testcase 2: param object is not unique
let mut state = generic_state.clone();
param_value.set_contains_top_flag();
state
.add_param_object_from_callee(param_object.clone(), &param_value)
.unwrap();
let value = state
.load_value(
&Expression::Var(Variable::mock("RSP", 8)).plus_const(-16),
ByteSize::new(8),
&global_memory,
)
.unwrap();
assert_eq!(value.get_absolute_value().unwrap(), &bv(2).into());
assert!(value.contains_top());
}
use super::super::ValueDomain;
use super::*;
use crate::analysis::pointer_inference::object::*;
use crate::{bitvec, def, expr, variable};
mod specialized_expressions;
fn bv(value: i64) -> ValueDomain {
ValueDomain::from(bitvec!(format!("{}:8", value)))
}
fn new_id(time: &str, register: &str) -> AbstractIdentifier {
AbstractIdentifier::new(
Tid::new(time),
AbstractLocation::Register(variable!(format!("{}:8", register))),
)
}
fn expr_bi_op(lhs: Expression, op: BinOpType, rhs: Expression) -> Expression {
Expression::BinOp {
lhs: Box::new(lhs),
op: op,
rhs: Box::new(rhs),
}
}
#[test]
fn state() {
let global_memory = RuntimeMemoryImage::mock();
let mut state = State::new(&variable!("RSP:8"), Tid::new("time0"), BTreeSet::new());
let stack_id = new_id("time0", "RSP");
let stack_addr = Data::from_target(stack_id.clone(), bv(8));
state
.store_value(&stack_addr, &bv(42).into(), &global_memory)
.unwrap();
state
.register
.insert(variable!("RSP:8"), stack_addr.clone());
assert_eq!(
state
.load_value(&expr!("RSP:8"), ByteSize::new(8), &global_memory)
.unwrap(),
bv(42).into()
);
let mut other_state = State::new(&variable!("RSP:8"), Tid::new("time0"), BTreeSet::new());
state.register.insert(variable!("RAX:8"), bv(42).into());
other_state
.register
.insert(variable!("RSP:8"), stack_addr.clone());
other_state
.register
.insert(variable!("RAX:8"), bv(42).into());
other_state
.register
.insert(variable!("RBX:8"), bv(35).into());
let merged_state = state.merge(&other_state);
assert_eq!(merged_state.register[&variable!("RAX:8")], bv(42).into());
assert_eq!(
merged_state
.get_register(&variable!("RBX:8"))
.get_absolute_value()
.unwrap(),
&bv(35).into()
);
assert!(merged_state
.get_register(&variable!("RBX:8"))
.contains_top());
assert!(merged_state
.load_value(&expr!("RSP:8"), ByteSize::new(8), &global_memory)
.unwrap()
.contains_top());
state.memory.add_abstract_object(
new_id("heap_time", "heap_obj"),
ByteSize::new(8),
Some(ObjectType::Heap),
);
assert_eq!(state.memory.get_num_objects(), 3);
state.remove_unreferenced_objects();
assert_eq!(state.memory.get_num_objects(), 2);
}
#[test]
fn handle_store() {
let global_memory = RuntimeMemoryImage::mock();
let mut state = State::new(&variable!("RSP:8"), Tid::new("time0"), BTreeSet::new());
let stack_id = new_id("time0", "RSP");
assert_eq!(
state.eval(&expr!("RSP:8")),
Data::from_target(stack_id.clone(), bv(0))
);
state.handle_register_assign(&variable!("RSP:8"), &expr!("RSP:8 - 32:8"));
assert_eq!(
state.eval(&expr!("RSP:8")),
Data::from_target(stack_id.clone(), bv(-32))
);
state.handle_register_assign(&variable!("RSP:8"), &expr!("RSP:8 + -8:8"));
assert_eq!(
state.eval(&expr!("RSP:8")),
Data::from_target(stack_id.clone(), bv(-40))
);
state
.handle_store(&expr!("RSP:8 + 8:8"), &expr!("1:8"), &global_memory)
.unwrap();
state
.handle_store(&expr!("RSP:8 - 8:8"), &expr!("2:8"), &global_memory)
.unwrap();
state
.handle_store(&expr!("RSP:8 + -16:8"), &expr!("3:8"), &global_memory)
.unwrap();
state.handle_register_assign(&variable!("RSP:8"), &expr!("RSP:8 - 4:8"));
assert_eq!(
state
.load_value(&expr!("RSP:8 + 12:8"), ByteSize::new(8), &global_memory)
.unwrap(),
bv(1).into()
);
assert_eq!(
state
.load_value(&expr!("RSP:8 - 4:8"), ByteSize::new(8), &global_memory)
.unwrap(),
bv(2).into()
);
assert_eq!(
state
.load_value(&expr!("RSP:8 + -12:8"), ByteSize::new(8), &global_memory)
.unwrap(),
bv(3).into()
);
}
#[test]
fn clear_parameters_on_the_stack_on_extern_calls() {
let global_memory = RuntimeMemoryImage::mock();
let mut state = State::new(&variable!("RSP:8"), Tid::new("time0"), BTreeSet::new());
state.register.insert(
variable!("RSP:8"),
Data::from_target(new_id("time0", "RSP"), bv(-20)),
);
// write something onto the stack
state
.handle_store(&expr!("RSP:8 + 8:8"), &expr!("42:8"), &global_memory)
.unwrap();
// create an extern symbol which uses the value on the stack as a parameter
let stack_param = Arg::Stack {
address: expr!("RSP:8 + 8:8"),
size: ByteSize::new(8),
data_type: None,
};
let extern_symbol = ExternSymbol {
tid: Tid::new("symbol"),
addresses: vec![],
name: "my_extern_symbol".into(),
calling_convention: None,
parameters: vec![stack_param],
return_values: Vec::new(),
no_return: false,
has_var_args: false,
};
// check the value before
let pointer = Data::from_target(new_id("time0", "RSP"), bv(-12));
assert_eq!(
state.memory.get_value(&pointer, ByteSize::new(8)),
bv(42).into()
);
// clear stack parameter
state
.clear_stack_parameter(&extern_symbol, &global_memory)
.unwrap();
// check the value after
assert_eq!(
state.memory.get_value(&pointer, ByteSize::new(8)),
Data::new_top(ByteSize::new(8))
);
}
#[test]
fn reachable_ids_under_and_overapproximation() {
let global_memory = RuntimeMemoryImage::mock();
let mut state = State::new(&variable!("RSP:8"), Tid::new("func_tid"), BTreeSet::new());
let stack_id = new_id("func_tid", "RSP");
let heap_id = new_id("heap_obj", "RAX");
let stack_address: Data = Data::from_target(stack_id.clone(), bitvec!("-8:8").into());
let heap_address: Data = Data::from_target(heap_id.clone(), bitvec!("0:8").into());
// Add the heap object to the state, so that it can be recursively searched.
state
.memory
.add_abstract_object(heap_id.clone(), ByteSize::new(8), Some(ObjectType::Heap));
state
.store_value(&stack_address, &heap_address, &global_memory)
.unwrap();
let reachable_ids: BTreeSet<AbstractIdentifier> = vec![stack_id.clone()].into_iter().collect();
assert_eq!(
state.add_directly_reachable_ids_to_id_set(reachable_ids.clone()),
vec![stack_id.clone(), heap_id.clone()]
.into_iter()
.collect()
);
assert_eq!(
state.add_recursively_referenced_ids_to_id_set(reachable_ids.clone()),
vec![stack_id.clone(), heap_id.clone()]
.into_iter()
.collect()
);
let _ = state.store_value(
&Data::from_target(stack_id.clone(), ValueDomain::new_top(ByteSize::new(8))),
&bitvec!("42:8").into(),
&global_memory,
);
assert_eq!(
state.add_directly_reachable_ids_to_id_set(reachable_ids.clone()),
vec![stack_id.clone(), heap_id.clone()]
.into_iter()
.collect()
);
assert_eq!(
state.add_recursively_referenced_ids_to_id_set(reachable_ids.clone()),
vec![stack_id.clone(), heap_id.clone()]
.into_iter()
.collect()
);
}
#[test]
fn global_mem_access() {
let global_memory = RuntimeMemoryImage::mock();
let mut state = State::new(
&variable!("RSP:8"),
Tid::new("func_tid"),
BTreeSet::from([0x2000]),
);
// global read-only address
let address_expr = expr!("0x1000:8");
assert_eq!(
state
.load_value(&address_expr, ByteSize::new(4), &global_memory)
.unwrap(),
bitvec!("0xb3b2b1b0:4").into() // note that we read in little-endian byte order
);
assert!(state
.write_to_address(
&address_expr,
&DataDomain::new_top(ByteSize::new(4)),
&global_memory
)
.is_err());
// global writeable address
let address_expr = expr!("0x2000:8");
assert_eq!(
state
.load_value(&address_expr, ByteSize::new(4), &global_memory)
.unwrap(),
DataDomain::new_top(ByteSize::new(4))
);
assert!(state
.write_to_address(&address_expr, &bitvec!("21:4").into(), &global_memory)
.is_ok());
assert_eq!(
state
.load_value(&address_expr, ByteSize::new(4), &global_memory)
.unwrap(),
bitvec!("21:4").into()
);
// invalid global address
let address_expr = expr!("0x3456:8");
assert!(state
.load_value(&address_expr, ByteSize::new(4), &global_memory)
.is_err());
assert!(state
.write_to_address(
&address_expr,
&DataDomain::new_top(ByteSize::new(4)),
&global_memory
)
.is_err());
}
/// Test that value specialization does not introduce unintended widening hints.
/// This is a regression test for cases where pointer comparisons introduced two-sided bounds
/// (resulting in two-sided widenings) instead of one-sided bounds.
#[test]
fn test_widening_hints_after_pointer_specialization() {
let mut state = State::new(&variable!("RSP:8"), Tid::new("func_tid"), BTreeSet::new());
state.set_register(
&variable!("RAX:8"),
Data::from_target(new_id("func_tid", "RSP"), bitvec!("10:8").into()),
);
state.set_register(
&variable!("RBX:8"),
Data::from_target(new_id("func_tid", "RSP"), bitvec!("10:8").into()),
);
let neq_expression = expr_bi_op(expr!("5:8"), BinOpType::IntNotEqual, expr!("RAX:8 - RBX:8"));
state
.specialize_by_expression_result(&neq_expression, bitvec!("1:1").into())
.unwrap();
state
.specialize_by_expression_result(&neq_expression, bitvec!("1:1").into())
.unwrap();
let offset_with_upper_bound: IntervalDomain = bitvec!("10:8").into();
let offset_with_upper_bound = offset_with_upper_bound
.add_signed_less_equal_bound(&bitvec!("14:8"))
.unwrap();
let expected_val = Data::from_target(new_id("func_tid", "RSP"), offset_with_upper_bound);
assert_eq!(state.get_register(&variable!("RAX:8")), expected_val);
let offset_with_lower_bound: IntervalDomain = bitvec!("10:8").into();
let offset_with_lower_bound = offset_with_lower_bound
.add_signed_greater_equal_bound(&Bitvector::from_i64(6))
.unwrap();
let expected_val = Data::from_target(new_id("func_tid", "RSP"), offset_with_lower_bound);
assert_eq!(state.get_register(&Variable::mock("RBX", 8)), expected_val);
}
#[test]
fn test_check_def_for_null_dereferences() {
let mut state = State::new(&variable!("RSP:8"), Tid::new("func_tid"), BTreeSet::new());
let var_rax = variable!("RAX:8");
let def = def![format!("load_def: RBX:8 := Load from {}", var_rax)];
state.set_register(&var_rax, bitvec!("0:8").into());
assert!(state.check_def_for_null_dereferences(&def).is_err());
state.set_register(&var_rax, bitvec!("12345:8").into());
assert_eq!(
state.check_def_for_null_dereferences(&def).ok(),
Some(false)
);
state.set_register(&var_rax, IntervalDomain::mock(-2000, 5).into());
assert_eq!(state.check_def_for_null_dereferences(&def).ok(), Some(true));
assert_eq!(
state.get_register(&var_rax),
IntervalDomain::mock(-2000, -1024).into()
);
let mut address = state.get_register(&variable!("RSP:8"));
address.set_contains_top_flag();
address.set_absolute_value(Some(IntervalDomain::mock(0, 0xffff)));
state.set_register(&var_rax, address);
assert_eq!(state.check_def_for_null_dereferences(&def).ok(), Some(true));
}
#[test]
fn from_fn_sig() {
let fn_sig = FunctionSignature::mock_x64();
let state = State::from_fn_sig(&fn_sig, &variable!("RSP:8"), Tid::new("func"));
assert_eq!(state.memory.get_num_objects(), 3);
assert_eq!(
*state.memory.get_object(&new_id("func", "RSI")).unwrap(),
AbstractObject::new(None, ByteSize::new(8))
);
assert_eq!(
state.get_register(&variable!("RSP:8")),
Data::from_target(new_id("func", "RSP"), bv(0).into())
);
assert_eq!(
state.get_register(&Variable::mock("RDI", 8)),
Data::from_target(new_id("func", "RDI"), bv(0).into())
);
assert_eq!(
state.get_register(&Variable::mock("RSI", 8)),
Data::from_target(new_id("func", "RSI"), bv(0).into())
);
}
#[test]
fn add_param_object_from_callee() {
let global_memory = RuntimeMemoryImage::empty(true);
let mut generic_state = State::new(&variable!("RSP:8"), Tid::new("func"), BTreeSet::new());
generic_state
.write_to_address(&expr!("RSP:8 + -8:8"), &bv(1).into(), &global_memory)
.unwrap();
let mut param_object = AbstractObject::new(None, ByteSize::new(8));
param_object.set_value(bv(2).into(), &bv(0).into()).unwrap();
let mut param_value = Data::from_target(new_id("func", "RSP"), bv(-16).into());
// Testcase 1: param object is unique
let mut state = generic_state.clone();
state
.add_param_object_from_callee(param_object.clone(), &param_value)
.unwrap();
let value = state
.load_value(&expr!("RSP:8 + -8:8"), ByteSize::new(8), &global_memory)
.unwrap();
assert_eq!(value.get_absolute_value().unwrap(), &bv(1).into());
assert!(value.contains_top());
let value = state
.load_value(&expr!("RSP:8 + -16:8"), ByteSize::new(8), &global_memory)
.unwrap();
assert_eq!(value.get_absolute_value().unwrap(), &bv(2).into());
assert!(!value.contains_top());
// Testcase 2: param object is not unique
let mut state = generic_state.clone();
param_value.set_contains_top_flag();
state
.add_param_object_from_callee(param_object.clone(), &param_value)
.unwrap();
let value = state
.load_value(&expr!("RSP:8 + -16:8"), ByteSize::new(8), &global_memory)
.unwrap();
assert_eq!(value.get_absolute_value().unwrap(), &bv(2).into());
assert!(value.contains_top());
}
use super::*;
/// Test expression specialization except for binary operations.
#[test]
fn specialize_by_expression_results() {
let mut base_state = State::new(&variable!("RSP:8"), Tid::new("func_tid"), BTreeSet::new());
base_state.set_register(
&variable!("RAX:8"),
IntervalDomain::new(bitvec!("5:8"), bitvec!("10:8")).into(),
);
// Expr = Var(RAX)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(&expr!("RAX:8"), bitvec!("7:8").into());
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
bitvec!("7:8").into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(&expr!("RAX:8"), bitvec!("-20:8").into());
assert!(x.is_err());
let mut state = base_state.clone();
let abstract_id = AbstractIdentifier::new(
Tid::new("heap_obj"),
AbstractLocation::from_var(&variable!("RAX:8")).unwrap(),
);
state.set_register(
&variable!("RAX:8"),
Data::from_target(abstract_id.clone(), IntervalDomain::mock(0, 50)),
);
let x = state.specialize_by_expression_result(
&expr!("RAX:8"),
Data::from_target(abstract_id.clone(), IntervalDomain::mock(20, 70)),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
Data::from_target(abstract_id, IntervalDomain::mock(20, 50))
);
// Expr = Const
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(&expr!("-20:8"), bitvec!("-20:8").into());
assert!(x.is_ok());
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(&expr!("5:8"), bitvec!("-20:8").into());
assert!(x.is_err());
// Expr = -Var(RAX)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr!("RAX:8").un_op(UnOpType::Int2Comp),
bitvec!("-7:8").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
bitvec!("7:8").into()
);
// Expr = IntSExt(Var(EAX))
let mut state = State::new(&variable!("RSP:8"), Tid::new("func_tid"), BTreeSet::new());
let eax_register = variable!("EAX:4");
state.set_register(
&eax_register,
IntervalDomain::new(bitvec!("-7:4"), bitvec!("-5:4")).into(),
);
let x = state.specialize_by_expression_result(
&expr!("EAX:4").cast(CastOpType::IntSExt),
bitvec!("-7:8").into(),
);
assert!(x.is_ok());
assert_eq!(state.get_register(&eax_register), bitvec!("-7:4").into());
// Expr = Subpiece(Var(RAX))
let mut state = State::new(&variable!("RSP:8"), Tid::new("func_tid"), BTreeSet::new());
let rax_register = variable!("RAX:8");
let x = state.specialize_by_expression_result(
&Expression::Var(rax_register.clone()).subpiece(ByteSize::new(0), ByteSize::new(1)),
bitvec!("5:1").into(),
);
assert!(x.is_ok());
assert!(state.get_register(&rax_register).is_top());
state.set_register(&rax_register, IntervalDomain::mock(3, 10).into());
let x = state.specialize_by_expression_result(
&Expression::Var(rax_register.clone()).subpiece(ByteSize::new(0), ByteSize::new(1)),
bitvec!("5:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&rax_register),
IntervalDomain::mock(5, 5).into()
);
}
/// Test expression specialization for binary operations
/// except equality and inequality operations
#[test]
fn specialize_by_binop() {
let base_state = State::new(&variable!("RSP:8"), Tid::new("func_tid"), BTreeSet::new());
// Expr = RAX + Const
let mut state = base_state.clone();
let x = state
.specialize_by_expression_result(&expr!("RAX:8 + 20:8"), IntervalDomain::mock(5, 7).into());
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock(-15, -13).into()
);
// Expr = RAX - Const
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(&expr!("RAX:8 - 20:8"), bitvec!("5:8").into());
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
bitvec!("25:8").into()
);
// Expr = RAX xor Const
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("RAX:8"), BinOpType::IntXOr, expr!("3:8")),
bitvec!("-1:8").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
bitvec!("-4:8").into()
);
// Expr = (RAX or RBX == 0)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("RAX:8"), BinOpType::IntOr, expr!("RBX:8")),
bitvec!("0:8").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
bitvec!("0:8").into()
);
assert_eq!(
state.get_register(&variable!("RBX:8")),
bitvec!("0:8").into()
);
// Expr = (RAX or 0 == Const)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("RAX:8"), BinOpType::IntOr, expr!("0:8")),
bitvec!("42:8").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
bitvec!("42:8").into()
);
// Expr = (FLAG1 bool_and FLAG2 == 1)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("FLAG1:1"), BinOpType::BoolAnd, expr!("FLAG2:1")),
bitvec!("1:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&Variable::mock("FLAG1", 1u64)),
bitvec!("1:1").into()
);
assert_eq!(
state.get_register(&Variable::mock("FLAG2", 1u64)),
bitvec!("1:1").into()
);
// Expr = (FLAG bool_and 1 = Const)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("1:1"), BinOpType::BoolAnd, expr!("FLAG:1")),
bitvec!("0:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("FLAG:1")),
bitvec!("0:1").into()
);
}
/// Test expression specialization for comparison operations `==` and `!=`.
#[test]
fn specialize_by_equality_comparison() {
let mut base_state = State::new(&variable!("RSP:8"), Tid::new("func_tid"), BTreeSet::new());
base_state.set_register(&variable!("RAX:8"), IntervalDomain::mock(0, 50).into());
// Expr = RAX == Const
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("23:8"), BinOpType::IntEqual, expr!("RAX:8")),
bitvec!("1:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
bitvec!("23:8").into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("23:8"), BinOpType::IntNotEqual, expr!("RAX:8")),
bitvec!("0:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
bitvec!("23:8").into()
);
// Expr = RAX != Const
let mut state = base_state.clone();
state.set_register(&variable!("RAX:8"), bitvec!("23:8").into());
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("23:8"), BinOpType::IntNotEqual, expr!("RAX:8")),
bitvec!("1:1").into(),
);
assert!(x.is_err());
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("100:8"), BinOpType::IntEqual, expr!("RAX:8")),
bitvec!("0:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock_with_bounds(None, 0, 50, Some(99)).into()
);
}
/// Test expression specialization for signed comparison operations `<` and `<=`.
#[test]
fn specialize_by_signed_comparison_op() {
let mut base_state = State::new(&variable!("RSP:8"), Tid::new("func_tid"), BTreeSet::new());
let interval = IntervalDomain::mock(5, 10);
base_state.set_register(&variable!("RAX:8"), interval.into());
// Expr = RAX < Const (signed)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("7:8"), BinOpType::IntSLess, expr!("RAX:8")),
bitvec!("1:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock(8, 10).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("15:8"), BinOpType::IntSLess, expr!("RAX:8")),
bitvec!("0:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock_with_bounds(None, 5, 10, Some(15)).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(
expr!("RAX:8"),
BinOpType::IntSLess,
Expression::Const(Bitvector::signed_min_value(ByteSize::new(8).into())),
),
bitvec!("1:1").into(),
);
assert!(x.is_err());
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("RAX:8"), BinOpType::IntSLess, expr!("7:8")),
bitvec!("0:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock(7, 10).into()
);
// Expr = RAX <= Const (signed)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("7:8"), BinOpType::IntSLessEqual, expr!("RAX:8")),
bitvec!("1:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock(7, 10).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("15:8"), BinOpType::IntSLessEqual, expr!("RAX:8")),
bitvec!("0:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock_with_bounds(None, 5, 10, Some(14)).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(
expr!("RAX:8"),
BinOpType::IntSLessEqual,
Expression::Const(Bitvector::signed_min_value(ByteSize::new(8).into())),
),
bitvec!("1:1").into(),
);
assert!(x.is_err());
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("RAX:8"), BinOpType::IntSLessEqual, expr!("7:8")),
bitvec!("0:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock(8, 10).into()
);
}
/// Test expression specialization for unsigned comparison operations `<` and `<=`.
#[test]
fn specialize_by_unsigned_comparison_op() {
let mut base_state = State::new(&variable!("RSP:8"), Tid::new("func_tid"), BTreeSet::new());
let interval = IntervalDomain::mock(-5, 10);
base_state.set_register(&variable!("RAX:8"), interval.into());
// Expr = RAX < Const (unsigned)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("7:8"), BinOpType::IntLess, expr!("RAX:8")),
bitvec!("1:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock(-5, 10).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("15:8"), BinOpType::IntLess, expr!("RAX:8")),
bitvec!("0:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock_with_bounds(None, 0, 10, Some(15)).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("RAX:8"), BinOpType::IntLess, expr!("0:8")),
bitvec!("1:1").into(),
);
assert!(x.is_err());
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("RAX:8"), BinOpType::IntLess, expr!("-20:8")),
bitvec!("0:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock_with_bounds(Some(-20), -5, -1, None).into()
);
// Expr = RAX <= Const (unsigned)
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("7:8"), BinOpType::IntLessEqual, expr!("RAX:8")),
bitvec!("1:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock(-5, 10).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("15:8"), BinOpType::IntLessEqual, expr!("RAX:8")),
bitvec!("0:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock_with_bounds(None, 0, 10, Some(14)).into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("RAX:8"), BinOpType::IntLessEqual, expr!("0:8")),
bitvec!("1:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
bitvec!("0:8").into()
);
let mut state = base_state.clone();
let x = state.specialize_by_expression_result(
&expr_bi_op(expr!("RAX:8"), BinOpType::IntLessEqual, expr!("-20:8")),
bitvec!("0:1").into(),
);
assert!(x.is_ok());
assert_eq!(
state.get_register(&variable!("RAX:8")),
IntervalDomain::mock_with_bounds(Some(-19), -5, -1, None).into()
);
}
#[test]
fn specialize_pointer_comparison() {
let mut state = State::new(&variable!("RSP:8"), Tid::new("func_tid"), BTreeSet::new());
let interval = IntervalDomain::mock(-5, 10);
state.set_register(
&variable!("RAX:8"),
Data::from_target(new_id("func_tid", "RSP"), interval.into()),
);
let interval = IntervalDomain::mock(20, 20);
state.set_register(
&variable!("RBX:8"),
Data::from_target(new_id("func_tid", "RSP"), interval.into()),
);
let expression = expr_bi_op(expr!("RAX:8"), BinOpType::IntEqual, expr!("RBX:8"));
assert!(state
.clone()
.specialize_by_expression_result(&expression, bitvec!("1:1").into())
.is_err());
let specialized_interval = IntervalDomain::mock_with_bounds(None, -5, 10, Some(19));
let specialized_pointer =
Data::from_target(new_id("func_tid", "RSP"), specialized_interval.into());
assert!(state
.specialize_by_expression_result(&expression, bitvec!("0:1").into())
.is_ok());
assert_eq!(state.get_register(&variable!("RAX:8")), specialized_pointer);
}
use super::*; use super::*;
use crate::{def, defs, expr, variable};
use std::borrow::BorrowMut; use std::borrow::BorrowMut;
/// Creates a x64 or ARM32 Project for easy addidion of assignments. /// Creates a x64 or ARM32 Project for easy addidion of assignments.
...@@ -30,9 +31,7 @@ fn unexpected_alignment() { ...@@ -30,9 +31,7 @@ fn unexpected_alignment() {
lhs: Box::new(Expression::Var( lhs: Box::new(Expression::Var(
Project::mock_x64().stack_pointer_register.clone(), Project::mock_x64().stack_pointer_register.clone(),
)), )),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u32( rhs: Box::new(expr!(format!("{}:4", 0xFFFFFFFF_u32 << i))),
0xFFFFFFFF << i,
))),
}, },
)]; )];
let mut proj_x64 = setup(def_x64, true); let mut proj_x64 = setup(def_x64, true);
...@@ -55,9 +54,7 @@ fn unexpected_alignment() { ...@@ -55,9 +54,7 @@ fn unexpected_alignment() {
lhs: Box::new(Expression::Var( lhs: Box::new(Expression::Var(
Project::mock_arm32().stack_pointer_register.clone(), Project::mock_arm32().stack_pointer_register.clone(),
)), )),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u32( rhs: Box::new(expr!(format!("{}:4", 0xFFFFFFFF_u32 << i))),
0xFFFFFFFF << i,
))),
}, },
)]; )];
let mut proj_arm = setup(def_arm, false); let mut proj_arm = setup(def_arm, false);
...@@ -77,26 +74,15 @@ fn unexpected_alignment() { ...@@ -77,26 +74,15 @@ fn unexpected_alignment() {
/// Tests the substituted offset meets the alignment for x64. Tests only the logical AND case. /// Tests the substituted offset meets the alignment for x64. Tests only the logical AND case.
fn compute_correct_offset_x64() { fn compute_correct_offset_x64() {
for i in 0..=33 { for i in 0..=33 {
let sub_from_sp = Def::assign( let sub_from_sp = def![format!("tid_alter_sp: RSP:8 = RSP:8 - {}:8", i)];
"tid_alter_sp",
Project::mock_x64().stack_pointer_register.clone(),
Expression::minus(
Expression::Var(Project::mock_x64().stack_pointer_register.clone()),
Expression::const_from_apint(ApInt::from_u64(i)),
),
);
let byte_alignment_as_and = Def::assign( let byte_alignment_as_and = Def::assign(
"tid_to_be_substituted", "tid_to_be_substituted",
Project::mock_x64().stack_pointer_register.clone(), variable!("RSP:8"),
Expression::BinOp { Expression::BinOp {
op: BinOpType::IntAnd, op: BinOpType::IntAnd,
lhs: Box::new(Expression::Var( lhs: Box::new(expr!("RSP:8")),
Project::mock_x64().stack_pointer_register.clone(), rhs: Box::new(expr!(format!("{}:8", 0xFFFFFFFF_FFFFFFFF_u64 << 4))),
)),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u64(
0xFFFFFFFF_FFFFFFFF << 4, // 16 Byte alignment
))),
}, },
); );
let mut proj = setup( let mut proj = setup(
...@@ -110,20 +96,15 @@ fn compute_correct_offset_x64() { ...@@ -110,20 +96,15 @@ fn compute_correct_offset_x64() {
if def.tid == byte_alignment_as_and.tid.clone() { if def.tid == byte_alignment_as_and.tid.clone() {
let expected_offset: u64 = match i % 16 { let expected_offset: u64 = match i % 16 {
0 => 0, 0 => 0,
_ => (16 - (i % 16)).into(), _ => 16 - (i % 16),
}; };
// translated alignment as substraction // translated alignment as substraction
let expected_def = Def::Assign { let expected_def = def![format!(
var: proj.stack_pointer_register.clone(), "{} = RSP:8 - {}:8",
value: Expression::BinOp { proj.stack_pointer_register, expected_offset
op: BinOpType::IntSub, )];
lhs: Box::new(Expression::Var(proj.stack_pointer_register.clone())),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u64( assert_eq!(expected_def.term, def.term);
expected_offset,
))),
},
};
assert_eq!(expected_def, def.term);
assert!(log.is_none()); assert!(log.is_none());
} }
} }
...@@ -136,25 +117,15 @@ fn compute_correct_offset_x64() { ...@@ -136,25 +117,15 @@ fn compute_correct_offset_x64() {
/// Tests the substituted offset meets the alignment for arm32. Tests only the logical AND case. /// Tests the substituted offset meets the alignment for arm32. Tests only the logical AND case.
fn compute_correct_offset_arm32() { fn compute_correct_offset_arm32() {
for i in 0..=33 { for i in 0..=33 {
let sub_from_sp = Def::assign( let sub_from_sp = def!(format!("tid_alter_sp: sp:4 = sp:4 - {}:4", i));
"tid_alter_sp",
Project::mock_arm32().stack_pointer_register.clone(),
Expression::minus(
Expression::Var(Project::mock_arm32().stack_pointer_register.clone()),
Expression::const_from_apint(ApInt::from_u32(i)),
),
);
let byte_alignment_as_and = Def::assign( let byte_alignment_as_and = Def::assign(
"tid_to_be_substituted", "tid_to_be_substituted",
Project::mock_arm32().stack_pointer_register.clone(), variable!("sp:4"),
Expression::BinOp { Expression::BinOp {
op: BinOpType::IntAnd, op: BinOpType::IntAnd,
lhs: Box::new(Expression::Var( lhs: Box::new(expr!("sp:4")),
Project::mock_arm32().stack_pointer_register.clone(), rhs: Box::new(expr!(format!("{}:4", 0xFFFFFFFF_u32 << 2))), // 4 Byte alignment
)),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u32(
0xFFFFFFFF << 2, // 4 Byte alignment
))),
}, },
); );
let mut proj = setup( let mut proj = setup(
...@@ -171,17 +142,8 @@ fn compute_correct_offset_arm32() { ...@@ -171,17 +142,8 @@ fn compute_correct_offset_arm32() {
_ => 4 - (i % 4), _ => 4 - (i % 4),
}; };
// translated alignment as substraction // translated alignment as substraction
let expected_def = Def::Assign { let expected_def = def!(format!("sp:4 = sp:4 - {}:4", expected_offset));
var: proj.stack_pointer_register.clone(), assert_eq!(expected_def.term, def.term);
value: Expression::BinOp {
op: BinOpType::IntSub,
lhs: Box::new(Expression::Var(proj.stack_pointer_register.clone())),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u32(
expected_offset,
))),
},
};
assert_eq!(expected_def, def.term);
assert!(log.is_none()); assert!(log.is_none());
} }
} }
...@@ -202,24 +164,20 @@ fn check_bin_operations() { ...@@ -202,24 +164,20 @@ fn check_bin_operations() {
] { ] {
let unsupported_def_x64 = Def::assign( let unsupported_def_x64 = Def::assign(
"tid_to_be_substituted", "tid_to_be_substituted",
Project::mock_x64().stack_pointer_register.clone(), variable!("RSP:8"),
Expression::BinOp { Expression::BinOp {
op: biopty, op: biopty,
lhs: Box::new(Expression::Var( lhs: Box::new(expr!("RSP:8")),
Project::mock_x64().stack_pointer_register.clone(), rhs: Box::new(expr!("0:4")),
)),
rhs: Box::new(Expression::const_from_i32(0)),
}, },
); );
let unsupported_def_arm32 = Def::assign( let unsupported_def_arm32 = Def::assign(
"tid_to_be_substituted", "tid_to_be_substituted",
Project::mock_arm32().stack_pointer_register.clone(), variable!("sp:4"),
Expression::BinOp { Expression::BinOp {
op: biopty, op: biopty,
lhs: Box::new(Expression::Var( lhs: Box::new(expr!("sp:4")),
Project::mock_arm32().stack_pointer_register.clone(), rhs: Box::new(expr!("0:4")),
)),
rhs: Box::new(Expression::const_from_i32(0)),
}, },
); );
let mut proj_x64 = setup(vec![unsupported_def_x64.clone()], true); let mut proj_x64 = setup(vec![unsupported_def_x64.clone()], true);
...@@ -267,9 +225,9 @@ fn substitution_ends_if_unsubstituable() { ...@@ -267,9 +225,9 @@ fn substitution_ends_if_unsubstituable() {
lhs: Box::new(Expression::Var( lhs: Box::new(Expression::Var(
Project::mock_x64().stack_pointer_register.clone(), Project::mock_x64().stack_pointer_register.clone(),
)), )),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u64( rhs: Box::new(
0xFFFFFFFF_FFFFFFFF << 4, // 16 Byte alignment expr!(format!("{}:8", 0xFFFFFFFF_FFFFFFFF_u64 << 4)), // 16 Byte alignment
))), ),
}, },
); );
...@@ -278,10 +236,8 @@ fn substitution_ends_if_unsubstituable() { ...@@ -278,10 +236,8 @@ fn substitution_ends_if_unsubstituable() {
Project::mock_x64().stack_pointer_register.clone(), Project::mock_x64().stack_pointer_register.clone(),
Expression::BinOp { Expression::BinOp {
op: BinOpType::Piece, op: BinOpType::Piece,
lhs: Box::new(Expression::Var( lhs: Box::new(expr!("RSP:8")),
Project::mock_x64().stack_pointer_register.clone(), rhs: Box::new(expr!("0:8")),
)),
rhs: Box::new(Expression::const_from_i64(0)),
}, },
); );
let mut proj = setup( let mut proj = setup(
...@@ -302,17 +258,9 @@ fn substitution_ends_if_unsubstituable() { ...@@ -302,17 +258,9 @@ fn substitution_ends_if_unsubstituable() {
.text .text
.contains("Unsubstitutable Operation on SP")); .contains("Unsubstitutable Operation on SP"));
let exp_16_byte_alignment_substituted = Def::assign( let exp_16_byte_alignment_substituted = defs!["tid_to_be_substituted: RSP:8 = RSP:8 - 0x0:8"]
"tid_to_be_substituted", .pop()
Project::mock_x64().stack_pointer_register.clone(), .unwrap();
Expression::BinOp {
op: BinOpType::IntSub,
lhs: Box::new(Expression::Var(
Project::mock_x64().stack_pointer_register.clone(),
)),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u64(0))),
},
);
for sub in proj.program.term.subs.into_values() { for sub in proj.program.term.subs.into_values() {
for blk in sub.term.blocks { for blk in sub.term.blocks {
...@@ -333,12 +281,10 @@ fn substitution_ends_if_unsubstituable() { ...@@ -333,12 +281,10 @@ fn substitution_ends_if_unsubstituable() {
fn supports_commutative_and() { fn supports_commutative_and() {
let var_and_bitmask = Def::assign( let var_and_bitmask = Def::assign(
"tid_to_be_substituted", "tid_to_be_substituted",
Project::mock_x64().stack_pointer_register.clone(), variable!("RSP:8"),
Expression::BinOp { Expression::BinOp {
op: BinOpType::IntAnd, op: BinOpType::IntAnd,
lhs: Box::new(Expression::Var( lhs: Box::new(expr!("RSP:8")),
Project::mock_x64().stack_pointer_register.clone(),
)),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u64( rhs: Box::new(Expression::const_from_apint(ApInt::from_u64(
0xFFFFFFFF_FFFFFFFF << 4, // 16 Byte alignment 0xFFFFFFFF_FFFFFFFF << 4, // 16 Byte alignment
))), ))),
...@@ -349,12 +295,8 @@ fn supports_commutative_and() { ...@@ -349,12 +295,8 @@ fn supports_commutative_and() {
Project::mock_x64().stack_pointer_register.clone(), Project::mock_x64().stack_pointer_register.clone(),
Expression::BinOp { Expression::BinOp {
op: BinOpType::IntAnd, op: BinOpType::IntAnd,
lhs: Box::new(Expression::const_from_apint(ApInt::from_u64( lhs: Box::new(expr!(format!("{}:8", 0xFFFFFFFF_FFFFFFFF_u64 << 4))),
0xFFFFFFFF_FFFFFFFF << 4, // 16 Byte alignment rhs: Box::new(expr!("RSP:8")),
))),
rhs: Box::new(Expression::Var(
Project::mock_x64().stack_pointer_register.clone(),
)),
}, },
); );
...@@ -362,17 +304,9 @@ fn supports_commutative_and() { ...@@ -362,17 +304,9 @@ fn supports_commutative_and() {
let log = substitute_and_on_stackpointer(proj.borrow_mut()); let log = substitute_and_on_stackpointer(proj.borrow_mut());
assert!(log.is_none()); assert!(log.is_none());
let expected_def = Def::assign( let expected_def = defs!["tid_to_be_substituted: RSP:8 = RSP:8 - 0x0:8"]
"tid_to_be_substituted", .pop()
Project::mock_x64().stack_pointer_register.clone(), .unwrap();
Expression::BinOp {
op: BinOpType::IntSub,
lhs: Box::new(Expression::Var(
Project::mock_x64().stack_pointer_register.clone(),
)),
rhs: Box::new(Expression::const_from_apint(ApInt::from_u64(0))),
},
);
for sub in proj.program.term.subs.into_values() { for sub in proj.program.term.subs.into_values() {
for blk in sub.term.blocks { for blk in sub.term.blocks {
...@@ -386,18 +320,11 @@ fn supports_commutative_and() { ...@@ -386,18 +320,11 @@ fn supports_commutative_and() {
/// Some functions have leading blocks without any defs. This might be due to `endbr`-like instructions. /// Some functions have leading blocks without any defs. This might be due to `endbr`-like instructions.
/// We skip those empty blocks and start substituting for rhe first non-empty block. /// We skip those empty blocks and start substituting for rhe first non-empty block.
fn skips_empty_blocks() { fn skips_empty_blocks() {
let sub_from_sp = Def::assign( let sub_from_sp = def!["tid_alter_sp: RSP:8 = RSP:8 - 1:8"];
"tid_alter_sp",
Project::mock_x64().stack_pointer_register.clone(),
Expression::minus(
Expression::Var(Project::mock_x64().stack_pointer_register.clone()),
Expression::const_from_apint(ApInt::from_u64(1)),
),
);
let byte_alignment_as_and = Def::assign( let byte_alignment_as_and = Def::assign(
"tid_to_be_substituted", "tid_to_be_substituted",
Project::mock_x64().stack_pointer_register.clone(), variable!("RSP:8"),
Expression::BinOp { Expression::BinOp {
op: BinOpType::IntAnd, op: BinOpType::IntAnd,
lhs: Box::new(Expression::Var( lhs: Box::new(Expression::Var(
...@@ -439,14 +366,7 @@ fn skips_empty_blocks() { ...@@ -439,14 +366,7 @@ fn skips_empty_blocks() {
.blocks .blocks
.push(blk); .push(blk);
let expected_def = Def::assign( let expected_def = def!["tid_to_be_substituted: RSP:8 = RSP:8 - 15:8"];
"tid_to_be_substituted",
Project::mock_x64().stack_pointer_register.clone(),
Expression::minus(
Expression::Var(Project::mock_x64().stack_pointer_register.clone()),
Expression::const_from_apint(ApInt::from_u64(15)),
),
);
substitute_and_on_stackpointer(&mut proj); substitute_and_on_stackpointer(&mut proj);
......
...@@ -7,12 +7,11 @@ use crate::{ ...@@ -7,12 +7,11 @@ use crate::{
analysis::pointer_inference::PointerInference as PointerInferenceComputation, analysis::pointer_inference::PointerInference as PointerInferenceComputation,
analysis::{ analysis::{
forward_interprocedural_fixpoint::Context, forward_interprocedural_fixpoint::Context,
string_abstraction::{ string_abstraction::{context::symbol_calls::tests::Setup, tests::*},
context::symbol_calls::tests::Setup,
tests::mock_project_with_intraprocedural_control_flow, tests::Setup as ProjectSetup,
},
}, },
intermediate_representation::{Bitvector, Blk, ByteSize, ExternSymbol, Jmp, Tid, Variable}, bitvec, def,
intermediate_representation::*,
variable,
}; };
#[test] #[test]
...@@ -28,29 +27,28 @@ fn test_update_def() { ...@@ -28,29 +27,28 @@ fn test_update_def() {
let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results); let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results);
setup.context.block_first_def_set = HashSet::new(); setup.context.block_first_def_set = HashSet::new();
let project_setup = ProjectSetup::new(); let assign_def = def!["assign_def: r1:4 = 0x7000:4"];
let assign_def = project_setup.string_input_constant("assign_def", "r1", 0x7000); let load_def = load_var_content_from_temp_var("load_def", "r5", "r2");
let load_def = project_setup.load_var_content_from_temp_var("load_def", "r5", "r2"); let store_def = store_var_content_at_temp_var("store_def", "r0", "r5");
let store_def = project_setup.store_var_content_at_temp_var("store_def", "r0", "r5");
let new_state = setup let new_state = setup
.context .context
.update_def(&setup.state_before_call, &assign_def) .update_def(&setup.state_before_call, &assign_def)
.unwrap(); .unwrap();
let absolute_target = DataDomain::from(Bitvector::from_i32(0x7000)); let absolute_target = DataDomain::from(bitvec!("0x7000:4"));
assert_eq!( assert_eq!(
absolute_target, absolute_target,
*new_state *new_state
.get_variable_to_pointer_map() .get_variable_to_pointer_map()
.get(&Variable::mock("r1", 4)) .get(&variable!("r1:4"))
.unwrap() .unwrap()
); );
let stack_id = AbstractIdentifier::new( let stack_id = AbstractIdentifier::new(
Tid::new("func"), Tid::new("func"),
AbstractLocation::from_var(&Variable::mock("sp", 4)).unwrap(), AbstractLocation::from_var(&variable!("sp:4")).unwrap(),
); );
let loaded_pointer = DataDomain::from_target(stack_id.clone(), IntervalDomain::mock_i32(4, 4)); let loaded_pointer = DataDomain::from_target(stack_id.clone(), IntervalDomain::mock_i32(4, 4));
...@@ -64,7 +62,7 @@ fn test_update_def() { ...@@ -64,7 +62,7 @@ fn test_update_def() {
); );
let r2_reg = Variable { let r2_reg = Variable {
name: String::from("r2"), name: "r2".into(),
size: ByteSize::new(4), size: ByteSize::new(4),
is_temp: true, is_temp: true,
}; };
...@@ -79,7 +77,7 @@ fn test_update_def() { ...@@ -79,7 +77,7 @@ fn test_update_def() {
setup setup
.state_before_call .state_before_call
.add_new_variable_to_pointer_entry(Variable::mock("r3", 4), loaded_pointer.clone()); .add_new_variable_to_pointer_entry(variable!("r3:4"), loaded_pointer.clone());
let new_state = setup let new_state = setup
.context .context
...@@ -90,14 +88,14 @@ fn test_update_def() { ...@@ -90,14 +88,14 @@ fn test_update_def() {
loaded_pointer, loaded_pointer,
*new_state *new_state
.get_variable_to_pointer_map() .get_variable_to_pointer_map()
.get(&Variable::mock("r5", 4)) .get(&variable!("r5:4"))
.unwrap() .unwrap()
); );
let store_target = DataDomain::from_target(stack_id, IntervalDomain::mock_i32(12, 12)); let store_target = DataDomain::from_target(stack_id, IntervalDomain::mock_i32(12, 12));
let r0_reg = Variable { let r0_reg = Variable {
name: String::from("r0"), name: "r0".into(),
size: ByteSize::new(4), size: ByteSize::new(4),
is_temp: true, is_temp: true,
}; };
...@@ -108,7 +106,7 @@ fn test_update_def() { ...@@ -108,7 +106,7 @@ fn test_update_def() {
setup setup
.pi_state_before_symbol_call .pi_state_before_symbol_call
.set_register(&Variable::mock("r5", 4), absolute_target.clone()); .set_register(&variable!("r5:4"), absolute_target.clone());
setup setup
.state_before_call .state_before_call
...@@ -165,9 +163,9 @@ fn test_update_return() { ...@@ -165,9 +163,9 @@ fn test_update_return() {
let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results); let mut setup: Setup<CharacterInclusionDomain> = Setup::new(&pi_results);
let pointer = DataDomain::from(Bitvector::from_i32(0x6000)); let pointer = DataDomain::from(bitvec!("0x6000:4"));
let callee_saved_reg = Variable::mock("r11", 4); let callee_saved_reg = variable!("r11:4");
let non_callee_saved_reg = Variable::mock("r0", 4); let non_callee_saved_reg = variable!("r0:4");
setup setup
.state_before_call .state_before_call
......
use crate::intermediate_representation::*; use crate::{def, defs, expr, intermediate_representation::*, variable};
pub struct Setup; // FIXME: Move this function to the intermediate_representation module
pub fn pointer_plus_offset_to_temp_var(
impl Setup { tid: &str,
pub fn new() -> Self { tmp_name: &str,
Setup pointer: &str,
} offset: i64,
) -> Term<Def> {
pub fn format_string_constant(&self, tid: &str, register: &str) -> Term<Def> { Def::assign(
Def::assign( tid,
tid, Variable {
Variable::mock(register, 4), name: String::from(tmp_name),
Expression::const_from_i32(0x6000), size: ByteSize::new(4),
) is_temp: true,
} },
expr!(format!("{}:4 + {}:4", pointer, offset)),
pub fn string_input_constant(&self, tid: &str, register: &str, address: i32) -> Term<Def> { )
Def::assign( }
tid,
Variable::mock(register, 4),
Expression::const_from_i32(address),
)
}
// FIXME: Move this function to the intermediate_representation module
pub fn pointer_plus_offset(
&self,
tid: &str,
output: &str,
pointer: &str,
offset: i64,
) -> Term<Def> {
Def::assign(
tid,
Variable::mock(output, 4),
Expression::var(pointer, 4).plus_const(offset),
)
}
// FIXME: Move this function to the intermediate_representation module
pub fn pointer_minus_offset(
&self,
tid: &str,
output: &str,
pointer: &str,
offset: i64,
) -> Term<Def> {
Def::assign(
tid,
Variable::mock(output, 4),
Expression::var(pointer, 4).minus_const(offset),
)
}
// FIXME: Move this function to the intermediate_representation module
pub fn pointer_plus_offset_to_temp_var(
&self,
tid: &str,
tmp_name: &str,
pointer: &str,
offset: i64,
) -> Term<Def> {
Def::assign(
tid,
Variable {
name: String::from(tmp_name),
size: ByteSize::new(4),
is_temp: true,
},
Expression::var(pointer, 4).plus_const(offset),
)
}
// FIXME: Move this function to the intermediate_representation module // FIXME: Move this function to the intermediate_representation module
pub fn store_var_content_at_temp_var(&self, tid: &str, tmp_name: &str, var: &str) -> Term<Def> { pub fn store_var_content_at_temp_var(tid: &str, tmp_name: &str, var: &str) -> Term<Def> {
Def::store( Def::store(
tid, tid,
Expression::Var(Variable { Expression::Var(Variable {
name: String::from(tmp_name), name: String::from(tmp_name),
size: ByteSize::new(4), size: ByteSize::new(4),
is_temp: true, is_temp: true,
}), }),
Expression::var(var, 4), expr!(format!("{}:4", var)),
) )
} }
// FIXME: Move this function to the intermediate_representation module // FIXME: Move this function to the intermediate_representation module
pub fn load_var_content_from_temp_var( pub fn load_var_content_from_temp_var(tid: &str, var: &str, tmp_name: &str) -> Term<Def> {
&self, Def::load(
tid: &str, tid,
var: &str, variable!(format!("{}:4", var)),
tmp_name: &str, Expression::Var(Variable {
) -> Term<Def> { name: String::from(tmp_name),
Def::load( size: ByteSize::new(4),
tid, is_temp: true,
Variable::mock(var, 4 as u64), }),
Expression::Var(Variable { )
name: String::from(tmp_name),
size: ByteSize::new(4),
is_temp: true,
}),
)
}
} }
fn mock_defs_for_sprintf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> { fn mock_defs_for_sprintf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> {
let setup = Setup::new();
let mut defs: Vec<Term<Def>> = Vec::new();
/* /*
r11 = INT_ADD sp, 4:4 r11 = INT_ADD sp, 4:4
r12 = COPY 0x3002:4 r12 = COPY 0x3002:4
r0 = INT_SUB r11, 0x58:4 // Destination string pointer r0 = INT_SUB r11, 0x58:4 // Destination string pointer
r1 = COPY 0x6000:4 // Constant format string r1 = COPY 0x6000:4 // Constant format string
OR OR
r1 = INT_SUB r11, 0x62:4 // Variable format string r1 = INT_SUB r11, 0x62:4 // Variable format string
r2 = INT_ADD sp, 24:4 // Variable input in register r2 = INT_ADD sp, 24:4 // Variable input in register
r3 = INT_ADD sp, 16:4 // Variable input in register r3 = INT_ADD sp, 16:4 // Variable input in register
$U1050:4 = INT_ADD sp, 0:4 // Constant string input 'Hello World' on stack $U1050:4 = INT_ADD sp, 0:4 // Constant string input 'Hello World' on stack
STORE ram($U1050:4), r12 STORE ram($U1050:4), r12
r12 = INT_ADD r11, 0x66:4 r12 = INT_ADD r11, 0x66:4
$U1050:4 = INT_ADD sp, 4:4 // Second variable input on stack $U1050:4 = INT_ADD sp, 4:4 // Second variable input on stack
STORE ram($U1050:4), r12 STORE ram($U1050:4), r12
*/ */
defs.push(setup.pointer_plus_offset(&format!("def_0_blk_{}", blk_num), "r11", "sp", 4)); let mut defs = defs![
defs.push(setup.string_input_constant(&format!("def_1_blk_{}", blk_num), "r12", 0x3002)); format!("def_0_blk_{}: r11:4 = sp:4 + 0x4:4", blk_num),
format!("def_1_blk_{}: r12:4 = 0x3002:4", blk_num),
defs.push(setup.pointer_minus_offset(&format!("def_2_blk_{}", blk_num), "r0", "r11", 0x58)); format!("def_2_blk_{}: r0:4 = r11:4 - 0x58:4", blk_num)
];
if format_known { if format_known {
defs.push(setup.format_string_constant(&format!("def_3_blk_{}", blk_num), "r1")); defs.append(defs![&format!("def_3_blk_{}: r1:4 = 0x6000:4", blk_num)].as_mut());
} else { } else {
defs.push(setup.pointer_minus_offset(&format!("def_3_blk_{}", blk_num), "r1", "r11", 0x62)); defs.append(defs![&format!("def_3_blk_{}: r1:4 = r11:4 - 0x62:4", blk_num)].as_mut());
} }
defs.push(setup.pointer_plus_offset(&format!("def_4_blk_{}", blk_num), "r2", "sp", 24)); defs.append(
defs![
defs.push(setup.pointer_plus_offset(&format!("def_5_blk_{}", blk_num), "r3", "sp", 16)); &format!("def_4_blk_{}: r2:4 = sp:4 + 0x18:4", blk_num),
&format!("def_5_blk_{}: r3:4 = sp:4 + 0x10:4", blk_num)
]
.as_mut(),
);
defs.push(setup.pointer_plus_offset_to_temp_var( defs.push(pointer_plus_offset_to_temp_var(
&format!("def_6_blk_{}", blk_num), &format!("def_6_blk_{}", blk_num),
"$U1050", "$U1050",
"sp", "sp",
0, 0,
)); ));
defs.push(setup.store_var_content_at_temp_var( defs.push(store_var_content_at_temp_var(
&format!("def_7_blk_{}", blk_num), &format!("def_7_blk_{}", blk_num),
"$U1050", "$U1050",
"r12", "r12",
)); ));
defs.push(setup.pointer_plus_offset(&format!("def_8_blk_{}", blk_num), "r12", "r11", 0x66)); defs.push(def![&format!(
"def_8_blk_{}: r12:4 = r11:4 + 0x66:4",
blk_num
)]);
defs.push(setup.pointer_plus_offset_to_temp_var( defs.push(pointer_plus_offset_to_temp_var(
&format!("def_9_blk_{}", blk_num), &format!("def_9_blk_{}", blk_num),
"$U1050", "$U1050",
"sp", "sp",
4, 4,
)); ));
defs.push(setup.store_var_content_at_temp_var( defs.push(store_var_content_at_temp_var(
&format!("def_10_blk_{}", blk_num), &format!("def_10_blk_{}", blk_num),
"$U1050", "$U1050",
"r12", "r12",
...@@ -177,67 +114,60 @@ fn mock_defs_for_sprintf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> { ...@@ -177,67 +114,60 @@ fn mock_defs_for_sprintf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> {
} }
fn mock_defs_for_scanf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> { fn mock_defs_for_scanf(format_known: bool, blk_num: usize) -> Vec<Term<Def>> {
let setup = Setup::new();
let mut defs: Vec<Term<Def>> = Vec::new();
/* /*
r11 = INT_ADD sp, 4:4 r11 = INT_ADD sp, 4:4
r0 = INT_SUB r11, 0x3c:4 r0 = INT_SUB r11, 0x3c:4
$U1050 = INT_ADD sp, 0:4 $U1050 = INT_ADD sp, 0:4
STORE ram($U1050:4), r0 - variable output 4 STORE ram($U1050:4), r0 - variable output 4
r3 = INT_SUB r11, 0x50:4 - variable output 3 r3 = INT_SUB r11, 0x50:4 - variable output 3
r2 = INT_SUB r11, 0x62:4 - variable output 2 r2 = INT_SUB r11, 0x62:4 - variable output 2
r1 = INT_SUB r11, 0x78:4 - variable output 1 r1 = INT_SUB r11, 0x78:4 - variable output 1
r0 = LOAD ram(0x6000) - constant format string r0 = LOAD ram(0x6000) - constant format string
OR OR
r0 = INT_SUB r11, 0x82:4 - variable format string r0 = INT_SUB r11, 0x82:4 - variable format string
*/ */
defs.push(setup.pointer_plus_offset(&format!("def_0_blk_{}", blk_num), "r11", "sp", 4)); let mut defs = defs![
format!("def_0_blk_{}: r11:4 = sp:4 + 4:4", blk_num),
defs.push(setup.pointer_minus_offset(&format!("def_1_blk_{}", blk_num), "r0", "r11", 0x3c)); format!("def_1_blk_{}: r0:4 = r11:4 - 0x3c:4", blk_num)
];
defs.push(setup.pointer_plus_offset_to_temp_var( defs.push(pointer_plus_offset_to_temp_var(
&format!("def_2_blk_{}", blk_num), &format!("def_2_blk_{}", blk_num),
"$U1050", "$U1050",
"sp", "sp",
0, 0,
)); ));
defs.push(setup.store_var_content_at_temp_var(
defs.push(store_var_content_at_temp_var(
&format!("def_3_blk_{}", blk_num), &format!("def_3_blk_{}", blk_num),
"$U1050", "$U1050",
"r0", "r0",
)); ));
defs.append(
defs.push(setup.pointer_minus_offset(&format!("def_4_blk_{}", blk_num), "r3", "r11", 0x50)); defs![
format!("def_4_blk_{}: r3:4 = r11:4 - 0x50:4", blk_num),
defs.push(setup.pointer_minus_offset(&format!("def_5_blk_{}", blk_num), "r2", "r11", 0x62)); format!("def_5_blk_{}: r2:4 = r11:4 - 0x62:4", blk_num),
format!("def_6_blk_{}: r1:4 = r11:4 - 0x78:4", blk_num)
defs.push(setup.pointer_minus_offset(&format!("def_6_blk_{}", blk_num), "r1", "r11", 0x78)); ]
.as_mut(),
);
if format_known { if format_known {
defs.push(setup.format_string_constant(&format!("def_7_blk_{}", blk_num), "r0")); defs.push(def![format!("def_7_blk_{}: r0:4 = 0x6000:4", blk_num)]);
} else { } else {
defs.push(setup.pointer_minus_offset(&format!("def_7_blk_{}", blk_num), "r0", "r11", 0x82)); defs.push(def![format!(
"def_7_blk_{}: r0:4 = r11:4 - 0x82:4",
blk_num
)]);
} }
defs defs
} }
fn mock_defs_for_sscanf(source_known: bool, format_known: bool, blk_num: usize) -> Vec<Term<Def>> { fn mock_defs_for_sscanf(source_known: bool, format_known: bool, blk_num: usize) -> Vec<Term<Def>> {
let setup = Setup::new();
let mut defs: Vec<Term<Def>> = Vec::new();
/* /*
r11 = INT_ADD sp, 4:4 r11 = INT_ADD sp, 4:4
r3 = INT_SUB r11, 0x96:4 r3 = INT_SUB r11, 0x96:4
$U1050:4 = INT_ADD sp, 0:4 $U1050:4 = INT_ADD sp, 0:4
...@@ -261,134 +191,125 @@ fn mock_defs_for_sscanf(source_known: bool, format_known: bool, blk_num: usize) ...@@ -261,134 +191,125 @@ fn mock_defs_for_sscanf(source_known: bool, format_known: bool, blk_num: usize)
r0 = INT_SUB r11, 048:4 - variable source string r0 = INT_SUB r11, 048:4 - variable source string
*/ */
let mut defs = defs![
format!("def_0_blk_{}: r11:4 = sp:4 + 4:4", blk_num),
format!("def_1_blk_{}: r3:4 = r11:4 - 0x96:4", blk_num)
];
defs.push(setup.pointer_plus_offset(&format!("def_0_blk_{}", blk_num), "r11", "sp", 4)); defs.push(pointer_plus_offset_to_temp_var(
defs.push(setup.pointer_minus_offset(&format!("def_1_blk_{}", blk_num), "r3", "r11", 0x96));
defs.push(setup.pointer_plus_offset_to_temp_var(
&format!("def_2_blk_{}", blk_num), &format!("def_2_blk_{}", blk_num),
"$U1050", "$U1050",
"sp", "sp",
0, 0,
)); ));
defs.push(setup.store_var_content_at_temp_var(
defs.push(store_var_content_at_temp_var(
&format!("def_3_blk_{}", blk_num), &format!("def_3_blk_{}", blk_num),
"$U1050", "$U1050",
"r3", "r3",
)); ));
defs.push(setup.pointer_minus_offset(&format!("def_4_blk_{}", blk_num), "r3", "r11", 0x88)); defs.push(def![format!(
"def_4_blk_{}: r3:4 = r11:4 - 0x88:4",
blk_num
)]);
defs.push(setup.pointer_plus_offset_to_temp_var( defs.push(pointer_plus_offset_to_temp_var(
&format!("def_5_blk_{}", blk_num), &format!("def_5_blk_{}", blk_num),
"$U1050", "$U1050",
"sp", "sp",
4, 4,
)); ));
defs.push(setup.store_var_content_at_temp_var( defs.push(store_var_content_at_temp_var(
&format!("def_6_blk_{}", blk_num), &format!("def_6_blk_{}", blk_num),
"$U1050", "$U1050",
"r3", "r3",
)); ));
defs.push(setup.pointer_minus_offset(&format!("def_7_blk_{}", blk_num), "r3", "r11", 0x6c)); defs.append(
defs![
defs.push(setup.pointer_minus_offset(&format!("def_8_blk_{}", blk_num), "r2", "r11", 0x80)); format!("def_7_blk_{}: r3:4 = r11:4 - 0x6c:4", blk_num),
format!("def_8_blk_{}: r2:4 = r11:4 - 0x80:4", blk_num)
]
.as_mut(),
);
if format_known { if format_known {
defs.push(setup.format_string_constant(&format!("def_9_blk_{}", blk_num), "r1")); defs.push(def![format!("def_9_blk_{}: r1:4 = 0x6000:4", blk_num)]);
} else { } else {
defs.push(setup.pointer_minus_offset(&format!("def_9_blk_{}", blk_num), "r1", "r11", 0x40)); defs.push(def![format!(
"def_9_blk_{}: r1:4 = r11:4 - 0x40:4",
blk_num
)]);
} }
if source_known { if source_known {
defs.push(setup.string_input_constant(&format!("def_10_blk_{}", blk_num), "r0", 0x7000)); defs.push(def![format!("def_10_blk_{}: r0:4 = 0x7000:4", blk_num)]);
} else { } else {
defs.push(setup.pointer_minus_offset( defs.push(def![format!(
&format!("def_10_blk_{}", blk_num), "def_10_blk_{}: r0:4 = r11:4 - 0x48:4",
"r0", blk_num
"r11", )]);
0x48,
));
} }
defs defs
} }
fn mock_defs_for_strcat(second_input_known: bool, blk_num: usize) -> Vec<Term<Def>> { fn mock_defs_for_strcat(second_input_known: bool, blk_num: usize) -> Vec<Term<Def>> {
let setup = Setup::new();
let mut defs: Vec<Term<Def>> = Vec::new();
/* /*
r11 = INT_ADD sp, 4:4 r11 = INT_ADD sp, 4:4
r0 = INT_SUB r11, 40:4, r0 = INT_SUB r11, 40:4,
r1 = LOAD ram(0x7000) r1 = LOAD ram(0x7000)
OR OR
r1 = INT_ADD r11, 0x24:4 r1 = INT_ADD r11, 0x24:4
*/ */
let mut def = defs![
defs.push(setup.pointer_plus_offset(&format!("def_0_blk_{}", blk_num), "r11", "sp", 4)); format!("def_0_blk_{}: r11:4 = sp:4 + 4:4", blk_num),
format!("def_1_blk_{}: r0:4 = r11:4 - 0x40:4", blk_num)
defs.push(setup.pointer_minus_offset(&format!("def_1_blk_{}", blk_num), "r0", "r11", 0x40)); ];
if second_input_known { if second_input_known {
defs.push(setup.string_input_constant(&format!("def_2_blk_{}", blk_num), "r1", 0x7000)); def.push(def![format!("def_2_blk_{}: r1:4 = 0x7000:4", blk_num)]);
} else { } else {
defs.push(setup.pointer_plus_offset(&format!("def_3_blk_{}", blk_num), "r1", "r11", 0x24)); def.push(def![format!(
"def_3_blk_{}: r1:4 = r11:4 + 0x24:4",
blk_num
)]);
} }
def
defs
}
fn mock_defs_for_free(_blk_num: usize) -> Vec<Term<Def>> {
vec![]
} }
fn mock_defs_for_malloc(blk_num: usize) -> Vec<Term<Def>> { fn mock_defs_for_malloc(blk_num: usize) -> Vec<Term<Def>> {
let setup = Setup::new();
let mut defs: Vec<Term<Def>> = Vec::new();
/* /*
r0 = COPY 0xf r0 = COPY 0xf
*/ */
defs![format!("def_0_blk_{}: r0:4 = 0xf:4", blk_num)]
defs.push(setup.string_input_constant(&format!("def_0_blk_{}", blk_num), "r0", 0xf));
defs
} }
fn mock_defs_for_memcpy(copy_from_global: bool, blk_num: usize) -> Vec<Term<Def>> { fn mock_defs_for_memcpy(copy_from_global: bool, blk_num: usize) -> Vec<Term<Def>> {
let setup = Setup::new();
let mut defs: Vec<Term<Def>> = Vec::new();
/* /*
r11 = INT_ADD sp, 4:4 r11 = INT_ADD sp, 4:4
r0 = INT_SUB r11, 0x40:4, r0 = INT_SUB r11, 0x40:4,
r1 = LOAD ram(0x7000) r1 = LOAD ram(0x7000)
OR OR
r1 = INT_ADD r11, 0x24:4 r1 = INT_ADD r11, 0x24:4
*/ */
let mut def = defs![
defs.push(setup.pointer_plus_offset(&format!("def_0_blk_{}", blk_num), "r11", "sp", 4)); format!("def_0_blk_{}: r11:4 = sp:4 + 4:4", blk_num),
format!("def_1_blk_{}: r0:4 = r11:4 - 0x40:4", blk_num)
defs.push(setup.pointer_minus_offset(&format!("def_1_blk_{}", blk_num), "r0", "r11", 0x40)); ];
if copy_from_global { if copy_from_global {
defs.push(setup.string_input_constant(&format!("def_2_blk_{}", blk_num), "r1", 0x7000)); def.push(def![format!("def_2_blk_{}: r1:4 = 0x7000:4", blk_num)]);
} else { } else {
defs.push(setup.pointer_plus_offset(&format!("def_3_blk_{}", blk_num), "r1", "r11", 0x24)); def.push(def![format!(
"def_3_blk_{}: r1:4 = r11:4 + 0x24:4",
blk_num
)])
} }
def
defs
} }
impl ExternSymbol { impl ExternSymbol {
...@@ -495,7 +416,7 @@ fn mock_block_with_function_call( ...@@ -495,7 +416,7 @@ fn mock_block_with_function_call(
"scanf" => mock_defs_for_scanf(*config.get(0).unwrap(), blk_num), "scanf" => mock_defs_for_scanf(*config.get(0).unwrap(), blk_num),
"sscanf" => mock_defs_for_sscanf(*config.get(0).unwrap(), *config.get(1).unwrap(), blk_num), "sscanf" => mock_defs_for_sscanf(*config.get(0).unwrap(), *config.get(1).unwrap(), blk_num),
"strcat" => mock_defs_for_strcat(*config.get(0).unwrap(), blk_num), "strcat" => mock_defs_for_strcat(*config.get(0).unwrap(), blk_num),
"free" => mock_defs_for_free(blk_num), "free" => vec![],
"malloc" => mock_defs_for_malloc(blk_num), "malloc" => mock_defs_for_malloc(blk_num),
"memcpy" => mock_defs_for_memcpy(*config.get(0).unwrap(), blk_num), "memcpy" => mock_defs_for_memcpy(*config.get(0).unwrap(), blk_num),
_ => panic!("Invalid symbol name for def mock"), _ => panic!("Invalid symbol name for def mock"),
......
...@@ -171,6 +171,7 @@ impl<'a> Context<'a> { ...@@ -171,6 +171,7 @@ impl<'a> Context<'a> {
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::analysis::pointer_inference::Data; use crate::analysis::pointer_inference::Data;
use crate::{bitvec, intermediate_representation::parsing};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
#[test] #[test]
...@@ -183,9 +184,8 @@ pub mod tests { ...@@ -183,9 +184,8 @@ pub mod tests {
let malloc_call_id = AbstractIdentifier::mock("malloc_call", "RAX", 8); let malloc_call_id = AbstractIdentifier::mock("malloc_call", "RAX", 8);
let main_stack_id = AbstractIdentifier::mock("main", "RSP", 8); let main_stack_id = AbstractIdentifier::mock("main", "RSP", 8);
let param_value = Data::from_target(malloc_call_id.clone(), Bitvector::from_i64(2).into()); let param_value = Data::from_target(malloc_call_id.clone(), bitvec!("2:8").into());
let param_value_2 = let param_value_2 = Data::from_target(main_stack_id.clone(), bitvec!("-10:8").into());
Data::from_target(main_stack_id.clone(), Bitvector::from_i64(-10).into());
let param_replacement_map = HashMap::from([ let param_replacement_map = HashMap::from([
(callsite_id, param_value.clone()), (callsite_id, param_value.clone()),
(callsite_id_2, param_value_2.clone()), (callsite_id_2, param_value_2.clone()),
...@@ -196,7 +196,7 @@ pub mod tests { ...@@ -196,7 +196,7 @@ pub mod tests {
context.callee_to_callsites_map = callee_to_callsites_map; context.callee_to_callsites_map = callee_to_callsites_map;
context context
.malloc_tid_to_object_size_map .malloc_tid_to_object_size_map
.insert(Tid::new("malloc_call"), Data::from(Bitvector::from_i64(42))); .insert(Tid::new("malloc_call"), Data::from(bitvec!("42:8")));
context.call_to_caller_fn_map = HashMap::from([ context.call_to_caller_fn_map = HashMap::from([
(Tid::new("malloc_call"), Tid::new("main")), (Tid::new("malloc_call"), Tid::new("main")),
(Tid::new("callsite_id"), Tid::new("main")), (Tid::new("callsite_id"), Tid::new("main")),
......
...@@ -166,7 +166,7 @@ impl<'a> Context<'a> { ...@@ -166,7 +166,7 @@ impl<'a> Context<'a> {
}; };
let mut cwe_warning = let mut cwe_warning =
CweWarning::new("CWE119", super::CWE_MODULE.version, description); CweWarning::new("CWE119", super::CWE_MODULE.version, description);
cwe_warning.tids = vec![format!("{}", call_tid)]; cwe_warning.tids = vec![format!("{call_tid}")];
cwe_warning.addresses = vec![call_tid.address.to_string()]; cwe_warning.addresses = vec![call_tid.address.to_string()];
cwe_warning.other = vec![warnings]; cwe_warning.other = vec![warnings];
self.log_collector.send(cwe_warning.into()).unwrap(); self.log_collector.send(cwe_warning.into()).unwrap();
......
...@@ -218,6 +218,7 @@ fn add_param_replacements_for_call( ...@@ -218,6 +218,7 @@ fn add_param_replacements_for_call(
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::bitvec;
#[test] #[test]
fn test_substitute_param_values_context_sensitive() { fn test_substitute_param_values_context_sensitive() {
...@@ -228,9 +229,8 @@ pub mod tests { ...@@ -228,9 +229,8 @@ pub mod tests {
let recursive_param_id = AbstractIdentifier::mock("main", "RSI", 8); let recursive_param_id = AbstractIdentifier::mock("main", "RSI", 8);
let recursive_callsite_id = AbstractIdentifier::mock("recursive_callsite_id", "RSI", 8); let recursive_callsite_id = AbstractIdentifier::mock("recursive_callsite_id", "RSI", 8);
let param_value = let param_value = Data::from_target(recursive_param_id.clone(), bitvec!("1:8").into());
Data::from_target(recursive_param_id.clone(), Bitvector::from_i64(1).into()); let recursive_param_value = Data::from(bitvec!("41:8"));
let recursive_param_value = Data::from(Bitvector::from_i64(41));
let param_replacement_map = HashMap::from([ let param_replacement_map = HashMap::from([
(callsite_id, param_value.clone()), (callsite_id, param_value.clone()),
(recursive_callsite_id.clone(), recursive_param_value), (recursive_callsite_id.clone(), recursive_param_value),
...@@ -254,7 +254,7 @@ pub mod tests { ...@@ -254,7 +254,7 @@ pub mod tests {
context.call_to_caller_fn_map = call_to_caller_map; context.call_to_caller_fn_map = call_to_caller_map;
// non-recursive substitution // non-recursive substitution
let result = context.substitute_param_values_context_sensitive( let result = context.substitute_param_values_context_sensitive(
&Data::from_target(param_id.clone(), Bitvector::from_i64(5).into()), &Data::from_target(param_id.clone(), bitvec!("5:8").into()),
&Tid::new("callsite_id"), &Tid::new("callsite_id"),
&Tid::new("func"), &Tid::new("func"),
); );
...@@ -264,12 +264,12 @@ pub mod tests { ...@@ -264,12 +264,12 @@ pub mod tests {
); );
// recursive substitution // recursive substitution
let result = context.recursively_substitute_param_values_context_sensitive( let result = context.recursively_substitute_param_values_context_sensitive(
&Data::from_target(param_id, Bitvector::from_i64(5).into()), &Data::from_target(param_id, bitvec!("5:8").into()),
&Tid::new("func"), &Tid::new("func"),
&[Tid::new("callsite_id"), Tid::new("recursive_callsite_id")], &[Tid::new("callsite_id"), Tid::new("recursive_callsite_id")],
); );
println!("{:#}", result.to_json_compact()); println!("{:#}", result.to_json_compact());
assert_eq!(result, Bitvector::from_i64(47).into()); assert_eq!(result, bitvec!("47:8").into());
} }
#[test] #[test]
...@@ -281,9 +281,8 @@ pub mod tests { ...@@ -281,9 +281,8 @@ pub mod tests {
let recursive_param_id = AbstractIdentifier::mock("main", "RSI", 8); let recursive_param_id = AbstractIdentifier::mock("main", "RSI", 8);
let recursive_callsite_id = AbstractIdentifier::mock("recursive_callsite_id", "RSI", 8); let recursive_callsite_id = AbstractIdentifier::mock("recursive_callsite_id", "RSI", 8);
let param_value = let param_value = Data::from_target(recursive_param_id.clone(), bitvec!("1:8").into());
Data::from_target(recursive_param_id.clone(), Bitvector::from_i64(1).into()); let recursive_param_value = Data::from(bitvec!("39:8"));
let recursive_param_value = Data::from(Bitvector::from_i64(39));
let param_replacement_map = HashMap::from([ let param_replacement_map = HashMap::from([
(callsite_id, param_value.clone()), (callsite_id, param_value.clone()),
(recursive_callsite_id.clone(), recursive_param_value), (recursive_callsite_id.clone(), recursive_param_value),
...@@ -304,8 +303,8 @@ pub mod tests { ...@@ -304,8 +303,8 @@ pub mod tests {
// recursive substitution // recursive substitution
let result = context.recursively_substitute_param_values(&Data::from_target( let result = context.recursively_substitute_param_values(&Data::from_target(
param_id, param_id,
Bitvector::from_i64(5).into(), bitvec!("5:8").into(),
)); ));
assert_eq!(result, Bitvector::from_i64(45).into()); assert_eq!(result, bitvec!("45:8").into());
} }
} }
use super::*; use super::*;
use crate::{bitvec, variable};
use std::collections::BTreeSet; use std::collections::BTreeSet;
impl<'a> Context<'a> { impl<'a> Context<'a> {
...@@ -28,9 +29,8 @@ fn test_compute_size_value_of_malloc_like_call() { ...@@ -28,9 +29,8 @@ fn test_compute_size_value_of_malloc_like_call() {
use crate::analysis::pointer_inference::State as PiState; use crate::analysis::pointer_inference::State as PiState;
let project = Project::mock_x64(); let project = Project::mock_x64();
let mut pi_results = PointerInference::mock(&project); let mut pi_results = PointerInference::mock(&project);
let mut malloc_state = let mut malloc_state = PiState::new(&variable!("RSP:8"), Tid::new("func"), BTreeSet::new());
PiState::new(&Variable::mock("RSP", 8), Tid::new("func"), BTreeSet::new()); malloc_state.set_register(&variable!("RDI:8"), bitvec!("3:8").into());
malloc_state.set_register(&Variable::mock("RDI", 8), Bitvector::from_i64(3).into());
*pi_results.get_mut_states_at_tids() = HashMap::from([(Tid::new("malloc_call"), malloc_state)]); *pi_results.get_mut_states_at_tids() = HashMap::from([(Tid::new("malloc_call"), malloc_state)]);
let malloc_symbol = ExternSymbol::mock_x64("malloc"); let malloc_symbol = ExternSymbol::mock_x64("malloc");
...@@ -41,7 +41,7 @@ fn test_compute_size_value_of_malloc_like_call() { ...@@ -41,7 +41,7 @@ fn test_compute_size_value_of_malloc_like_call() {
&pi_results &pi_results
) )
.unwrap(), .unwrap(),
Bitvector::from_i64(3).into() bitvec!("3:8").into()
); );
assert!(compute_size_value_of_malloc_like_call( assert!(compute_size_value_of_malloc_like_call(
&Tid::new("other"), &Tid::new("other"),
......
...@@ -73,11 +73,7 @@ impl State { ...@@ -73,11 +73,7 @@ impl State {
if let Ok((lower_offset, upper_offset)) = offset.try_to_offset_interval() { if let Ok((lower_offset, upper_offset)) = offset.try_to_offset_interval() {
if let Ok(lower_bound) = self.object_lower_bounds.get(id).unwrap().try_to_offset() { if let Ok(lower_bound) = self.object_lower_bounds.get(id).unwrap().try_to_offset() {
if lower_bound > lower_offset { if lower_bound > lower_offset {
out_of_bounds_access_warnings.push(format!("For the object ID {} access to the offset {} may be smaller than the lower object bound of {}.", out_of_bounds_access_warnings.push(format!("For the object ID {id} access to the offset {lower_offset} may be smaller than the lower object bound of {lower_bound}."));
id,
lower_offset,
lower_bound,
));
if let ( if let (
Some(BoundsMetadata { Some(BoundsMetadata {
source: Some(source), source: Some(source),
...@@ -93,7 +89,7 @@ impl State { ...@@ -93,7 +89,7 @@ impl State {
context, context,
); );
out_of_bounds_access_warnings out_of_bounds_access_warnings
.push(format!("Relevant callgraph TIDs: [{}]", call_sequence_tids)); .push(format!("Relevant callgraph TIDs: [{call_sequence_tids}]"));
} else { } else {
out_of_bounds_access_warnings.push(format!( out_of_bounds_access_warnings.push(format!(
"Relevant callgraph TIDs: [{}]", "Relevant callgraph TIDs: [{}]",
...@@ -128,7 +124,7 @@ impl State { ...@@ -128,7 +124,7 @@ impl State {
context, context,
); );
out_of_bounds_access_warnings out_of_bounds_access_warnings
.push(format!("Relevant callgraph TIDs: [{}]", call_sequence_tids)); .push(format!("Relevant callgraph TIDs: [{call_sequence_tids}]"));
} else { } else {
out_of_bounds_access_warnings.push(format!( out_of_bounds_access_warnings.push(format!(
"Relevant callgraph TIDs: [{}]", "Relevant callgraph TIDs: [{}]",
...@@ -204,13 +200,13 @@ impl State { ...@@ -204,13 +200,13 @@ impl State {
let lower_bounds: Vec<_> = self let lower_bounds: Vec<_> = self
.object_lower_bounds .object_lower_bounds
.iter() .iter()
.map(|(id, bound)| Value::String(format!("{}: {}", id, bound))) .map(|(id, bound)| Value::String(format!("{id}: {bound}")))
.collect(); .collect();
state_map.insert("lower_bounds".to_string(), Value::Array(lower_bounds)); state_map.insert("lower_bounds".to_string(), Value::Array(lower_bounds));
let upper_bounds: Vec<_> = self let upper_bounds: Vec<_> = self
.object_upper_bounds .object_upper_bounds
.iter() .iter()
.map(|(id, bound)| Value::String(format!("{}: {}", id, bound))) .map(|(id, bound)| Value::String(format!("{id}: {bound}")))
.collect(); .collect();
state_map.insert("upper_bounds".to_string(), Value::Array(upper_bounds)); state_map.insert("upper_bounds".to_string(), Value::Array(upper_bounds));
...@@ -265,8 +261,8 @@ fn collect_tids_for_cwe_warning( ...@@ -265,8 +261,8 @@ fn collect_tids_for_cwe_warning(
} }
// Build a string out of the TID list // Build a string out of the TID list
tids.iter() tids.iter()
.map(|tid| format!("{}", tid)) .map(|tid| format!("{tid}"))
.reduce(|accum, elem| format!("{}, {}", accum, elem)) .reduce(|accum, elem| format!("{accum}, {elem}"))
.unwrap() .unwrap()
} }
......
...@@ -169,7 +169,7 @@ fn generate_cwe_warning( ...@@ -169,7 +169,7 @@ fn generate_cwe_warning(
_ => panic!("Invalid String Location."), _ => panic!("Invalid String Location."),
}; };
CweWarning::new(CWE_MODULE.name, CWE_MODULE.version, description) CweWarning::new(CWE_MODULE.name, CWE_MODULE.version, description)
.tids(vec![format!("{}", callsite)]) .tids(vec![format!("{callsite}")])
.addresses(vec![callsite.address.clone()]) .addresses(vec![callsite.address.clone()])
.symbols(vec![called_symbol.name.clone()]) .symbols(vec![called_symbol.name.clone()])
} }
...@@ -179,7 +179,7 @@ pub mod tests { ...@@ -179,7 +179,7 @@ pub mod tests {
use std::collections::HashSet; use std::collections::HashSet;
use crate::analysis::pointer_inference::PointerInference as PointerInferenceComputation; use crate::analysis::pointer_inference::PointerInference as PointerInferenceComputation;
use crate::intermediate_representation::*; use crate::{defs, intermediate_representation::*};
use super::*; use super::*;
...@@ -189,21 +189,10 @@ pub mod tests { ...@@ -189,21 +189,10 @@ pub mod tests {
let mut block1 = Blk::mock_with_tid("block1"); let mut block1 = Blk::mock_with_tid("block1");
let block2 = Blk::mock_with_tid("block2"); let block2 = Blk::mock_with_tid("block2");
let def1 = Def::assign( let mut defs = defs!["def2: RDI:8 = RBP:8 + 8:8", "def3: RSI:8 = 0x3002:8"];
"def2",
Variable::mock("RDI", 8 as u64),
Expression::var("RBP", 8).plus_const(8),
);
let def2 = Def::assign(
"def3",
Variable::mock("RSI", 8 as u64),
Expression::Const(Bitvector::from_str_radix(16, "3002").unwrap()),
);
let jump = Jmp::call("call_string", "sprintf", Some("block2")); let jump = Jmp::call("call_string", "sprintf", Some("block2"));
block1.term.defs.push(def1); block1.term.defs.append(&mut defs);
block1.term.defs.push(def2);
block1.term.jmps.push(jump); block1.term.jmps.push(jump);
sub.term.blocks.push(block1); sub.term.blocks.push(block1);
sub.term.blocks.push(block2); sub.term.blocks.push(block2);
......
...@@ -98,7 +98,7 @@ fn generate_cwe_warning(callsite: &Tid, called_symbol: &ExternSymbol) -> CweWarn ...@@ -98,7 +98,7 @@ fn generate_cwe_warning(callsite: &Tid, called_symbol: &ExternSymbol) -> CweWarn
"(Integer Overflow or Wraparound) Potential overflow due to multiplication before call to {} at {}", "(Integer Overflow or Wraparound) Potential overflow due to multiplication before call to {} at {}",
called_symbol.name, callsite.address called_symbol.name, callsite.address
)) ))
.tids(vec![format!("{}", callsite)]) .tids(vec![format!("{callsite}")])
.addresses(vec![callsite.address.clone()]) .addresses(vec![callsite.address.clone()])
.symbols(vec![called_symbol.name.clone()]) .symbols(vec![called_symbol.name.clone()])
} }
......
...@@ -63,7 +63,7 @@ pub fn check_cwe( ...@@ -63,7 +63,7 @@ pub fn check_cwe(
(vec![info_log], Vec::new()) (vec![info_log], Vec::new())
} }
Err(err) => { Err(err) => {
let err_log = LogMessage::new_error(format!("Error while parsing binary: {}", err)) let err_log = LogMessage::new_error(format!("Error while parsing binary: {err}"))
.source(CWE_MODULE.name); .source(CWE_MODULE.name);
(vec![err_log], Vec::new()) (vec![err_log], Vec::new())
} }
......
...@@ -99,7 +99,7 @@ fn generate_cwe_warning(sub: &Term<Sub>, callsite: &Tid) -> CweWarning { ...@@ -99,7 +99,7 @@ fn generate_cwe_warning(sub: &Term<Sub>, callsite: &Tid) -> CweWarning {
"(The program utilizes chroot without dropping privileges and/or changing the directory) at {} ({})", "(The program utilizes chroot without dropping privileges and/or changing the directory) at {} ({})",
callsite.address, sub.term.name callsite.address, sub.term.name
)) ))
.tids(vec![format!("{}", callsite)]) .tids(vec![format!("{callsite}")])
.addresses(vec![callsite.address.clone()]) .addresses(vec![callsite.address.clone()])
.symbols(vec![sub.term.name.clone()]) .symbols(vec![sub.term.name.clone()])
} }
......
...@@ -48,9 +48,7 @@ fn generate_cwe_warning(secure_initializer_func: &str, rand_func: &str) -> CweWa ...@@ -48,9 +48,7 @@ fn generate_cwe_warning(secure_initializer_func: &str, rand_func: &str) -> CweWa
CWE_MODULE.name, CWE_MODULE.name,
CWE_MODULE.version, CWE_MODULE.version,
format!( format!(
"(Insufficient Entropy in PRNG) program uses {} without calling {} before", "(Insufficient Entropy in PRNG) program uses {rand_func} without calling {secure_initializer_func} before"),
rand_func, secure_initializer_func
),
) )
} }
......
...@@ -61,7 +61,7 @@ fn generate_cwe_warning( ...@@ -61,7 +61,7 @@ fn generate_cwe_warning(
"(Time-of-check Time-of-use Race Condition) '{}' is reachable from '{}' at {} ({}). This could lead to a TOCTOU.", "(Time-of-check Time-of-use Race Condition) '{}' is reachable from '{}' at {} ({}). This could lead to a TOCTOU.",
sink, source, sink_callsite.address, sub_name sink, source, sink_callsite.address, sub_name
)) ))
.tids(vec![format!("{}", source_callsite), format!("{}", sink_callsite)]) .tids(vec![format!("{source_callsite}"), format!("{sink_callsite}")])
.addresses(vec![source_callsite.address, sink_callsite.address]) .addresses(vec![source_callsite.address, sink_callsite.address])
.symbols(vec![source.into(), sink.into()]) .symbols(vec![source.into(), sink.into()])
} }
......
...@@ -117,7 +117,7 @@ impl<'a> Context<'a> { ...@@ -117,7 +117,7 @@ impl<'a> Context<'a> {
name: "CWE416".to_string(), name: "CWE416".to_string(),
version: CWE_MODULE.version.to_string(), version: CWE_MODULE.version.to_string(),
addresses: vec![call_tid.address.clone()], addresses: vec![call_tid.address.clone()],
tids: vec![format!("{}", call_tid)], tids: vec![format!("{call_tid}")],
symbols: Vec::new(), symbols: Vec::new(),
other: vec![warnings], other: vec![warnings],
description: format!( description: format!(
...@@ -151,7 +151,7 @@ impl<'a> Context<'a> { ...@@ -151,7 +151,7 @@ impl<'a> Context<'a> {
name: "CWE415".to_string(), name: "CWE415".to_string(),
version: CWE_MODULE.version.to_string(), version: CWE_MODULE.version.to_string(),
addresses: vec![call_tid.address.clone()], addresses: vec![call_tid.address.clone()],
tids: vec![format!("{}", call_tid)], tids: vec![format!("{call_tid}")],
symbols: Vec::new(), symbols: Vec::new(),
other: vec![warning_causes], other: vec![warning_causes],
description: format!( description: format!(
......
...@@ -63,8 +63,7 @@ impl State { ...@@ -63,8 +63,7 @@ impl State {
for id in address.get_relative_values().keys() { for id in address.get_relative_values().keys() {
if let Some(ObjectState::Dangling(free_id)) = self.dangling_objects.get(id) { if let Some(ObjectState::Dangling(free_id)) = self.dangling_objects.get(id) {
free_ids_of_dangling_pointers.push(format!( free_ids_of_dangling_pointers.push(format!(
"Accessed ID {} may have been already freed at {}", "Accessed ID {id} may have been already freed at {free_id}"
id, free_id
)); ));
self.dangling_objects self.dangling_objects
...@@ -101,8 +100,7 @@ impl State { ...@@ -101,8 +100,7 @@ impl State {
.insert(id.clone(), ObjectState::Dangling(call_tid.clone())) .insert(id.clone(), ObjectState::Dangling(call_tid.clone()))
{ {
warnings.push(format!( warnings.push(format!(
"Object {} may have been freed before at {}.", "Object {id} may have been freed before at {old_free_id}."
id, old_free_id
)); ));
} }
} }
...@@ -169,7 +167,7 @@ impl AbstractDomain for State { ...@@ -169,7 +167,7 @@ impl AbstractDomain for State {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::intermediate_representation::Variable; use crate::{bitvec, intermediate_representation::parsing, variable};
use std::collections::BTreeSet; use std::collections::BTreeSet;
#[test] #[test]
...@@ -223,9 +221,9 @@ pub mod tests { ...@@ -223,9 +221,9 @@ pub mod tests {
let mut state = State::new(Tid::new("current_fn")); let mut state = State::new(Tid::new("current_fn"));
let param = Data::from_target( let param = Data::from_target(
AbstractIdentifier::mock("obj_id", "RAX", 8), AbstractIdentifier::mock("obj_id", "RAX", 8),
Bitvector::from_i64(0).into(), bitvec!("0:8").into(),
); );
let pi_state = PiState::new(&Variable::mock("RSP", 8), Tid::new("call"), BTreeSet::new()); let pi_state = PiState::new(&variable!("RSP:8"), Tid::new("call"), BTreeSet::new());
// Check that the parameter is correctly marked as freed in the state. // Check that the parameter is correctly marked as freed in the state.
assert!(state assert!(state
.handle_param_of_free_call(&Tid::new("free_call"), &param, &pi_state) .handle_param_of_free_call(&Tid::new("free_call"), &param, &pi_state)
...@@ -251,12 +249,12 @@ pub mod tests { ...@@ -251,12 +249,12 @@ pub mod tests {
AbstractIdentifier::mock("callee_obj_tid", "RAX", 8), AbstractIdentifier::mock("callee_obj_tid", "RAX", 8),
ObjectState::Dangling(Tid::new("free_tid")), ObjectState::Dangling(Tid::new("free_tid")),
); );
let pi_state = PiState::new(&Variable::mock("RSP", 8), Tid::new("call"), BTreeSet::new()); let pi_state = PiState::new(&variable!("RSP:8"), Tid::new("call"), BTreeSet::new());
let id_replacement_map = BTreeMap::from([( let id_replacement_map = BTreeMap::from([(
AbstractIdentifier::mock("callee_obj_tid", "RAX", 8), AbstractIdentifier::mock("callee_obj_tid", "RAX", 8),
Data::from_target( Data::from_target(
AbstractIdentifier::mock("caller_tid", "RBX", 8), AbstractIdentifier::mock("caller_tid", "RBX", 8),
Bitvector::from_i64(42).into(), bitvec!("42:8").into(),
), ),
)]); )]);
// Check that the callee object ID is correctly translated to a caller object ID // Check that the callee object ID is correctly translated to a caller object ID
......
...@@ -156,7 +156,7 @@ impl<'a> Context<'a> { ...@@ -156,7 +156,7 @@ impl<'a> Context<'a> {
format!("(NULL Pointer Dereference) There is no check if the return value is NULL at {} ({}).", format!("(NULL Pointer Dereference) There is no check if the return value is NULL at {} ({}).",
taint_source.tid.address, taint_source_name)) taint_source.tid.address, taint_source_name))
.addresses(vec![taint_source.tid.address.clone(), taint_access_location.address.clone()]) .addresses(vec![taint_source.tid.address.clone(), taint_access_location.address.clone()])
.tids(vec![format!("{}", taint_source.tid), format!("{}", taint_access_location)]) .tids(vec![format!("{}", taint_source.tid), format!("{taint_access_location}")])
.symbols(vec![taint_source_name]); .symbols(vec![taint_source_name]);
let _ = self.cwe_collector.send(cwe_warning); let _ = self.cwe_collector.send(cwe_warning);
} }
...@@ -412,6 +412,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont ...@@ -412,6 +412,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{def, expr, variable};
impl<'a> Context<'a> { impl<'a> Context<'a> {
pub fn mock( pub fn mock(
...@@ -451,10 +452,7 @@ mod tests { ...@@ -451,10 +452,7 @@ mod tests {
false false
); );
state.set_register_taint( state.set_register_taint(&variable!("RDI:8"), Taint::Tainted(ByteSize::new(8)));
&Variable::mock("RDI", ByteSize::new(8)),
Taint::Tainted(ByteSize::new(8)),
);
assert_eq!( assert_eq!(
context.check_parameters_for_taint( context.check_parameters_for_taint(
&state, &state,
...@@ -476,10 +474,7 @@ mod tests { ...@@ -476,10 +474,7 @@ mod tests {
.handle_generic_call(&state, &Tid::new("call_tid")) .handle_generic_call(&state, &Tid::new("call_tid"))
.is_some()); .is_some());
state.set_register_taint( state.set_register_taint(&variable!("RDX:8"), Taint::Tainted(ByteSize::new(8)));
&Variable::mock("RDX", 8u64),
Taint::Tainted(ByteSize::new(8)),
);
assert!(context assert!(context
.handle_generic_call(&state, &Tid::new("call_tid")) .handle_generic_call(&state, &Tid::new("call_tid"))
.is_none()); .is_none());
...@@ -493,48 +488,21 @@ mod tests { ...@@ -493,48 +488,21 @@ mod tests {
let (mut state, pi_state) = State::mock_with_pi_state(); let (mut state, pi_state) = State::mock_with_pi_state();
state.set_pointer_inference_state(Some(pi_state)); state.set_pointer_inference_state(Some(pi_state));
let assign_def = Term { let assign_def = def!["def: RCX:8 = RAX:8"];
tid: Tid::new("def"),
term: Def::Assign {
var: Variable::mock("RCX", 8u64),
value: Expression::Var(Variable::mock("RAX", 8u64)),
},
};
let result = context.update_def(&state, &assign_def).unwrap(); let result = context.update_def(&state, &assign_def).unwrap();
assert!(result assert!(result.eval(&expr!("RCX:8")).is_tainted());
.eval(&Expression::Var(Variable::mock("RCX", 8u64))) assert!(result.eval(&expr!("RSP:8")).is_top());
.is_tainted());
assert!(result let load_def = def!["def: RCX:8 := Load from RSP:8"];
.eval(&Expression::Var(Variable::mock("RSP", 8u64)))
.is_top());
let load_def = Term {
tid: Tid::new("def"),
term: Def::Load {
var: Variable::mock("RCX", 8u64),
address: Expression::Var(Variable::mock("RSP", 8u64)),
},
};
let result = context.update_def(&state, &load_def).unwrap(); let result = context.update_def(&state, &load_def).unwrap();
assert!(result assert!(result.eval(&expr!("RCX:8")).is_tainted());
.eval(&Expression::Var(Variable::mock("RCX", 8u64))) assert!(result.eval(&expr!("RSP:8")).is_top());
.is_tainted());
assert!(result let store_def = def!["def: Store at RSP:8 := RCX:8"];
.eval(&Expression::Var(Variable::mock("RSP", 8u64)))
.is_top());
let store_def = Term {
tid: Tid::new("def"),
term: Def::Store {
value: Expression::Var(Variable::mock("RCX", 8u64)),
address: Expression::Var(Variable::mock("RSP", 8u64)),
},
};
let result = context.update_def(&state, &store_def).unwrap(); let result = context.update_def(&state, &store_def).unwrap();
let result = context.update_def(&result, &load_def).unwrap(); let result = context.update_def(&result, &load_def).unwrap();
assert!(result assert!(result.eval(&expr!("RCX:8")).is_top());
.eval(&Expression::Var(Variable::mock("RCX", 8u64)))
.is_top());
} }
#[test] #[test]
...@@ -548,7 +516,7 @@ mod tests { ...@@ -548,7 +516,7 @@ mod tests {
tid: Tid::new("jmp"), tid: Tid::new("jmp"),
term: Jmp::CBranch { term: Jmp::CBranch {
target: Tid::new("target"), target: Tid::new("target"),
condition: Expression::Var(Variable::mock("RAX", 8u64)), condition: expr!("RAX:8"),
}, },
}; };
assert!(context assert!(context
...@@ -558,7 +526,7 @@ mod tests { ...@@ -558,7 +526,7 @@ mod tests {
tid: Tid::new("jmp"), tid: Tid::new("jmp"),
term: Jmp::CBranch { term: Jmp::CBranch {
target: Tid::new("target"), target: Tid::new("target"),
condition: Expression::Var(Variable::mock("RBX", 8u64)), condition: expr!("RBX:8"),
}, },
}; };
assert!(context assert!(context
......
...@@ -349,7 +349,7 @@ impl State { ...@@ -349,7 +349,7 @@ impl State {
let register: Vec<(String, Value)> = self let register: Vec<(String, Value)> = self
.register_taint .register_taint
.iter() .iter()
.map(|(var, data)| (var.name.clone(), json!(format!("{}", data)))) .map(|(var, data)| (var.name.clone(), json!(format!("{data}"))))
.collect(); .collect();
let mut memory = Vec::new(); let mut memory = Vec::new();
for (tid, mem_region) in self.memory_taint.iter() { for (tid, mem_region) in self.memory_taint.iter() {
...@@ -357,7 +357,7 @@ impl State { ...@@ -357,7 +357,7 @@ impl State {
for (offset, elem) in mem_region.iter() { for (offset, elem) in mem_region.iter() {
elements.push((offset.to_string(), json!(elem.to_string()))); elements.push((offset.to_string(), json!(elem.to_string())));
} }
memory.push((format!("{}", tid), Value::Object(Map::from_iter(elements)))); memory.push((format!("{tid}"), Value::Object(Map::from_iter(elements))));
} }
let state_map = vec![ let state_map = vec![
( (
...@@ -374,8 +374,8 @@ impl State { ...@@ -374,8 +374,8 @@ impl State {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::abstract_domain::*;
use crate::analysis::pointer_inference::ValueDomain; use crate::analysis::pointer_inference::ValueDomain;
use crate::{abstract_domain::*, expr, variable};
use std::collections::BTreeSet; use std::collections::BTreeSet;
impl State { impl State {
...@@ -389,16 +389,16 @@ mod tests { ...@@ -389,16 +389,16 @@ mod tests {
pub fn mock_with_pi_state() -> (State, PointerInferenceState) { pub fn mock_with_pi_state() -> (State, PointerInferenceState) {
let arg1 = Arg::Register { let arg1 = Arg::Register {
expr: Expression::Var(register("RAX")), expr: expr!("RAX:8"),
data_type: None, data_type: None,
}; };
let arg2 = Arg::Stack { let arg2 = Arg::Stack {
address: Expression::Var(register("RSP")), address: expr!("RSP:8"),
size: ByteSize::new(8), size: ByteSize::new(8),
data_type: None, data_type: None,
}; };
let pi_state = let pi_state =
PointerInferenceState::new(&register("RSP"), Tid::new("func"), BTreeSet::new()); PointerInferenceState::new(&variable!("RSP:8"), Tid::new("func"), BTreeSet::new());
let symbol = ExternSymbol { let symbol = ExternSymbol {
tid: Tid::new("extern_symbol".to_string()), tid: Tid::new("extern_symbol".to_string()),
addresses: vec![], addresses: vec![],
...@@ -414,14 +414,6 @@ mod tests { ...@@ -414,14 +414,6 @@ mod tests {
} }
} }
fn register(name: &str) -> Variable {
Variable {
name: name.into(),
size: ByteSize::new(8),
is_temp: false,
}
}
fn bv(value: i64) -> ValueDomain { fn bv(value: i64) -> ValueDomain {
ValueDomain::from(Bitvector::from_i64(value)) ValueDomain::from(Bitvector::from_i64(value))
} }
...@@ -429,7 +421,7 @@ mod tests { ...@@ -429,7 +421,7 @@ mod tests {
fn new_id(name: &str) -> AbstractIdentifier { fn new_id(name: &str) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new("time0"), Tid::new("time0"),
AbstractLocation::Register(Variable::mock(name, ByteSize::new(8))), AbstractLocation::Register(variable!(format!("{}:8", name))),
) )
} }
...@@ -444,7 +436,7 @@ mod tests { ...@@ -444,7 +436,7 @@ mod tests {
let top = Taint::Top(ByteSize::new(8)); let top = Taint::Top(ByteSize::new(8));
let mut state = State::mock(); let mut state = State::mock();
state.set_register_taint(&register("RAX"), taint.clone()); state.set_register_taint(&variable!("RAX:8"), taint.clone());
let mut other_state = State::mock(); let mut other_state = State::mock();
let address = new_pointer("mem", 10); let address = new_pointer("mem", 10);
...@@ -452,10 +444,10 @@ mod tests { ...@@ -452,10 +444,10 @@ mod tests {
let merged_state = state.merge(&other_state); let merged_state = state.merge(&other_state);
assert_eq!( assert_eq!(
merged_state.register_taint.get(&register("RAX")), merged_state.register_taint.get(&variable!("RAX:8")),
Some(&taint) Some(&taint)
); );
assert_eq!(merged_state.register_taint.get(&register("RBX")), None); assert_eq!(merged_state.register_taint.get(&variable!("RBX:8")), None);
assert_eq!( assert_eq!(
merged_state.load_taint_from_memory(&address, ByteSize::new(8)), merged_state.load_taint_from_memory(&address, ByteSize::new(8)),
taint.clone() taint.clone()
...@@ -471,9 +463,9 @@ mod tests { ...@@ -471,9 +463,9 @@ mod tests {
fn new_state() { fn new_state() {
let (state, pi_state) = State::mock_with_pi_state(); let (state, pi_state) = State::mock_with_pi_state();
let taint = Taint::Tainted(ByteSize::new(8)); let taint = Taint::Tainted(ByteSize::new(8));
assert_eq!(state.register_taint.get(&register("RAX")), Some(&taint)); assert_eq!(state.register_taint.get(&variable!("RAX:8")), Some(&taint));
assert_eq!(state.register_taint.get(&register("RSP")), None); assert_eq!(state.register_taint.get(&variable!("RSP:8")), None);
let address = Expression::Var(register("RSP")); let address = Expression::Var(variable!("RSP:8"));
assert_eq!( assert_eq!(
state.load_taint_from_memory(&pi_state.eval(&address), ByteSize::new(8)), state.load_taint_from_memory(&pi_state.eval(&address), ByteSize::new(8)),
taint taint
...@@ -484,12 +476,12 @@ mod tests { ...@@ -484,12 +476,12 @@ mod tests {
fn eval_expression() { fn eval_expression() {
let (state, _pi_state) = State::mock_with_pi_state(); let (state, _pi_state) = State::mock_with_pi_state();
let expr = Expression::Var(register("RAX")).plus(Expression::Var(register("RBX"))); let expr = expr!("RAX:8 + RBX:8");
assert!(state.eval(&expr).is_tainted()); assert!(state.eval(&expr).is_tainted());
let expr = Expression::UnOp { let expr = Expression::UnOp {
op: UnOpType::Int2Comp, op: UnOpType::Int2Comp,
arg: Box::new(Expression::Var(register("RSP"))), arg: Box::new(Expression::Var(variable!("RSP:8"))),
}; };
assert!(state.eval(&expr).is_top()); assert!(state.eval(&expr).is_top());
} }
......
...@@ -23,8 +23,8 @@ impl Display for Taint { ...@@ -23,8 +23,8 @@ impl Display for Taint {
/// Print the value of a `Taint` object. /// Print the value of a `Taint` object.
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Tainted(size) => write!(f, "Tainted:{}", size), Self::Tainted(size) => write!(f, "Tainted:{size}"),
Self::Top(size) => write!(f, "Top:{}", size), Self::Top(size) => write!(f, "Top:{size}"),
} }
} }
} }
......
...@@ -92,7 +92,7 @@ fn generate_cwe_warning(sub: &Term<Sub>, jmp: &Term<Jmp>, permission_const: u64) ...@@ -92,7 +92,7 @@ fn generate_cwe_warning(sub: &Term<Sub>, jmp: &Term<Jmp>, permission_const: u64)
.addresses(vec![jmp.tid.address.clone()]) .addresses(vec![jmp.tid.address.clone()])
.other(vec![vec![ .other(vec![vec![
"umask_arg".to_string(), "umask_arg".to_string(),
format!("{:#o}", permission_const), format!("{permission_const:#o}"),
]]) ]])
} }
...@@ -122,8 +122,7 @@ pub fn check_cwe( ...@@ -122,8 +122,7 @@ pub fn check_cwe(
} }
Err(err) => { Err(err) => {
let log = LogMessage::new_info(format!( let log = LogMessage::new_info(format!(
"Could not determine umask argument: {}", "Could not determine umask argument: {err}"
err
)) ))
.location(jmp.tid.clone()) .location(jmp.tid.clone())
.source(CWE_MODULE.name); .source(CWE_MODULE.name);
......
...@@ -66,8 +66,7 @@ pub fn generate_cwe_warnings<'a>( ...@@ -66,8 +66,7 @@ pub fn generate_cwe_warnings<'a>(
for (sub_name, jmp_tid, target_name) in dangerous_calls.iter() { for (sub_name, jmp_tid, target_name) in dangerous_calls.iter() {
let address: &String = &jmp_tid.address; let address: &String = &jmp_tid.address;
let description: String = format!( let description: String = format!(
"(Use of Potentially Dangerous Function) {} ({}) -> {}", "(Use of Potentially Dangerous Function) {sub_name} ({address}) -> {target_name}"
sub_name, address, target_name
); );
let cwe_warning = CweWarning::new( let cwe_warning = CweWarning::new(
String::from(CWE_MODULE.name), String::from(CWE_MODULE.name),
...@@ -75,7 +74,7 @@ pub fn generate_cwe_warnings<'a>( ...@@ -75,7 +74,7 @@ pub fn generate_cwe_warnings<'a>(
description, description,
) )
.addresses(vec![address.clone()]) .addresses(vec![address.clone()])
.tids(vec![format!("{}", jmp_tid)]) .tids(vec![format!("{jmp_tid}")])
.symbols(vec![String::from(*sub_name)]) .symbols(vec![String::from(*sub_name)])
.other(vec![vec![ .other(vec![vec![
String::from("dangerous_function"), String::from("dangerous_function"),
......
...@@ -247,7 +247,7 @@ pub fn generate_cwe_warning(sub_name: &str, jmp_tid: &Tid, symbol_name: &str) -> ...@@ -247,7 +247,7 @@ pub fn generate_cwe_warning(sub_name: &str, jmp_tid: &Tid, symbol_name: &str) ->
description, description,
) )
.addresses(vec![jmp_tid.address.clone()]) .addresses(vec![jmp_tid.address.clone()])
.tids(vec![format!("{}", jmp_tid)]) .tids(vec![format!("{jmp_tid}")])
.symbols(vec![String::from(sub_name)]) .symbols(vec![String::from(sub_name)])
.other(vec![vec![ .other(vec![vec![
String::from("OS Command Injection"), String::from("OS Command Injection"),
......
...@@ -50,16 +50,14 @@ pub fn generate_cwe_warning(calls: &[(&str, &Tid, &str)]) -> Vec<CweWarning> { ...@@ -50,16 +50,14 @@ pub fn generate_cwe_warning(calls: &[(&str, &Tid, &str)]) -> Vec<CweWarning> {
for (sub_name, jmp_tid, _) in calls.iter() { for (sub_name, jmp_tid, _) in calls.iter() {
let address: &String = &jmp_tid.address; let address: &String = &jmp_tid.address;
let description = format!( let description = format!(
"(Exposed IOCTL with Insufficient Access Control) Program uses ioctl at {} ({}). Be sure to double check the program and the corresponding driver.", "(Exposed IOCTL with Insufficient Access Control) Program uses ioctl at {sub_name} ({address}). Be sure to double check the program and the corresponding driver.");
sub_name, address
);
let cwe_warning = CweWarning::new( let cwe_warning = CweWarning::new(
String::from(CWE_MODULE.name), String::from(CWE_MODULE.name),
String::from(CWE_MODULE.version), String::from(CWE_MODULE.version),
description, description,
) )
.addresses(vec![address.clone()]) .addresses(vec![address.clone()])
.tids(vec![format!("{}", jmp_tid)]) .tids(vec![format!("{jmp_tid}")])
.symbols(vec![String::from(*sub_name)]); .symbols(vec![String::from(*sub_name)]);
cwe_warnings.push(cwe_warning); cwe_warnings.push(cwe_warning);
......
...@@ -120,7 +120,7 @@ fn generate_cwe_warning(allocation: &Tid, is_stack_allocation: bool) -> CweWarni ...@@ -120,7 +120,7 @@ fn generate_cwe_warning(allocation: &Tid, is_stack_allocation: bool) -> CweWarni
allocation.address allocation.address
), ),
) )
.tids(vec![format!("{}", allocation)]) .tids(vec![format!("{allocation}")])
.addresses(vec![allocation.address.clone()]) .addresses(vec![allocation.address.clone()])
.symbols(vec![]) .symbols(vec![])
} }
......
...@@ -91,9 +91,9 @@ impl Term<Def> { ...@@ -91,9 +91,9 @@ impl Term<Def> {
impl fmt::Display for Def { impl fmt::Display for Def {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Def::Load { var, address } => write!(f, "{} := Load from {}", var, address), Def::Load { var, address } => write!(f, "{var} := Load from {address}"),
Def::Store { address, value } => write!(f, "Store at {} := {}", address, value), Def::Store { address, value } => write!(f, "Store at {address} := {value}"),
Def::Assign { var, value } => write!(f, "{} = {}", var, value), Def::Assign { var, value } => write!(f, "{var} = {value}"),
} }
} }
} }
......
...@@ -213,24 +213,24 @@ impl Expression { ...@@ -213,24 +213,24 @@ impl Expression {
impl fmt::Display for Expression { impl fmt::Display for Expression {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Expression::Var(var) => write!(f, "{}", var), Expression::Var(var) => write!(f, "{var}"),
Expression::Const(c) => { Expression::Const(c) => {
write!(f, "0x{:016x}:i{}", c, c.bytesize().as_bit_length()) write!(f, "0x{:016x}:{}", c, c.bytesize())
} }
Expression::BinOp { op, lhs, rhs } => match op { Expression::BinOp { op, lhs, rhs } => match op {
BinOpType::IntMult BinOpType::IntMult
| BinOpType::IntDiv | BinOpType::IntDiv
| BinOpType::IntRem | BinOpType::IntRem
| BinOpType::FloatMult | BinOpType::FloatMult
| BinOpType::FloatDiv => write!(f, "{} {} {}", lhs, op, rhs), | BinOpType::FloatDiv => write!(f, "{lhs} {op} {rhs}"),
_ => write!(f, "({} {} {})", lhs, op, rhs), _ => write!(f, "({lhs} {op} {rhs})"),
}, },
Expression::UnOp { op, arg } => write!(f, "{}({})", op, arg), Expression::UnOp { op, arg } => write!(f, "{op}({arg})"),
Expression::Cast { op, size: _, arg } => write!(f, "{}({})", op, arg), Expression::Cast { op, size: _, arg } => write!(f, "{op}({arg})"),
Expression::Unknown { Expression::Unknown {
description, description,
size: _, size: _,
} => write!(f, "{}", description), } => write!(f, "{description}"),
Expression::Subpiece { Expression::Subpiece {
low_byte, low_byte,
size, size,
...@@ -263,7 +263,7 @@ impl fmt::Display for BinOpType { ...@@ -263,7 +263,7 @@ impl fmt::Display for BinOpType {
BinOpType::IntRem => write!(f, "%"), BinOpType::IntRem => write!(f, "%"),
BinOpType::BoolAnd => write!(f, "&&"), BinOpType::BoolAnd => write!(f, "&&"),
BinOpType::BoolOr => write!(f, "||"), BinOpType::BoolOr => write!(f, "||"),
_ => write!(f, "{:?}", self), _ => write!(f, "{self:?}"),
} }
} }
} }
...@@ -272,14 +272,15 @@ impl fmt::Display for UnOpType { ...@@ -272,14 +272,15 @@ impl fmt::Display for UnOpType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
UnOpType::BoolNegate => write!(f, "¬"), UnOpType::BoolNegate => write!(f, "¬"),
_ => write!(f, "{:?}", self), UnOpType::IntNegate => write!(f, "-"),
_ => write!(f, "{self:?}"),
} }
} }
} }
impl fmt::Display for CastOpType { impl fmt::Display for CastOpType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self) write!(f, "{self:?}")
} }
} }
#[cfg(test)] #[cfg(test)]
......
...@@ -232,7 +232,7 @@ fn display() { ...@@ -232,7 +232,7 @@ fn display() {
.subpiece(ByteSize(0), ByteSize(20)); .subpiece(ByteSize(0), ByteSize(20));
assert_eq!( assert_eq!(
"(FloatCeil(IntSExt(IntNegate((0x2:i32 + RAX:64 * RBP:64)))))[0-19]", "(FloatCeil(IntSExt(-((0x2:4 + RAX:8 * RBP:8)))))[0-19]",
format!("{}", expr) format!("{}", expr)
); );
} }
...@@ -64,9 +64,9 @@ pub enum Jmp { ...@@ -64,9 +64,9 @@ pub enum Jmp {
impl fmt::Display for Jmp { impl fmt::Display for Jmp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Jmp::Branch(tid) => write!(f, "Jump to {}", tid), Jmp::Branch(tid) => write!(f, "Jump to {tid}"),
Jmp::BranchInd(expr) => write!(f, "Jump to {}", expr), Jmp::BranchInd(expr) => write!(f, "Jump to {expr}"),
Jmp::CBranch { target, condition } => write!(f, "If {} jump to {}", condition, target), Jmp::CBranch { target, condition } => write!(f, "If {condition} jump to {target}"),
Jmp::Call { target, return_ } => write!( Jmp::Call { target, return_ } => write!(
f, f,
"call {} ret {}", "call {} ret {}",
...@@ -79,7 +79,7 @@ impl fmt::Display for Jmp { ...@@ -79,7 +79,7 @@ impl fmt::Display for Jmp {
target, target,
return_.as_ref().unwrap_or(&Tid::new("?")) return_.as_ref().unwrap_or(&Tid::new("?"))
), ),
Jmp::Return(expr) => write!(f, "ret {}", expr), Jmp::Return(expr) => write!(f, "ret {expr}"),
Jmp::CallOther { Jmp::CallOther {
description, description,
return_, return_,
......
//! This module implements macros for an intuitive and condensed construction of intermediate representation elements.
//! [variable!] creates a Variable, [bitvec!] creates a Bitvector, [expr!] creates an Expression, [def!] and [defs!]
//! create `Term<Def>` and Vec<Term<Def>>.
/// Creates a `Variable` specified by the string slice of form `name:size`.
///
/// `size` determines the size in bytes. `is_temp` field is set to `false`.
///
///
/// ## Example
/// ```rust
/// use cwe_checker_lib::intermediate_representation::*;
/// use cwe_checker_lib::variable;
///
/// assert_eq!(variable!("RAX:8"), Variable{ name: "RAX".into(), size: ByteSize::new(8), is_temp: false });
/// ```
#[macro_export]
macro_rules! variable {
( $x:expr ) => {
parsing::parse_variable($x)
};
}
/// Creates a `Bitvector` specified by the string slice of form `0xvalue:size` or value:size.
///
/// `value` is either in hexadecimal representation with leading `0x` or in
/// decimal representation. `size` is in bytes.
/// If `value` does not fit in `size`, `value` is truncated.
/// ## Panics
///- string must contain `:`
///- `size` must be one of `1`, `2`, `4` or `8`
///
/// ## Example
/// ```rust
/// use cwe_checker_lib::intermediate_representation::*;
/// use cwe_checker_lib::bitvec;
///
/// assert_eq!(bitvec!("0xFF:4"), Bitvector::from_u32(0xFF));
/// assert_eq!(bitvec!("0x-A:8"), Bitvector::from_i64(-10));
/// assert_eq!(bitvec!("-5:1"), Bitvector::from_i8(-5));
/// ```
#[macro_export]
macro_rules! bitvec {
( $x:expr ) => {
parsing::parse_bitvec($x)
};
}
/// Creates an `Expression` specified by the string slice.
///
/// Currently supported are: `Var` and `Const` as well as `IntAdd` and `IntSub` of `BinOp`.
/// Supported unary operations are `IntNegate` and `BoolNegate`.
/// Does not support `(`, `)` nor chaining of `+`.
/// ## Panics
///- utilizes `variable!` and `bitvec!` macros and their constrains.
///
///
/// ## Example
/// ```rust
/// use cwe_checker_lib::intermediate_representation::*;
/// use cwe_checker_lib::expr;
///
/// assert_eq!(expr!("0xFF:32"), Expression::Const(Bitvector::from_u32(0xFF)));
/// assert_eq!(
/// expr!("RAX:8"),
/// Expression::Var(Variable {name: "RAX".into(), size: ByteSize::new(8),is_temp: false})
/// );
/// assert_eq!(expr!("¬(0xFF)"), Expression::UnOp { op: UnOpType::BoolNegate, arg: Box::new(Expression::Const(Bitvector::from_u32(0xFF)))});
/// assert_eq!(expr!("-(0xFF)"), Expression::UnOp { op: UnOpType::IntNegate, arg: Box::new(Expression::Const(Bitvector::from_u32(0xFF)))})
///
/// assert_eq!(
/// expr!("RAX:8 + 0x42:8"),
/// Expression::BinOp { op: BinOpType::IntAdd,
/// lhs: Box::new(Expression::Var(Variable { name: "RAX".into(), size: ByteSize::new(8), is_temp: false })),
/// rhs: Box::new(Expression::Const(Bitvector::from_u8(0x42)))}
/// );
/// ```
#[macro_export]
macro_rules! expr {
( $x:expr ) => {
parsing::parse_expr($x)
};
}
/// Creates a `Vec<Term<Def>>` specified by the string slices. Utilizes `variable!`, `bitvec!` and `expr!` macros and their constrains.
///
/// Tid IDs are optionally prefixed by `tid_name: `. If not, `tid_x` is set as Tid ID with incrementing `x` starting by `0`.
/// ## Syntax
/// Load: `var := Load from expr`, with a Variable `var` according to `variable!` macro and and expression `expr` according to `expr!` macro.
///
/// Store: `Store at expr_a := expr_b` with Expressions `expr_a` and `expr_b` according to `expr!` macro.
///
/// Assign: `var = expr` with a Variable `var` according to `var!` macro and an Expression `expr` according to `expr!` macro.
/// ## Example
/// ```rust
/// use cwe_checker_lib::intermediate_representation::*;
/// use cwe_checker_lib::def;
///
/// defs!["tid_x: Store at RSP:8 + 0x8:8 := RAX:8", "RSP:8 = RSP:8 + 0x8:8", "tid_z: RDI:8 := Load from RSP:8"];
/// ```
#[macro_export]
macro_rules! defs {
[$($x:expr),*] => {{
let mut vec = vec![];
let mut _tid_suffix: u8 = 0;
$(
vec.push(parsing::parse_def($x, _tid_suffix));
_tid_suffix += 1;
)*
vec}
};
}
/// Creates a `Term<Def>` specified by the string slices. Utilizes `variable!`, `bitvec!` and `expr!` macros and their constrains.
///
/// Tid ID is optionally prefixed by `tid_name: `. If not, Tid ID `tid_0` is set.
/// ## Syntax
/// Load: `var := Load from expr`, with a Variable `var` according to `variable!` macro and and expression `expr` according to `expr!` macro.
///
/// Store: `Store at expr_a := expr_b` with Expressions `expr_a` and `expr_b` according to `expr!` macro.
///
/// Assign: `var = expr` with a Variable `var` according to `var!` macro and an Expression `expr` according to `expr!` macro.
/// ## Example
/// ```rust
/// use cwe_checker_lib::intermediate_representation::*;
/// use cwe_checker_lib::def;
///
/// def!["tid_x: Store at RSP:8 + 0x8:8 := RAX:8"];
/// def!["RSP:8 = RSP:8 + 0x8:8"];
/// ```
#[macro_export]
macro_rules! def {
($x:expr) => {
parsing::parse_def($x, 0)
};
}
pub mod parsing {
//! Provides parsing functions for the macros defined in `macros.rs`.
//! This module hides the parsing functions and allows exposure of the macros only.
use crate::intermediate_representation::{
BinOpType, Bitvector, ByteSize, Def, Expression, Term, Tid, UnOpType, Variable,
};
use regex::RegexSet;
/// Parses a Variable defining string slice and returns its corresponding Variable.
///
/// This is used for the `var!` macro, consider the macro documentation for more details.
#[allow(dead_code)]
pub fn parse_variable<S: AsRef<str>>(str: S) -> Variable {
let args: Vec<&str> = str.as_ref().split(':').collect();
if args.len() != 2 {
panic!("Could not uniquely parse variable: {}", str.as_ref())
}
let (name, size) = (args[0], args[1]);
Variable {
name: name.to_string(),
size: ByteSize(size.parse().unwrap()),
is_temp: false,
}
}
/// Parses a Bitvector defining string slice and returns its corresponding Bitvector.
///
/// This is used for the `bitvec!` macro, consider the macro documentation for more details.
#[allow(dead_code)]
pub fn parse_bitvec<S: AsRef<str>>(str: S) -> Bitvector {
let args: Vec<&str> = str.as_ref().split(&['x', ':'][..]).collect();
let value: i128;
if args.len() == 3 {
// hex representation
value = i128::from_str_radix(args[1], 16).unwrap();
} else if args.len() == 2 {
// dec representation
value = args[0].parse().unwrap();
} else {
panic!("Could not uniquely parse bitvector: {}", str.as_ref())
}
Bitvector::from_i128(value)
.into_sign_resize(args[args.len() - 1].parse::<usize>().unwrap() * 8)
}
/// Parses a Expression defining string slice and returns its corresponding Expression.
///
/// This is used for the `expr!` macro, consider the macro documentation for more details.
/// Variable names must not start with a number.
#[allow(dead_code)]
pub fn parse_expr<S: AsRef<str>>(str: S) -> Expression {
let set = RegexSet::new([
r"^[[:alnum:]&&[^0-9]]{1}[[:alnum:]&&[^x]]?[[:alnum:]]*:[0-9]{1,2}$", // Variable
r"^((0x(-)?[[:alnum:]]+)|^(-)?([0-9])+)+:[0-9]+$", // Constant
r"^[^\+]*\+{1}[^\+]*$", // BinOp (IntAdd)
r"^[[:ascii:]]+ \-{1} [[:ascii:]]+$", // BinOp (IntSub)
r"^-\([[:ascii:]]*\)$", // UnOp (IntNegate)
r"^¬\([[:ascii:]]*\)$", // UnOp (BoolNegate)
])
.unwrap();
let result: Vec<usize> = set.matches(str.as_ref()).into_iter().collect();
if result.len() != 1 {
panic!("Expression: {} matched Regex: {:#?}", str.as_ref(), result)
}
match result[0] {
0 => Expression::Var(parse_variable(str)),
1 => Expression::Const(parse_bitvec(str)),
2 => {
let args: Vec<&str> = str.as_ref().split('+').collect();
Expression::BinOp {
op: BinOpType::IntAdd,
lhs: Box::new(parse_expr(args[0].trim())),
rhs: Box::new(parse_expr(args[1].trim())),
}
}
3 => {
let args: Vec<&str> = str.as_ref().split('-').collect();
Expression::BinOp {
op: BinOpType::IntSub,
lhs: Box::new(parse_expr(args[0].trim())),
rhs: Box::new(parse_expr(args[1].trim())),
}
}
4 => {
let arg: &str = str.as_ref().trim_matches(&['-', '(', ')'][..]);
Expression::UnOp {
op: UnOpType::IntNegate,
arg: Box::new(parse_expr(arg.trim())),
}
}
5 => {
let arg: &str = str.as_ref().trim_matches(&['¬', '(', ')'][..]);
Expression::UnOp {
op: UnOpType::BoolNegate,
arg: Box::new(parse_expr(arg.trim())),
}
}
_ => panic!(),
}
}
/// Parses a `Term<Def>` defining string slice and returns its corresponding `Term<Def>`.
///
/// This is used for the `def!` and `defs!` macro, consider the macro documentation for more details.
#[allow(dead_code)]
pub fn parse_def<S: AsRef<str>>(str: S, tid_suffix: u8) -> Term<Def> {
let set = RegexSet::new([
r"^[[:ascii:]]+: [[:alnum:]:]* = ", // Assign with tid
r"^[[:alnum:]:]* = ", // Assign without tid
r"^[[:ascii:]]+: [[:alnum:]:]* := Load from [[:ascii:]:]*$", // Load with tid
r"^[[:alnum:]:]* := Load from [[:ascii:]:]*$", // Load without tid
r"^[[:ascii:]]+: Store at [[:ascii:]:]* := ", // Store with tid
r"^Store at [[:ascii:]:]* := ", // Store without tid
])
.unwrap();
let result: Vec<usize> = set.matches(str.as_ref()).into_iter().collect();
if result.len() != 1 {
panic!("Def: {} matched Regex: {:#?}", str.as_ref(), result)
}
let (tid, def): (String, &str) = match result[0] {
0 | 2 | 4 => {
// tid is specified
let (tid, def) = str.as_ref().split_once(": ").unwrap();
(tid.into(), def)
}
_ => (format!("tid_{}", tid_suffix), str.as_ref()), // unspecified tid
};
match result[0] {
0 | 1 => {
// Assign
let args: Vec<&str> = def.split('=').collect();
Term {
tid: Tid::new(tid),
term: Def::Assign {
var: parse_variable(args[0].trim()),
value: parse_expr(args[1].trim()),
},
}
}
2 | 3 => {
// Load
let args: Vec<&str> = def.split(":= Load from").collect();
Term {
tid: Tid::new(tid),
term: Def::Load {
var: parse_variable(args[0].trim()),
address: parse_expr(args[1].trim()),
},
}
}
4 | 5 => {
// Store
let args: Vec<&str> = def.split(":=").collect();
Term {
tid: Tid::new(tid),
term: Def::Store {
address: parse_expr(args[0].trim_start_matches("Store at ").trim()),
value: parse_expr(args[1].trim()),
},
}
}
_ => panic!(),
}
}
}
#[cfg(test)]
mod tests;
use crate::intermediate_representation::*;
#[test]
fn test_var() {
assert_eq!(
variable!("RAX:8"),
Variable {
name: "RAX".to_string(),
size: ByteSize(8),
is_temp: false
}
);
}
#[test]
#[should_panic]
fn var_empty_panics() {
variable!("");
}
#[test]
#[should_panic]
fn var_no_colon_panics() {
variable!("RAX8");
}
#[test]
#[should_panic]
fn var_no_size_panics() {
variable!("RAX:");
}
#[test]
fn test_bitvec() {
assert_eq!(bitvec!("0x42:1"), Bitvector::from_u8(0x42));
assert_eq!(bitvec!("0xFF:2"), Bitvector::from_u16(0xFF));
assert_eq!(bitvec!("0xAAFF:1"), Bitvector::from_u8(0xFF));
assert_eq!(bitvec!("0x-01:1"), Bitvector::from_i8(-1));
assert_eq!(bitvec!("123:4"), Bitvector::from_u32(123));
assert_eq!(bitvec!("-42:8"), Bitvector::from_i64(-42));
}
#[test]
fn test_expr_var() {
assert_eq!(
expr!("RAX:8"),
Expression::Var(Variable {
name: "RAX".into(),
size: ByteSize(8),
is_temp: false
})
);
}
#[test]
fn test_expr_const() {
assert_eq!(
expr!("0x42:8"),
Expression::Const(Bitvector::from_u64(0x42))
);
assert_eq!(
expr!("0xFFFF:1"),
Expression::Const(Bitvector::from_u8(255))
);
assert_eq!(expr!("42:4"), Expression::Const(Bitvector::from_u32(42)));
}
#[test]
fn test_expr_plus() {
assert_eq!(
expr!("RAX:8 + 0x42:8"),
Expression::BinOp {
op: BinOpType::IntAdd,
lhs: Box::new(Expression::Var(Variable {
name: "RAX".into(),
size: ByteSize(8),
is_temp: false
})),
rhs: Box::new(Expression::Const(Bitvector::from_u64(0x42)))
}
);
}
#[test]
fn test_expr_minus() {
assert_eq!(
expr!("RAX:8 - 0x42:8"),
Expression::BinOp {
op: BinOpType::IntSub,
lhs: Box::new(Expression::Var(Variable {
name: "RAX".into(),
size: ByteSize(8),
is_temp: false
})),
rhs: Box::new(Expression::Const(Bitvector::from_u64(0x42)))
}
);
}
#[test]
fn test_expr_int_negate() {
assert_eq!(
expr!("-(RAX:8)"),
Expression::UnOp {
op: UnOpType::IntNegate,
arg: Box::new(Expression::Var(Variable {
name: "RAX".into(),
size: ByteSize(8),
is_temp: false
}))
}
);
}
#[test]
fn test_expr_bool_negate() {
assert_eq!(
expr!("¬(RAX:8)"),
Expression::UnOp {
op: UnOpType::BoolNegate,
arg: Box::new(Expression::Var(Variable {
name: "RAX".into(),
size: ByteSize(8),
is_temp: false
}))
}
);
}
#[test]
fn test_def_tid() {
let defs = defs![
"RDI:8 = RAX:8 + RBP:8",
"A: RAX:8 = 0x42:1",
"RDX:8 = RAX:8 + RBP:8"
];
assert_eq!(
defs.into_iter()
.map(|x| x.tid.to_string())
.collect::<Vec<String>>(),
["tid_0", "A", "tid_2"]
)
}
#[test]
fn test_defs_assign() {
assert_eq!(
defs!["tid_0: RAX:8 = 0x42:1", "tid_1: RDI:8 = RAX:8 + RBP:8"],
vec![
Term {
tid: Tid::new("tid_0"),
term: Def::Assign {
var: Variable {
name: "RAX".into(),
size: ByteSize(8),
is_temp: false
},
value: Expression::Const(Bitvector::from_i8(0x42))
}
},
Term {
tid: Tid::new("tid_1"),
term: Def::Assign {
var: Variable {
name: "RDI".into(),
size: ByteSize(8),
is_temp: false
},
value: Expression::BinOp {
op: BinOpType::IntAdd,
lhs: Box::new(Expression::Var(Variable {
name: "RAX".into(),
size: ByteSize(8),
is_temp: false
})),
rhs: Box::new(Expression::Var(Variable {
name: "RBP".into(),
size: ByteSize(8),
is_temp: false
}))
}
}
}
]
);
}
#[test]
fn test_defs_store() {
assert_eq!(
defs!["tid: Store at RSP:8 - 0x8:1 := 0x42:1"],
vec![Term {
tid: Tid::new("tid"),
term: Def::Store {
address: Expression::BinOp {
op: BinOpType::IntSub,
lhs: Box::new(Expression::Var(Variable {
name: "RSP".into(),
size: ByteSize(8),
is_temp: false
})),
rhs: Box::new(Expression::Const(Bitvector::from_u8(0x8)))
},
value: Expression::Const(Bitvector::from_u8(0x42))
}
}]
)
}
#[test]
fn test_defs_load() {
assert_eq!(
defs!["tid_a: RAX:8 := Load from 0xFF00:4 + 0x08:4"],
vec![Term {
tid: Tid::new("tid_a"),
term: Def::Load {
var: Variable {
name: "RAX".into(),
size: ByteSize(8),
is_temp: false
},
address: Expression::BinOp {
op: BinOpType::IntAdd,
lhs: Box::new(Expression::Const(Bitvector::from_u32(0xFF00))),
rhs: Box::new(Expression::Const(Bitvector::from_u32(0x08)))
}
}
}]
)
}
#[test]
fn test_defs_composition() {
assert_eq!(
defs![
"tid_a: Store at RSP:8 + -(0x8:1) := RAX:8",
"tid_b: RSP:8 = RSP:8 + ¬(0x8:1)",
"tid_c: RDI:8 := Load from RSP:8"
],
vec![
Term {
tid: Tid::new("tid_a"),
term: Def::Store {
address: Expression::BinOp {
op: BinOpType::IntAdd,
lhs: Box::new(Expression::Var(Variable {
name: "RSP".into(),
size: ByteSize(8),
is_temp: false
})),
rhs: Box::new(Expression::UnOp {
op: UnOpType::IntNegate,
arg: Box::new(Expression::Const(Bitvector::from_u8(0x08)))
})
},
value: Expression::Var(Variable {
name: "RAX".into(),
size: ByteSize(8),
is_temp: false
})
}
},
Term {
tid: Tid::new("tid_b"),
term: Def::Assign {
var: Variable {
name: "RSP".into(),
size: ByteSize(8),
is_temp: false
},
value: Expression::BinOp {
op: BinOpType::IntAdd,
lhs: Box::new(Expression::Var(Variable {
name: "RSP".into(),
size: ByteSize(8),
is_temp: false
})),
rhs: Box::new(Expression::UnOp {
op: UnOpType::BoolNegate,
arg: Box::new(Expression::Const(Bitvector::from_u8(0x08)))
})
}
}
},
Term {
tid: Tid::new("tid_c"),
term: Def::Load {
var: Variable {
name: "RDI".into(),
size: ByteSize(8),
is_temp: false
},
address: Expression::Var(Variable {
name: "RSP".into(),
size: ByteSize(8),
is_temp: false
})
}
}
]
)
}
...@@ -32,6 +32,11 @@ mod project; ...@@ -32,6 +32,11 @@ mod project;
pub use project::*; pub use project::*;
mod runtime_memory_image; mod runtime_memory_image;
pub use runtime_memory_image::*; pub use runtime_memory_image::*;
#[cfg(test)]
#[macro_use]
mod macros;
#[cfg(test)]
pub use macros::*;
/// An unsigned number of bytes. /// An unsigned number of bytes.
/// ///
......
...@@ -37,7 +37,7 @@ impl Tid { ...@@ -37,7 +37,7 @@ impl Tid {
/// the returned block ID is the one that would be executed first if a jump to the given address happened. /// the returned block ID is the one that would be executed first if a jump to the given address happened.
pub fn blk_id_at_address(address: &str) -> Tid { pub fn blk_id_at_address(address: &str) -> Tid {
Tid { Tid {
id: format!("blk_{}", address), id: format!("blk_{address}"),
address: address.to_string(), address: address.to_string(),
} }
} }
......
...@@ -22,7 +22,7 @@ pub struct Variable { ...@@ -22,7 +22,7 @@ pub struct Variable {
impl Display for Variable { impl Display for Variable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.name, self.size.as_bit_length())?; write!(f, "{}:{}", self.name, self.size)?;
if self.is_temp { if self.is_temp {
write!(f, "(temp)")?; write!(f, "(temp)")?;
} }
......
...@@ -42,7 +42,7 @@ impl From<Variable> for IrExpression { ...@@ -42,7 +42,7 @@ impl From<Variable> for IrExpression {
match (&pcode_var.name, &pcode_var.value) { match (&pcode_var.name, &pcode_var.value) {
(Some(_name), None) => IrExpression::Var(pcode_var.into()), (Some(_name), None) => IrExpression::Var(pcode_var.into()),
(None, Some(_hex_value)) => IrExpression::Const(pcode_var.parse_const_to_bitvector()), (None, Some(_hex_value)) => IrExpression::Const(pcode_var.parse_const_to_bitvector()),
_ => panic!("Conversion failed:\n{:?}", pcode_var), _ => panic!("Conversion failed:\n{pcode_var:?}"),
} }
} }
} }
......
...@@ -312,7 +312,7 @@ fn piecing_or_zero_extending() { ...@@ -312,7 +312,7 @@ fn piecing_or_zero_extending() {
replace_subregister_in_block(&mut block, &register_map); replace_subregister_in_block(&mut block, &register_map);
assert!(check_defs_of_block( assert!(check_defs_of_block(
&block, &block,
vec!["zext_eax_to_rax: RAX:64 = IntZExt(0x0:i32)"] vec!["zext_eax_to_rax: RAX:8 = IntZExt(0x0:4)"]
)); ));
// Test whether zero extension to base register is still recognized // Test whether zero extension to base register is still recognized
...@@ -328,7 +328,7 @@ fn piecing_or_zero_extending() { ...@@ -328,7 +328,7 @@ fn piecing_or_zero_extending() {
replace_subregister_in_block(&mut block, &register_map); replace_subregister_in_block(&mut block, &register_map);
assert!(check_defs_of_block( assert!(check_defs_of_block(
&block, &block,
vec!["zext_ah_to_rax: RAX:64 = IntZExt(0x0:i8)"] vec!["zext_ah_to_rax: RAX:8 = IntZExt(0x0:1)"]
)); ));
// Test when the next register is a zero extension to a different register. // Test when the next register is a zero extension to a different register.
...@@ -344,8 +344,8 @@ fn piecing_or_zero_extending() { ...@@ -344,8 +344,8 @@ fn piecing_or_zero_extending() {
assert!(check_defs_of_block( assert!(check_defs_of_block(
&block, &block,
vec![ vec![
"eax_assign: RAX:64 = ((RAX:64)[4-7] Piece 0x0:i32)", "eax_assign: RAX:8 = ((RAX:8)[4-7] Piece 0x0:4)",
"zext_eax_to_rcx: RCX:64 = IntZExt((RAX:64)[0-3])" "zext_eax_to_rcx: RCX:8 = IntZExt((RAX:8)[0-3])"
] ]
)); ));
...@@ -362,8 +362,8 @@ fn piecing_or_zero_extending() { ...@@ -362,8 +362,8 @@ fn piecing_or_zero_extending() {
assert!(check_defs_of_block( assert!(check_defs_of_block(
&block, &block,
vec![ vec![
"ah_assign: RAX:64 = (((RAX:64)[2-7] Piece 0x0:i8) Piece (RAX:64)[0-0])", "ah_assign: RAX:8 = (((RAX:8)[2-7] Piece 0x0:1) Piece (RAX:8)[0-0])",
"zext_ah_to_eax: RAX:64 = ((RAX:64)[4-7] Piece IntZExt((RAX:64)[1-1]))", "zext_ah_to_eax: RAX:8 = ((RAX:8)[4-7] Piece IntZExt((RAX:8)[1-1]))",
] ]
)); ));
...@@ -380,8 +380,8 @@ fn piecing_or_zero_extending() { ...@@ -380,8 +380,8 @@ fn piecing_or_zero_extending() {
assert!(check_defs_of_block( assert!(check_defs_of_block(
&block, &block,
vec![ vec![
"load_to_eax: loaded_value:32(temp) := Load from 0x0:i64", "load_to_eax: loaded_value:4(temp) := Load from 0x0:8",
"zext_eax_to_rax: RAX:64 = IntZExt(loaded_value:32(temp))", "zext_eax_to_rax: RAX:8 = IntZExt(loaded_value:4(temp))",
] ]
)); ));
...@@ -398,9 +398,9 @@ fn piecing_or_zero_extending() { ...@@ -398,9 +398,9 @@ fn piecing_or_zero_extending() {
assert!(check_defs_of_block( assert!(check_defs_of_block(
&block, &block,
vec![ vec![
"load_to_eax: loaded_value:32(temp) := Load from 0x0:i64", "load_to_eax: loaded_value:4(temp) := Load from 0x0:8",
"load_to_eax_cast_to_base: RAX:64 = ((RAX:64)[4-7] Piece loaded_value:32(temp))", "load_to_eax_cast_to_base: RAX:8 = ((RAX:8)[4-7] Piece loaded_value:4(temp))",
"zext_eax_to_rcx: RCX:64 = IntZExt((RAX:64)[0-3])" "zext_eax_to_rcx: RCX:8 = IntZExt((RAX:8)[0-3])"
] ]
)); ));
} }
...@@ -293,7 +293,7 @@ impl Blk { ...@@ -293,7 +293,7 @@ impl Blk {
_ => panic!(), _ => panic!(),
}; };
if input.address.is_some() { if input.address.is_some() {
let temp_register_name = format!("$load_temp{}", index); let temp_register_name = format!("$load_temp{index}");
let load_def = input.to_load_def(temp_register_name, generic_pointer_size); let load_def = input.to_load_def(temp_register_name, generic_pointer_size);
*input = load_def.lhs.clone().unwrap(); *input = load_def.lhs.clone().unwrap();
refactored_defs.push(Term { refactored_defs.push(Term {
......
...@@ -792,28 +792,28 @@ fn from_project_to_ir_project() { ...@@ -792,28 +792,28 @@ fn from_project_to_ir_project() {
// Checks if the other definitions and the jump were correctly casted. // Checks if the other definitions and the jump were correctly casted.
assert_eq!( assert_eq!(
format!("{}", ir_block.defs[0].term), format!("{}", ir_block.defs[0].term),
"loaded_value:32(temp) := Load from (RDI:64)[0-3]".to_string() "loaded_value:4(temp) := Load from (RDI:8)[0-3]".to_string()
); );
assert_eq!( assert_eq!(
format!("{}", ir_block.defs[1].term), format!("{}", ir_block.defs[1].term),
"RDI:64 = ((RDI:64)[4-7] Piece loaded_value:32(temp))".to_string() "RDI:8 = ((RDI:8)[4-7] Piece loaded_value:4(temp))".to_string()
); );
assert_eq!( assert_eq!(
format!("{}", ir_block.defs[2].term), format!("{}", ir_block.defs[2].term),
"RAX:64 = (((RAX:64)[2-7] Piece ((RAX:64)[1-1] ^ (RAX:64)[1-1])) Piece (RAX:64)[0-0])" "RAX:8 = (((RAX:8)[2-7] Piece ((RAX:8)[1-1] ^ (RAX:8)[1-1])) Piece (RAX:8)[0-0])"
.to_string() .to_string()
); );
assert_eq!( assert_eq!(
format!("{}", ir_block.defs[3].term), format!("{}", ir_block.defs[3].term),
"RAX:64 = IntZExt((RDI:64)[0-3])".to_string() "RAX:8 = IntZExt((RDI:8)[0-3])".to_string()
); );
assert_eq!( assert_eq!(
format!("{}", ir_block.defs[4].term), format!("{}", ir_block.defs[4].term),
"RAX:64 = ((RAX:64)[4-7] Piece (0x0:i16 Piece (RAX:64)[0-1]))".to_string() "RAX:8 = ((RAX:8)[4-7] Piece (0x0:2 Piece (RAX:8)[0-1]))".to_string()
); );
assert_eq!( assert_eq!(
format!("{}", ir_block.defs[5].term), format!("{}", ir_block.defs[5].term),
"RAX:64 = ((RAX:64)[2-7] Piece ((RDI:64)[0-3])[1-2])".to_string() "RAX:8 = ((RAX:8)[2-7] Piece ((RDI:8)[0-3])[1-2])".to_string()
); );
assert_eq!(ir_block.jmps[0].term, expected_jmp); assert_eq!(ir_block.jmps[0].term, expected_jmp);
} }
...@@ -31,7 +31,7 @@ pub fn get_project_from_ghidra( ...@@ -31,7 +31,7 @@ pub fn get_project_from_ghidra(
.as_millis() .as_millis()
); );
// Create a unique name for the pipe // Create a unique name for the pipe
let fifo_path = tmp_folder.join(format!("pcode_{}.pipe", timestamp_suffix)); let fifo_path = tmp_folder.join(format!("pcode_{timestamp_suffix}.pipe"));
let ghidra_command = generate_ghidra_call_command( let ghidra_command = generate_ghidra_call_command(
file_path, file_path,
&fifo_path, &fifo_path,
...@@ -93,7 +93,7 @@ fn execute_ghidra( ...@@ -93,7 +93,7 @@ fn execute_ghidra(
let output = match ghidra_command.output() { let output = match ghidra_command.output() {
Ok(output) => output, Ok(output) => output,
Err(err) => { Err(err) => {
eprintln!("Ghidra could not be executed: {}", err); eprintln!("Ghidra could not be executed: {err}");
std::process::exit(101); std::process::exit(101);
} }
}; };
...@@ -107,7 +107,7 @@ fn execute_ghidra( ...@@ -107,7 +107,7 @@ fn execute_ghidra(
eprintln!("{}", String::from_utf8(output.stdout).unwrap()); eprintln!("{}", String::from_utf8(output.stdout).unwrap());
eprintln!("{}", String::from_utf8(output.stderr).unwrap()); eprintln!("{}", String::from_utf8(output.stderr).unwrap());
if let Some(code) = output.status.code() { if let Some(code) = output.status.code() {
eprintln!("Ghidra plugin failed with exit code {}", code); eprintln!("Ghidra plugin failed with exit code {code}");
} }
eprintln!("Execution of Ghidra plugin failed."); eprintln!("Execution of Ghidra plugin failed.");
} else { } else {
...@@ -150,7 +150,7 @@ fn generate_ghidra_call_command( ...@@ -150,7 +150,7 @@ fn generate_ghidra_call_command(
let mut ghidra_command = Command::new(headless_path); let mut ghidra_command = Command::new(headless_path);
ghidra_command ghidra_command
.arg(&tmp_folder) // The folder where temporary files should be stored .arg(&tmp_folder) // The folder where temporary files should be stored
.arg(format!("PcodeExtractor_{}_{}", filename, timestamp_suffix)) // The name of the temporary Ghidra Project. .arg(format!("PcodeExtractor_{filename}_{timestamp_suffix}")) // The name of the temporary Ghidra Project.
.arg("-import") // Import a file into the Ghidra project .arg("-import") // Import a file into the Ghidra project
.arg(file_path) // File import path .arg(file_path) // File import path
.arg("-postScript") // Execute a script after standard analysis by Ghidra finished .arg("-postScript") // Execute a script after standard analysis by Ghidra finished
......
...@@ -155,9 +155,9 @@ impl std::fmt::Display for LogMessage { ...@@ -155,9 +155,9 @@ impl std::fmt::Display for LogMessage {
LogLevel::Info => write!(formatter, "INFO: ")?, LogLevel::Info => write!(formatter, "INFO: ")?,
}; };
match (&self.source, &self.location) { match (&self.source, &self.location) {
(Some(source), Some(location)) => write!(formatter, "{} @ {}: ", source, location)?, (Some(source), Some(location)) => write!(formatter, "{source} @ {location}: ")?,
(Some(source), None) => write!(formatter, "{}: ", source)?, (Some(source), None) => write!(formatter, "{source}: ")?,
(None, Some(location)) => write!(formatter, "{}: ", location)?, (None, Some(location)) => write!(formatter, "{location}: ")?,
(None, None) => (), (None, None) => (),
}; };
write!(formatter, "{}", self.text) write!(formatter, "{}", self.text)
...@@ -177,23 +177,22 @@ pub fn print_all_messages( ...@@ -177,23 +177,22 @@ pub fn print_all_messages(
emit_json: bool, emit_json: bool,
) { ) {
for log in logs { for log in logs {
println!("{}", log); println!("{log}");
} }
let output: String = if emit_json { let output: String = if emit_json {
serde_json::to_string_pretty(&cwes).unwrap() serde_json::to_string_pretty(&cwes).unwrap()
} else { } else {
cwes.iter() cwes.iter()
.map(|cwe| format!("{}", cwe)) .map(|cwe| format!("{cwe}"))
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\n") .join("\n")
+ "\n" + "\n"
}; };
if let Some(file_path) = out_path { if let Some(file_path) = out_path {
std::fs::write(file_path, output).unwrap_or_else(|error| { std::fs::write(file_path, output)
panic!("Writing to output path {} failed: {}", file_path, error) .unwrap_or_else(|error| panic!("Writing to output path {file_path} failed: {error}"));
});
} else { } else {
print!("{}", output); print!("{output}",);
} }
} }
...@@ -215,7 +214,7 @@ pub fn add_debug_log_statistics(all_logs: &mut Vec<LogMessage>) { ...@@ -215,7 +214,7 @@ pub fn add_debug_log_statistics(all_logs: &mut Vec<LogMessage>) {
} }
for (analysis, count) in analysis_debug_log_count { for (analysis, count) in analysis_debug_log_count {
all_logs.push(LogMessage { all_logs.push(LogMessage {
text: format!("Logged {} debug log messages.", count), text: format!("Logged {count} debug log messages."),
level: LogLevel::Info, level: LogLevel::Info,
location: None, location: None,
source: Some(analysis), source: Some(analysis),
...@@ -223,10 +222,7 @@ pub fn add_debug_log_statistics(all_logs: &mut Vec<LogMessage>) { ...@@ -223,10 +222,7 @@ pub fn add_debug_log_statistics(all_logs: &mut Vec<LogMessage>) {
} }
if general_debug_log_count > 0 { if general_debug_log_count > 0 {
all_logs.push(LogMessage { all_logs.push(LogMessage {
text: format!( text: format!("Logged {general_debug_log_count} general debug log messages."),
"Logged {} general debug log messages.",
general_debug_log_count
),
level: LogLevel::Info, level: LogLevel::Info,
location: None, location: None,
source: None, source: None,
......
...@@ -70,8 +70,7 @@ impl CweTestCase { ...@@ -70,8 +70,7 @@ impl CweTestCase {
} else { } else {
println!("{} \t {}", filepath, "[FAILED]".red()); println!("{} \t {}", filepath, "[FAILED]".red());
Err(format!( Err(format!(
"Expected occurrences: {}. Found: {}", "Expected occurrences: {num_expected_occurences}. Found: {num_cwes}"
num_expected_occurences, num_cwes
)) ))
} }
} else { } else {
...@@ -79,7 +78,7 @@ impl CweTestCase { ...@@ -79,7 +78,7 @@ impl CweTestCase {
match output.status.code() { match output.status.code() {
Some(_code) => Err(String::from_utf8(output.stdout).unwrap() Some(_code) => Err(String::from_utf8(output.stdout).unwrap()
+ &String::from_utf8(output.stderr).unwrap()), + &String::from_utf8(output.stderr).unwrap()),
None => Err(format!("Execution failed for file {}", filepath)), None => Err(format!("Execution failed for file {filepath}")),
} }
} }
} }
...@@ -160,8 +159,8 @@ pub fn all_test_cases(cwe: &'static str, check_name: &'static str) -> Vec<CweTes ...@@ -160,8 +159,8 @@ pub fn all_test_cases(cwe: &'static str, check_name: &'static str) -> Vec<CweTes
/// The `error_log` tuples are of the form `(check_filename, error_message)`. /// The `error_log` tuples are of the form `(check_filename, error_message)`.
pub fn print_errors(error_log: Vec<(String, String)>) { pub fn print_errors(error_log: Vec<(String, String)>) {
for (filepath, error) in error_log { for (filepath, error) in error_log {
println!("{}", format!("+++ Error for {} +++", filepath).red()); println!("{}", format!("+++ Error for {filepath} +++").red());
println!("{}", error); println!("{error}");
} }
} }
......
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