Unverified Commit 80455c63 by Enkelmann Committed by GitHub

add strides to the interval domain (#189)

parent 1c369052
...@@ -17,6 +17,7 @@ crossbeam-channel = "0.4" ...@@ -17,6 +17,7 @@ crossbeam-channel = "0.4"
derive_more = "0.99" derive_more = "0.99"
directories = "3.0" directories = "3.0"
goblin = "0.2" goblin = "0.2"
gcd = "2.0"
[lib] [lib]
name = "cwe_checker_lib" name = "cwe_checker_lib"
...@@ -159,7 +159,7 @@ impl TryToInterval for BitvectorDomain { ...@@ -159,7 +159,7 @@ impl TryToInterval for BitvectorDomain {
/// If the domain represents an absolute value, return it as an interval of length one. /// If the domain represents an absolute value, return it as an interval of length one.
fn try_to_interval(&self) -> Result<Interval, Error> { fn try_to_interval(&self) -> Result<Interval, Error> {
match self { match self {
BitvectorDomain::Value(val) => Ok(Interval::new(val.clone(), val.clone())), BitvectorDomain::Value(val) => Ok(val.clone().into()),
BitvectorDomain::Top(_) => Err(anyhow!("Value is Top")), BitvectorDomain::Top(_) => Err(anyhow!("Value is Top")),
} }
} }
......
...@@ -5,9 +5,7 @@ impl IntervalDomain { ...@@ -5,9 +5,7 @@ impl IntervalDomain {
/// if one adds a value from `self` to a value from `rhs`. /// if one adds a value from `self` to a value from `rhs`.
pub fn add(&self, rhs: &Self) -> Self { pub fn add(&self, rhs: &Self) -> Self {
let mut interval: IntervalDomain = self.interval.add(&rhs.interval).into(); let mut interval: IntervalDomain = self.interval.add(&rhs.interval).into();
if interval.is_top() { if !interval.is_top() {
interval
} else {
interval.widening_delay = std::cmp::max(self.widening_delay, rhs.widening_delay); interval.widening_delay = std::cmp::max(self.widening_delay, rhs.widening_delay);
interval.update_widening_lower_bound( interval.update_widening_lower_bound(
&self &self
...@@ -31,17 +29,15 @@ impl IntervalDomain { ...@@ -31,17 +29,15 @@ impl IntervalDomain {
.as_ref() .as_ref()
.and_then(|bound| bound.signed_add_overflow_checked(&self.interval.end)), .and_then(|bound| bound.signed_add_overflow_checked(&self.interval.end)),
); );
interval
} }
interval
} }
/// Compute the interval of possible results /// Compute the interval of possible results
/// if one subtracts a value in `rhs` from a value in `self`. /// if one subtracts a value in `rhs` from a value in `self`.
pub fn sub(&self, rhs: &Self) -> Self { pub fn sub(&self, rhs: &Self) -> Self {
let mut interval: IntervalDomain = self.interval.sub(&rhs.interval).into(); let mut interval: IntervalDomain = self.interval.sub(&rhs.interval).into();
if interval.is_top() { if !interval.is_top() {
interval
} else {
interval.widening_delay = std::cmp::max(self.widening_delay, rhs.widening_delay); interval.widening_delay = std::cmp::max(self.widening_delay, rhs.widening_delay);
interval.update_widening_lower_bound( interval.update_widening_lower_bound(
&self &self
...@@ -65,8 +61,8 @@ impl IntervalDomain { ...@@ -65,8 +61,8 @@ impl IntervalDomain {
.as_ref() .as_ref()
.and_then(|bound| self.interval.end.signed_sub_overflow_checked(bound)), .and_then(|bound| self.interval.end.signed_sub_overflow_checked(bound)),
); );
interval
} }
interval
} }
/// Compute the interval of possible results /// Compute the interval of possible results
......
use super::*;
impl Interval {
pub fn mock(start: i64, end: i64) -> Interval {
Interval::new(Bitvector::from_i64(start), Bitvector::from_i64(end), 1)
}
pub fn mock_i8(start: i8, end: i8) -> Interval {
Interval::new(Bitvector::from_i8(start), Bitvector::from_i8(end), 1)
}
pub fn with_stride(mut self, stride: u64) -> Interval {
self.stride = stride;
self
}
}
#[test]
fn signed_merge() {
// Strides and Merge
let a = Interval::mock(1, 13).with_stride(6);
let b = Interval::mock(4, 10).with_stride(3);
assert_eq!(a.signed_merge(&b), Interval::mock(1, 13).with_stride(3));
let a = Interval::mock(1, 13).with_stride(6);
let b = Interval::mock(3, 9).with_stride(3);
assert_eq!(a.signed_merge(&b), Interval::mock(1, 13).with_stride(1));
let a = Interval::mock(0, 24).with_stride(12);
let b = Interval::mock(2, 42).with_stride(4);
assert_eq!(a.signed_merge(&b), Interval::mock(0, 42).with_stride(2));
}
#[test]
fn adjust_start_or_end_to_value_in_stride() {
let mut val = Interval::mock(-3, 10).with_stride(7);
val.adjust_start_to_value_in_stride();
assert_eq!(val, Interval::mock(3, 10).with_stride(7));
let mut val = Interval::mock(-3, 10).with_stride(7);
val.adjust_end_to_value_in_stride();
assert_eq!(val, Interval::mock(-3, 4).with_stride(7));
let mut val = Interval::mock(-3, 2).with_stride(7);
val.adjust_start_to_value_in_stride();
assert_eq!(val, Interval::mock(2, 2).with_stride(0));
let mut val = Interval::mock(-3, 2).with_stride(7);
val.adjust_end_to_value_in_stride();
assert_eq!(val, Interval::mock(-3, -3).with_stride(0));
}
#[test]
fn adjust_to_stride_and_remainder() {
let val = Interval::mock(-17, 23).with_stride(5);
assert_eq!(
val.adjust_to_stride_and_remainder(4, 2).unwrap(),
Interval::mock(-14, 22).with_stride(4)
);
let val = Interval::mock(7, 24);
assert!(val.adjust_to_stride_and_remainder(50, 5).is_err());
let val = Interval::mock(5, 5).with_stride(1);
assert_eq!(
val.adjust_to_stride_and_remainder(1, 0).unwrap(),
Interval::mock(5, 5)
);
}
#[test]
fn zero_extend() {
// Interval with only non-negative values
let val = Interval::mock_i8(11, 51).with_stride(10);
assert_eq!(
val.zero_extend(ByteSize::new(8)),
Interval::mock(11, 51).with_stride(10)
);
// Interval with only negative values
let val = Interval::mock_i8(-50, -10).with_stride(10);
assert_eq!(
val.zero_extend(ByteSize::new(8)),
Interval::mock(206, 246).with_stride(10)
);
// Interval with both positive and negative values
let val = Interval::mock_i8(-3, 21).with_stride(12);
assert_eq!(val.clone().zero_extend(ByteSize::new(1)), val);
assert_eq!(
val.zero_extend(ByteSize::new(8)),
Interval::mock(1, 253).with_stride(4)
);
}
#[test]
fn subpiece_higher() {
let val = Interval::mock(3, 21).with_stride(6);
assert_eq!(
val.subpiece_higher(ByteSize::new(7)),
Interval::from(Bitvector::from_i8(0))
)
}
#[test]
fn subpiece_lower() {
let val = Interval::mock(-15, 25).with_stride(10);
assert_eq!(
val.subpiece_lower(ByteSize::new(1)),
Interval::mock_i8(-15, 25).with_stride(10)
);
let val = Interval::mock(-256, 25).with_stride(10);
assert_eq!(
val.subpiece_lower(ByteSize::new(1)),
Interval::new_top(ByteSize::new(1))
);
}
#[test]
fn piece() {
let left = Interval::mock_i8(1, 4).with_stride(3);
let right = Interval::mock_i8(-2, 2).with_stride(2);
assert_eq!(
left.piece(&right),
Interval {
start: Bitvector::from_i16(256),
end: Bitvector::from_i16(1278),
stride: 2,
}
);
let left = Interval::mock_i8(1, 4).with_stride(3);
let right = Interval::mock_i8(3, 15).with_stride(6);
assert_eq!(
left.piece(&right),
Interval {
start: Bitvector::from_i16(259),
end: Bitvector::from_i16(1039),
stride: 2,
}
);
}
#[test]
fn add_and_sub() {
let left = Interval::mock(3, 15).with_stride(12);
let right = Interval::mock(-2, 18).with_stride(10);
assert_eq!(left.add(&right), Interval::mock(1, 33).with_stride(2));
assert_eq!(left.sub(&right), Interval::mock(-15, 17).with_stride(2));
}
#[test]
fn contains() {
let interval = Interval::mock(2, 10).with_stride(4);
let elem = Bitvector::from_i64(4);
assert!(!interval.contains(&elem));
let elem = Bitvector::from_i64(6);
assert!(interval.contains(&elem));
let elem = Bitvector::from_i64(14);
assert!(!interval.contains(&elem));
}
#[test]
fn test_extended_gcd() {
assert_eq!(extended_gcd(48, 21), (3, -3, 7));
}
#[test]
fn test_compute_intersection_residue_class() {
let left = Interval::mock(21, 421).with_stride(20);
let right = Interval::mock(11, 191).with_stride(15);
assert_eq!(
compute_intersection_residue_class(&left, &right).unwrap(),
Some((60, 41))
);
let left = Interval::mock(21, 421).with_stride(20);
let right = Interval::mock(14, 194).with_stride(15);
assert_eq!(
compute_intersection_residue_class(&left, &right).unwrap(),
None
);
let left = Interval::mock(0, 2 << 60).with_stride(2 << 60);
let right = Interval::mock(2, (2 << 60) + 1).with_stride((2 << 60) - 1);
assert!(compute_intersection_residue_class(&left, &right).is_err());
let left = Interval::mock(3, 3);
let right = Interval::mock(0, 15).with_stride(3);
assert_eq!(
compute_intersection_residue_class(&left, &right).unwrap(),
Some((3, 0))
);
let left = Interval::mock(3, 23).with_stride(5);
let right = Interval::mock(8, 8);
assert_eq!(
compute_intersection_residue_class(&left, &right).unwrap(),
Some((5, 3))
);
let left = Interval::mock(3, 3);
let right = Interval::mock(0, 15).with_stride(5);
assert_eq!(
compute_intersection_residue_class(&left, &right).unwrap(),
None
);
}
#[test]
fn signed_intersect() {
let left = Interval::mock(21, 421).with_stride(20);
let right = Interval::mock(11, 191).with_stride(15);
assert_eq!(
left.signed_intersect(&right).unwrap(),
Interval::mock(41, 161).with_stride(60)
);
let left = Interval::mock(21, 421).with_stride(20);
let right = Interval::mock(14, 194).with_stride(15);
assert!(left.signed_intersect(&right).is_err());
let left = Interval::mock(0, 2 << 60).with_stride(2 << 60);
let right = Interval::mock(2, (2 << 60) + 1).with_stride((2 << 60) - 1);
assert!(left.signed_intersect(&right).is_err());
}
...@@ -38,7 +38,17 @@ impl IntervalDomain { ...@@ -38,7 +38,17 @@ impl IntervalDomain {
/// Set the widening delay to the interval length /// Set the widening delay to the interval length
/// to simulate that `self` was just widened. /// to simulate that `self` was just widened.
pub fn as_freshly_widened(mut self) -> Self { pub fn as_freshly_widened(mut self) -> Self {
self.widening_delay = self.interval.length().try_to_u64().unwrap(); self.widening_delay = (self.interval.end.clone() - &self.interval.start)
.try_to_u64()
.unwrap();
self
}
/// Set the stride of the interval.
/// also rounds down the interval end to be contained in the same residue class as the interval start.
pub fn with_stride(mut self, stride: u64) -> Self {
self.interval.stride = stride;
self.interval.adjust_end_to_value_in_stride();
self self
} }
} }
...@@ -62,7 +72,7 @@ fn signed_merge() { ...@@ -62,7 +72,7 @@ fn signed_merge() {
let b = IntervalDomain::mock_with_bounds(None, 3, 3, Some(5)); let b = IntervalDomain::mock_with_bounds(None, 3, 3, Some(5));
assert_eq!( assert_eq!(
a.merge(&b), a.merge(&b),
IntervalDomain::mock_with_bounds(None, -3, 5, None).as_freshly_widened() IntervalDomain::mock_with_bounds(Some(-3), 1, 3, Some(5)).with_stride(2)
); );
let a = IntervalDomain::mock_with_bounds(None, 1, 5, None); let a = IntervalDomain::mock_with_bounds(None, 1, 5, None);
let b = IntervalDomain::mock_with_bounds(None, -1, -1, Some(5)); let b = IntervalDomain::mock_with_bounds(None, -1, -1, Some(5));
...@@ -110,7 +120,7 @@ fn signed_merge() { ...@@ -110,7 +120,7 @@ fn signed_merge() {
let update = IntervalDomain::mock_with_bounds(Some(-3), -2, 3, None); let update = IntervalDomain::mock_with_bounds(Some(-3), -2, 3, None);
var = var.merge(&update); var = var.merge(&update);
let mut expected_result = IntervalDomain::mock_with_bounds(None, -3, 3, None); let mut expected_result = IntervalDomain::mock_with_bounds(None, -3, 3, None);
expected_result.widening_delay = 6; expected_result.widening_delay = 5; // The last update does not cause widening
assert_eq!(var, expected_result); assert_eq!(var, expected_result);
} }
...@@ -411,6 +421,14 @@ fn add_signed_bounds() { ...@@ -411,6 +421,14 @@ fn add_signed_bounds() {
.clone() .clone()
.add_signed_less_equal_bound(&Bitvector::from_i64(-20)); .add_signed_less_equal_bound(&Bitvector::from_i64(-20));
assert!(x.is_err()); assert!(x.is_err());
let x = IntervalDomain::mock(0, 10)
.with_stride(10)
.add_signed_less_equal_bound(&Bitvector::from_i64(15));
assert_eq!(x.unwrap(), IntervalDomain::mock(0, 10).with_stride(10));
let x = IntervalDomain::mock(0, 10)
.with_stride(10)
.add_signed_less_equal_bound(&Bitvector::from_i64(5));
assert_eq!(x.unwrap(), IntervalDomain::mock(0, 0));
//signed_greater_equal //signed_greater_equal
let x = interval let x = interval
...@@ -431,6 +449,14 @@ fn add_signed_bounds() { ...@@ -431,6 +449,14 @@ fn add_signed_bounds() {
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)
.with_stride(10)
.add_signed_greater_equal_bound(&Bitvector::from_i64(-5));
assert_eq!(x.unwrap(), IntervalDomain::mock(0, 10).with_stride(10));
let x = IntervalDomain::mock(0, 10)
.with_stride(10)
.add_signed_greater_equal_bound(&Bitvector::from_i64(5));
assert_eq!(x.unwrap(), IntervalDomain::mock(10, 10));
} }
#[test] #[test]
...@@ -588,3 +614,44 @@ fn float_nan_bytesize() { ...@@ -588,3 +614,44 @@ fn float_nan_bytesize() {
assert!(result.is_top()); assert!(result.is_top());
assert_eq!(result.bytesize(), ByteSize::new(1)); assert_eq!(result.bytesize(), ByteSize::new(1));
} }
#[test]
fn stride_rounding() {
let interval = Interval::mock(3, 13).with_stride(10);
assert_eq!(
Bitvector::from_i64(5)
.round_up_to_stride_of(&interval)
.unwrap(),
Bitvector::from_i64(13)
);
assert_eq!(
Bitvector::from_i64(5)
.round_down_to_stride_of(&interval)
.unwrap(),
Bitvector::from_i64(3)
);
assert_eq!(
Bitvector::from_i64(-277)
.round_up_to_stride_of(&interval)
.unwrap(),
Bitvector::from_i64(-277)
);
assert_eq!(
Bitvector::from_i64(-277)
.round_down_to_stride_of(&interval)
.unwrap(),
Bitvector::from_i64(-277)
);
let interval = Interval::mock_i8(100, 110).with_stride(10);
assert_eq!(
Bitvector::from_i8(-123)
.round_up_to_stride_of(&interval)
.unwrap(),
Bitvector::from_i8(-120)
);
assert_eq!(
Bitvector::from_i8(-123).round_down_to_stride_of(&interval),
None
);
}
...@@ -461,7 +461,7 @@ mod tests { ...@@ -461,7 +461,7 @@ mod tests {
object.merge_value(new_data(23), &bv(-12)); object.merge_value(new_data(23), &bv(-12));
assert_eq!( assert_eq!(
object.get_value(Bitvector::from_i64(-12), ByteSize::new(8)), object.get_value(Bitvector::from_i64(-12), ByteSize::new(8)),
Data::Value(ValueDomain::new_top(ByteSize::new(8))) Data::Value(IntervalDomain::mock(4, 23).with_stride(19))
); );
let mut other_object = new_abstract_object(); let mut other_object = new_abstract_object();
......
...@@ -536,7 +536,7 @@ mod tests { ...@@ -536,7 +536,7 @@ mod tests {
merged merged
.get_value(&Data::Pointer(pointer.clone()), ByteSize::new(8)) .get_value(&Data::Pointer(pointer.clone()), ByteSize::new(8))
.unwrap(), .unwrap(),
Data::Value(ValueDomain::new_top(ByteSize::new(8))) Data::Value(IntervalDomain::mock(3, 42).with_stride(39))
); );
assert_eq!( assert_eq!(
merged merged
......
...@@ -10,6 +10,12 @@ pub type Bitvector = apint::ApInt; ...@@ -10,6 +10,12 @@ pub type Bitvector = apint::ApInt;
/// A trait to extend the bitvector type with useful helper functions /// A trait to extend the bitvector type with useful helper functions
/// that are not contained in the [`apint`] crate. /// that are not contained in the [`apint`] crate.
pub trait BitvectorExtended: Sized { pub trait BitvectorExtended: Sized {
/// Resize `self` to the target byte size by either zero extending or truncating `self`.
fn into_resize_unsigned(self, size: ByteSize) -> Self;
/// Resize `self` to the target byte size by either sign extending or truncating `self`.
fn into_resize_signed(self, size: ByteSize) -> Self;
/// Perform a cast operation on the bitvector. /// Perform a cast operation on the bitvector.
/// Returns an error for non-implemented cast operations (currently all float-related casts). /// Returns an error for non-implemented cast operations (currently all float-related casts).
fn cast(&self, kind: CastOpType, width: ByteSize) -> Result<Self, Error>; fn cast(&self, kind: CastOpType, width: ByteSize) -> Result<Self, Error>;
...@@ -269,6 +275,24 @@ impl BitvectorExtended for Bitvector { ...@@ -269,6 +275,24 @@ impl BitvectorExtended for Bitvector {
fn bytesize(&self) -> ByteSize { fn bytesize(&self) -> ByteSize {
self.width().into() self.width().into()
} }
/// Resize `self` to the target byte size by either zero extending or truncating `self`.
fn into_resize_unsigned(self, size: ByteSize) -> Self {
if self.width() < size.into() {
self.into_zero_extend(size).unwrap()
} else {
self.into_truncate(size).unwrap()
}
}
/// Resize `self` to the target byte size by either sign extending or truncating `self`.
fn into_resize_signed(self, size: ByteSize) -> Self {
if self.width() < size.into() {
self.into_sign_extend(size).unwrap()
} else {
self.into_truncate(size).unwrap()
}
}
} }
#[cfg(test)] #[cfg(test)]
......
...@@ -834,12 +834,11 @@ impl Project { ...@@ -834,12 +834,11 @@ impl Project {
for sub in self.program.term.subs.iter_mut() { for sub in self.program.term.subs.iter_mut() {
if !sub.term.blocks.is_empty() if !sub.term.blocks.is_empty()
&& sub.tid.address != sub.term.blocks[0].tid.address && sub.tid.address != sub.term.blocks[0].tid.address
&& sub && !sub
.term .term
.blocks .blocks
.iter() .iter()
.find(|block| block.tid.address == sub.tid.address) .any(|block| block.tid.address == sub.tid.address)
.is_none()
{ {
log_messages.push(LogMessage::new_error(format!( log_messages.push(LogMessage::new_error(format!(
"Starting block of function {} ({}) not found.", "Starting block of function {} ({}) not found.",
......
...@@ -30,11 +30,7 @@ pub fn get_calls_to_symbols<'a, 'b>( ...@@ -30,11 +30,7 @@ pub fn get_calls_to_symbols<'a, 'b>(
for jmp in blk.term.jmps.iter() { for jmp in blk.term.jmps.iter() {
if let Jmp::Call { target: dst, .. } = &jmp.term { if let Jmp::Call { target: dst, .. } = &jmp.term {
if symbols.contains_key(dst) { if symbols.contains_key(dst) {
calls.push(( calls.push((sub.term.name.as_str(), &jmp.tid, symbols.get(dst).unwrap()));
sub.term.name.as_str(),
&jmp.tid,
symbols.get(dst).clone().unwrap(),
));
} }
} }
} }
......
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