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"
derive_more = "0.99"
directories = "3.0"
goblin = "0.2"
gcd = "2.0"
[lib]
name = "cwe_checker_lib"
......@@ -159,7 +159,7 @@ impl TryToInterval for BitvectorDomain {
/// If the domain represents an absolute value, return it as an interval of length one.
fn try_to_interval(&self) -> Result<Interval, Error> {
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")),
}
}
......
......@@ -5,9 +5,7 @@ impl IntervalDomain {
/// if one adds a value from `self` to a value from `rhs`.
pub fn add(&self, rhs: &Self) -> Self {
let mut interval: IntervalDomain = self.interval.add(&rhs.interval).into();
if interval.is_top() {
interval
} else {
if !interval.is_top() {
interval.widening_delay = std::cmp::max(self.widening_delay, rhs.widening_delay);
interval.update_widening_lower_bound(
&self
......@@ -31,17 +29,15 @@ impl IntervalDomain {
.as_ref()
.and_then(|bound| bound.signed_add_overflow_checked(&self.interval.end)),
);
interval
}
interval
}
/// Compute the interval of possible results
/// if one subtracts a value in `rhs` from a value in `self`.
pub fn sub(&self, rhs: &Self) -> Self {
let mut interval: IntervalDomain = self.interval.sub(&rhs.interval).into();
if interval.is_top() {
interval
} else {
if !interval.is_top() {
interval.widening_delay = std::cmp::max(self.widening_delay, rhs.widening_delay);
interval.update_widening_lower_bound(
&self
......@@ -65,8 +61,8 @@ impl IntervalDomain {
.as_ref()
.and_then(|bound| self.interval.end.signed_sub_overflow_checked(bound)),
);
interval
}
interval
}
/// 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 {
/// Set the widening delay to the interval length
/// to simulate that `self` was just widened.
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
}
}
......@@ -62,7 +72,7 @@ fn signed_merge() {
let b = IntervalDomain::mock_with_bounds(None, 3, 3, Some(5));
assert_eq!(
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 b = IntervalDomain::mock_with_bounds(None, -1, -1, Some(5));
......@@ -110,7 +120,7 @@ fn signed_merge() {
let update = IntervalDomain::mock_with_bounds(Some(-3), -2, 3, None);
var = var.merge(&update);
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);
}
......@@ -411,6 +421,14 @@ fn add_signed_bounds() {
.clone()
.add_signed_less_equal_bound(&Bitvector::from_i64(-20));
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
let x = interval
......@@ -431,6 +449,14 @@ fn add_signed_bounds() {
x.unwrap(),
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]
......@@ -588,3 +614,44 @@ fn float_nan_bytesize() {
assert!(result.is_top());
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 {
object.merge_value(new_data(23), &bv(-12));
assert_eq!(
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();
......
......@@ -536,7 +536,7 @@ mod tests {
merged
.get_value(&Data::Pointer(pointer.clone()), ByteSize::new(8))
.unwrap(),
Data::Value(ValueDomain::new_top(ByteSize::new(8)))
Data::Value(IntervalDomain::mock(3, 42).with_stride(39))
);
assert_eq!(
merged
......
......@@ -10,6 +10,12 @@ pub type Bitvector = apint::ApInt;
/// A trait to extend the bitvector type with useful helper functions
/// that are not contained in the [`apint`] crate.
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.
/// Returns an error for non-implemented cast operations (currently all float-related casts).
fn cast(&self, kind: CastOpType, width: ByteSize) -> Result<Self, Error>;
......@@ -269,6 +275,24 @@ impl BitvectorExtended for Bitvector {
fn bytesize(&self) -> ByteSize {
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)]
......
......@@ -834,12 +834,11 @@ impl Project {
for sub in self.program.term.subs.iter_mut() {
if !sub.term.blocks.is_empty()
&& sub.tid.address != sub.term.blocks[0].tid.address
&& sub
&& !sub
.term
.blocks
.iter()
.find(|block| block.tid.address == sub.tid.address)
.is_none()
.any(|block| block.tid.address == sub.tid.address)
{
log_messages.push(LogMessage::new_error(format!(
"Starting block of function {} ({}) not found.",
......
......@@ -30,11 +30,7 @@ pub fn get_calls_to_symbols<'a, 'b>(
for jmp in blk.term.jmps.iter() {
if let Jmp::Call { target: dst, .. } = &jmp.term {
if symbols.contains_key(dst) {
calls.push((
sub.term.name.as_str(),
&jmp.tid,
symbols.get(dst).clone().unwrap(),
));
calls.push((sub.term.name.as_str(), &jmp.tid, symbols.get(dst).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