Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
cwe_checker
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
fact-depend
cwe_checker
Commits
76936d8c
Commit
76936d8c
authored
Aug 10, 2021
by
Enkelmann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor MemRegion for usage with new DataDomain (#211)
parent
a20a5379
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
440 additions
and
222 deletions
+440
-222
mem_region.rs
src/cwe_checker_lib/src/abstract_domain/mem_region.rs
+201
-210
tests.rs
src/cwe_checker_lib/src/abstract_domain/mem_region/tests.rs
+224
-0
object.rs
src/cwe_checker_lib/src/analysis/pointer_inference/object.rs
+7
-2
object_list.rs
...checker_lib/src/analysis/pointer_inference/object_list.rs
+4
-4
tests.rs
...checker_lib/src/analysis/pointer_inference/state/tests.rs
+4
-6
No files found.
src/cwe_checker_lib/src/abstract_domain/mem_region.rs
View file @
76936d8c
...
...
@@ -2,10 +2,8 @@ use super::{AbstractDomain, HasTop, SizedDomain};
use
crate
::
intermediate_representation
::
ByteSize
;
use
crate
::
prelude
::
*
;
use
apint
::{
Int
,
Width
};
use
derive_more
::
Deref
;
use
serde
::{
Deserialize
,
Serialize
};
use
std
::
collections
::
BTreeMap
;
use
std
::
ops
::
DerefMut
;
use
std
::
sync
::
Arc
;
/// A memory region is an abstract domain representing a continuous region of memory, e.g. the stack frame of a function.
...
...
@@ -20,15 +18,27 @@ use std::sync::Arc;
/// Thus an empty memory region actually represents the *Top* element of its abstract domain.
///
/// To allow cheap cloning of a `MemRegion`, the actual data is wrapped inside an `Arc`.
#[derive(Serialize,
Deserialize,
Debug,
Hash,
Clone,
PartialEq,
Eq,
Deref)]
#[deref(forward)]
pub
struct
MemRegion
<
T
:
AbstractDomain
+
SizedDomain
+
HasTop
+
std
::
fmt
::
Debug
>
(
Arc
<
MemRegionData
<
T
>>
,
);
impl
<
T
:
AbstractDomain
+
SizedDomain
+
HasTop
+
std
::
fmt
::
Debug
>
DerefMut
for
MemRegion
<
T
>
{
fn
deref_mut
(
&
mut
self
)
->
&
mut
MemRegionData
<
T
>
{
Arc
::
make_mut
(
&
mut
self
.
0
)
#[derive(Serialize,
Deserialize,
Debug,
Hash,
Clone,
PartialEq,
Eq)]
pub
struct
MemRegion
<
T
:
AbstractDomain
+
SizedDomain
+
HasTop
+
std
::
fmt
::
Debug
>
{
inner
:
Arc
<
Inner
<
T
>>
,
}
/// The internal data of a memory region. See the description of `MemRegion` for more.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone)]
struct
Inner
<
T
:
AbstractDomain
+
SizedDomain
+
HasTop
+
std
::
fmt
::
Debug
>
{
address_bytesize
:
ByteSize
,
values
:
BTreeMap
<
i64
,
T
>
,
}
#[allow(clippy
::
from_over_into)]
impl
<
T
:
AbstractDomain
+
SizedDomain
+
HasTop
+
std
::
fmt
::
Debug
>
std
::
convert
::
Into
<
MemRegion
<
T
>>
for
Inner
<
T
>
{
/// Wrap the contents of a `MemRegion` into an `Arc<..>`.
fn
into
(
self
)
->
MemRegion
<
T
>
{
MemRegion
{
inner
:
Arc
::
new
(
self
),
}
}
}
...
...
@@ -39,13 +49,13 @@ impl<T: AbstractDomain + SizedDomain + HasTop + std::fmt::Debug> AbstractDomain
if
self
==
other
{
self
.clone
()
}
else
{
MemRegion
(
Arc
::
new
(
self
.
0
.merge
(
&
other
.
0
))
)
self
.merge_inner
(
other
)
}
}
/// The *Top* element is represented by an empty memory region.
fn
is_top
(
&
self
)
->
bool
{
self
.values
.is_empty
()
self
.
inner.
values
.is_empty
()
}
}
...
...
@@ -58,58 +68,46 @@ impl<T: AbstractDomain + SizedDomain + HasTop + std::fmt::Debug> HasTop for MemR
impl
<
T
:
AbstractDomain
+
SizedDomain
+
HasTop
+
std
::
fmt
::
Debug
>
MemRegion
<
T
>
{
/// Create a new, empty memory region.
pub
fn
new
(
address_bytesize
:
ByteSize
)
->
Self
{
MemRegion
(
Arc
::
new
(
MemRegionData
::
new
(
address_bytesize
)))
}
}
/// The internal data of a memory region. See the description of `MemRegion` for more.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone)]
pub
struct
MemRegionData
<
T
:
AbstractDomain
+
SizedDomain
+
HasTop
+
std
::
fmt
::
Debug
>
{
address_bytesize
:
ByteSize
,
values
:
BTreeMap
<
i64
,
T
>
,
}
impl
<
T
:
AbstractDomain
+
SizedDomain
+
HasTop
+
std
::
fmt
::
Debug
>
MemRegionData
<
T
>
{
/// create a new, empty MemRegion
pub
fn
new
(
address_bytesize
:
ByteSize
)
->
MemRegionData
<
T
>
{
MemRegionData
{
pub
fn
new
(
address_bytesize
:
ByteSize
)
->
MemRegion
<
T
>
{
Inner
{
address_bytesize
,
values
:
BTreeMap
::
new
(),
}
.into
()
}
/// Get the b
it
size of pointers for the address space that the memory region belongs to.
/// Get the b
yte
size of pointers for the address space that the memory region belongs to.
pub
fn
get_address_bytesize
(
&
self
)
->
ByteSize
{
self
.address_bytesize
self
.
inner.
address_bytesize
}
/// Remove all elements intersecting the provided interval.
/// This function does not sanitize its inputs.
fn
clear_interval
(
&
mut
self
,
position
:
i64
,
size
:
i64
)
{
let
inner
=
Arc
::
make_mut
(
&
mut
self
.inner
);
// If the previous element intersects the range, remove it
if
let
Some
((
prev_pos
,
prev_size
))
=
self
if
let
Some
((
prev_pos
,
prev_size
))
=
inner
.values
.range
(
..
position
)
.map
(|(
pos
,
elem
)|
(
*
pos
,
u64
::
from
(
elem
.bytesize
())
as
i64
))
.last
()
{
if
prev_pos
+
prev_size
>
position
{
self
.values
.remove
(
&
prev_pos
);
inner
.values
.remove
(
&
prev_pos
);
}
}
// remove all other intersecting elements
let
intersecting_elements
:
Vec
<
i64
>
=
self
let
intersecting_elements
:
Vec
<
i64
>
=
inner
.values
.range
(
position
..
(
position
+
size
))
.map
(|(
pos
,
_elem
)|
*
pos
)
.collect
();
for
index
in
intersecting_elements
{
self
.values
.remove
(
&
index
);
inner
.values
.remove
(
&
index
);
}
}
/// Clear all values that might be overwritten if one writes a value with byte size `value_size`
/// Clear all values that might be
fully or partially
overwritten if one writes a value with byte size `value_size`
/// to an offset contained in the interval from `start` to `end` (both bounds included in the interval).
///
/// This represents the effect of writing an arbitrary value (with known byte size)
...
...
@@ -121,7 +119,10 @@ impl<T: AbstractDomain + SizedDomain + HasTop + std::fmt::Debug> MemRegionData<T
/// Add a value to the memory region.
pub
fn
add
(
&
mut
self
,
value
:
T
,
position
:
Bitvector
)
{
assert_eq!
(
ByteSize
::
from
(
position
.width
()),
self
.address_bytesize
);
assert_eq!
(
ByteSize
::
from
(
position
.width
()),
self
.inner.address_bytesize
);
let
position
=
Int
::
from
(
position
)
.try_to_i64
()
.unwrap
();
self
.insert_at_byte_index
(
value
,
position
);
}
...
...
@@ -135,17 +136,22 @@ impl<T: AbstractDomain + SizedDomain + HasTop + std::fmt::Debug> MemRegionData<T
self
.clear_interval
(
position
,
size_in_bytes
);
if
!
value
.is_top
()
{
// top()-values do not need to be explicitly saved, as they don't contain any information anyway.
self
.values
.insert
(
position
,
value
);
Arc
::
make_mut
(
&
mut
self
.inner
)
.values
.insert
(
position
,
value
);
}
}
/// Get the value at the given position.
/// If there is no value at the position or the size of the element is not the same as the provided size, return `T::new_top()`.
pub
fn
get
(
&
self
,
position
:
Bitvector
,
size_in_bytes
:
ByteSize
)
->
T
{
assert_eq!
(
ByteSize
::
from
(
position
.width
()),
self
.address_bytesize
);
assert_eq!
(
ByteSize
::
from
(
position
.width
()),
self
.inner.address_bytesize
);
let
position
=
Int
::
from
(
position
)
.try_to_i64
()
.unwrap
();
if
let
Some
(
elem
)
=
self
.values
.get
(
&
position
)
{
if
let
Some
(
elem
)
=
self
.
inner.
values
.get
(
&
position
)
{
if
elem
.bytesize
()
==
size_in_bytes
{
return
elem
.clone
();
}
...
...
@@ -156,15 +162,21 @@ impl<T: AbstractDomain + SizedDomain + HasTop + std::fmt::Debug> MemRegionData<T
/// Get the value at the given position regardless of the value size.
/// Return `None` if there is no value at that position in the memory region.
pub
fn
get_unsized
(
&
self
,
position
:
Bitvector
)
->
Option
<
T
>
{
assert_eq!
(
ByteSize
::
from
(
position
.width
()),
self
.address_bytesize
);
assert_eq!
(
ByteSize
::
from
(
position
.width
()),
self
.inner.address_bytesize
);
let
position
=
Int
::
from
(
position
)
.try_to_i64
()
.unwrap
();
self
.values
.get
(
&
position
)
.cloned
()
self
.
inner.
values
.get
(
&
position
)
.cloned
()
}
/// Remove all elements intersecting the provided interval.
pub
fn
remove
(
&
mut
self
,
position
:
Bitvector
,
size_in_bytes
:
Bitvector
)
{
assert_eq!
(
ByteSize
::
from
(
position
.width
()),
self
.address_bytesize
);
assert_eq!
(
ByteSize
::
from
(
position
.width
()),
self
.inner.address_bytesize
);
let
position
=
Int
::
from
(
position
)
.try_to_i64
()
.unwrap
();
let
size
=
Int
::
from
(
size_in_bytes
)
.try_to_i64
()
.unwrap
();
assert
!
(
size
>
0
);
...
...
@@ -172,60 +184,147 @@ impl<T: AbstractDomain + SizedDomain + HasTop + std::fmt::Debug> MemRegionData<T
self
.clear_interval
(
position
,
size
);
}
/// If the `MemRegion` contains an element at the given position and with the given size
/// then merge it with a `Top` element.
/// Else clear all values intersecting the range from `position` to `position + size`.
pub
fn
merge_write_top
(
&
mut
self
,
position
:
Bitvector
,
size
:
ByteSize
)
{
let
position
=
Int
::
from
(
position
)
.try_to_i64
()
.unwrap
();
if
let
Some
(
prev_value
)
=
self
.inner.values
.get
(
&
position
)
{
if
prev_value
.bytesize
()
==
size
{
let
merged_value
=
prev_value
.merge
(
&
prev_value
.top
());
let
inner
=
Arc
::
make_mut
(
&
mut
self
.inner
);
if
merged_value
.is_top
()
{
inner
.values
.remove
(
&
position
);
}
else
{
inner
.values
.insert
(
position
,
merged_value
);
}
return
;
}
}
self
.clear_interval
(
position
,
u64
::
from
(
size
)
as
i64
)
}
/// Emulate a write operation of a value to an unknown offset in the range between `start` and `end`
/// by merging all values in the range with `Top` (as we don't exactly know which values are overwritten).
pub
fn
mark_interval_values_as_top
(
&
mut
self
,
start
:
i64
,
end
:
i64
,
elem_size
:
ByteSize
)
{
self
.merge_values_intersecting_range_with_top
(
start
,
end
+
u64
::
from
(
elem_size
)
as
i64
)
}
/// Merge all values intersecting the given range with `Top`.
/// If `Top` is a maximal element of the value abstract domain,
/// this effectively removes all values intersecting the range.
fn
merge_values_intersecting_range_with_top
(
&
mut
self
,
start
:
i64
,
end
:
i64
)
{
let
inner
=
Arc
::
make_mut
(
&
mut
self
.inner
);
// If the previous element intersects the range, merge it with Top
if
let
Some
((
prev_pos
,
prev_size
))
=
inner
.values
.range
(
..
start
)
.map
(|(
pos
,
elem
)|
(
*
pos
,
u64
::
from
(
elem
.bytesize
())
as
i64
))
.last
()
{
if
prev_pos
+
prev_size
>
start
{
let
value
=
inner
.values
.get
(
&
prev_pos
)
.unwrap
();
let
merged_value
=
value
.merge
(
&
value
.top
());
if
merged_value
.is_top
()
{
inner
.values
.remove
(
&
prev_pos
);
}
else
{
inner
.values
.insert
(
prev_pos
,
merged_value
);
}
}
}
// Merge all other intersecting elements with Top
let
intersecting_elements
:
Vec
<
_
>
=
inner
.values
.range
(
start
..
end
)
.map
(|(
pos
,
elem
)|
(
*
pos
,
elem
.merge
(
&
elem
.top
())))
.collect
();
for
(
index
,
merged_value
)
in
intersecting_elements
{
if
merged_value
.is_top
()
{
inner
.values
.remove
(
&
index
);
}
else
{
inner
.values
.insert
(
index
,
merged_value
);
}
}
}
/// Merge two memory regions.
///
/// Values at the same position and with the same size get merged via their merge function.
/// Other values are *not* added to the merged region, because they could be anything in at least one of the two regions.
pub
fn
merge
(
&
self
,
other
:
&
MemRegionData
<
T
>
)
->
MemRegionData
<
T
>
{
assert_eq!
(
self
.address_bytesize
,
other
.address_bytesize
);
/// Values intersecting other values but with not exactly matching position or size are not added to the merged region.
/// Values that do not intersect a value from the other `MemRegion`
/// are merged with `Top` before adding them.
/// This can only add elements to the merged domain if the `Top` value is not a maximal element of the abstract domain.
fn
merge_inner
(
&
self
,
other
:
&
MemRegion
<
T
>
)
->
MemRegion
<
T
>
{
assert_eq!
(
self
.inner.address_bytesize
,
other
.inner.address_bytesize
);
let
mut
zipped
:
BTreeMap
<
i64
,
(
Option
<&
T
>
,
Option
<&
T
>
)
>
=
BTreeMap
::
new
();
for
(
index
,
elem
)
in
self
.inner.values
.iter
()
{
if
let
Some
(
other_elem
)
=
other
.inner.values
.get
(
index
)
{
zipped
.insert
(
*
index
,
(
Some
(
elem
),
Some
(
other_elem
)));
}
else
{
zipped
.insert
(
*
index
,
(
Some
(
elem
),
None
));
}
}
for
(
index
,
other_elem
)
in
other
.inner.values
.iter
()
{
if
self
.inner.values
.get
(
index
)
.is_none
()
{
zipped
.insert
(
*
index
,
(
None
,
Some
(
other_elem
)));
}
}
let
mut
merged_values
:
BTreeMap
<
i64
,
T
>
=
BTreeMap
::
new
();
// add all elements contained in both memory regions
for
(
pos_left
,
elem_left
)
in
self
.values
.iter
()
{
if
let
Some
((
_pos_right
,
elem_right
))
=
other
.values
.get_key_value
(
pos_left
)
{
if
elem_left
.bytesize
()
==
elem_right
.bytesize
()
{
let
merged_val
=
elem_left
.merge
(
elem_right
);
if
!
merged_val
.is_top
()
{
// we discard top()-values, as they don't contain information
merged_values
.insert
(
*
pos_left
,
merged_val
);
let
mut
merged_range_end
=
i64
::
MIN
;
for
(
index
,
(
left
,
right
))
in
zipped
.iter
()
{
let
elem_range_end
=
compute_range_end
(
*
index
,
*
left
,
*
right
);
if
*
index
>=
merged_range_end
{
// The element does not overlap a previous element
if
let
Some
((
next_index
,
_
))
=
zipped
.range
((
index
+
1
)
..
)
.next
()
{
if
*
next_index
>=
elem_range_end
{
// The element does not overlap a subsequent element
if
let
Some
(
merged
)
=
merge_or_merge_with_top
(
*
left
,
*
right
)
{
merged_values
.insert
(
*
index
,
merged
);
}
}
}
else
if
let
Some
(
merged
)
=
merge_or_merge_with_top
(
*
left
,
*
right
)
{
merged_values
.insert
(
*
index
,
merged
);
}
}
merged_range_end
=
std
::
cmp
::
max
(
merged_range_end
,
elem_range_end
);
}
MemRegionData
{
address_bytesize
:
self
.address_bytesize
,
Inner
{
address_bytesize
:
self
.
inner.
address_bytesize
,
values
:
merged_values
,
}
.into
()
}
/// Get an iterator over all elements together with their offset into the memory region.
pub
fn
iter
(
&
self
)
->
std
::
collections
::
btree_map
::
Iter
<
i64
,
T
>
{
self
.values
.iter
()
self
.
inner.
values
.iter
()
}
/// Get an iterator over all values in the memory region
pub
fn
values
(
&
self
)
->
std
::
collections
::
btree_map
::
Values
<
i64
,
T
>
{
self
.values
.values
()
self
.
inner.
values
.values
()
}
/// Get the map of all elements including their offset into the memory region.
pub
fn
entry_map
(
&
self
)
->
&
BTreeMap
<
i64
,
T
>
{
&
self
.values
&
self
.
inner.
values
}
/// Get an iterator over all values in the memory region for in-place manipulation.
/// Note that one can changes values to *Top* using the iterator.
/// These values should be removed from the memory region using `clear_top_values()`.
pub
fn
values_mut
(
&
mut
self
)
->
std
::
collections
::
btree_map
::
ValuesMut
<
i64
,
T
>
{
self
.values
.values_mut
()
Arc
::
make_mut
(
&
mut
self
.inner
)
.values
.values_mut
()
}
/// Remove all values representing the *Top* element from the internal value store,
/// as these should not be saved in the internal representation.
pub
fn
clear_top_values
(
&
mut
self
)
{
let
indices_to_remove
:
Vec
<
i64
>
=
self
.inner
.values
.iter
()
.filter_map
(
...
...
@@ -238,165 +337,57 @@ impl<T: AbstractDomain + SizedDomain + HasTop + std::fmt::Debug> MemRegionData<T
},
)
.collect
();
let
inner
=
Arc
::
make_mut
(
&
mut
self
.inner
);
for
index
in
indices_to_remove
{
self
.values
.remove
(
&
index
);
inner
.values
.remove
(
&
index
);
}
}
}
#[cfg(test)]
mod
tests
{
use
super
::
*
;
use
crate
::
abstract_domain
::
RegisterDomain
;
use
crate
::
intermediate_representation
::
*
;
#[derive(PartialEq,
Eq,
Clone,
Copy,
Debug,
Hash,
PartialOrd,
Ord)]
struct
MockDomain
(
i64
,
ByteSize
);
impl
AbstractDomain
for
MockDomain
{
fn
merge
(
&
self
,
other
:
&
Self
)
->
Self
{
assert_eq!
(
self
.
1
,
other
.
1
);
if
self
==
other
{
self
.clone
()
}
else
{
self
.top
()
/// Helper function that either merges `left` and `right`
/// or, if one of them is `None`, merges the other with a `Top` value.
/// Furthermore, if `left` and `right` have different byte sizes
/// or the merge operation returns a `Top` value, then `None` is returned.
/// Panics if both `left` and `right` are `None`.
fn
merge_or_merge_with_top
<
T
:
AbstractDomain
+
SizedDomain
>
(
left
:
Option
<&
T
>
,
right
:
Option
<&
T
>
,
)
->
Option
<
T
>
{
match
(
left
,
right
)
{
(
Some
(
elem_left
),
Some
(
elem_right
))
=>
{
if
elem_left
.bytesize
()
==
elem_right
.bytesize
()
{
let
merged
=
elem_left
.merge
(
elem_right
);
if
!
merged
.is_top
()
{
return
Some
(
merged
);
}
}
None
}
fn
is_top
(
&
self
)
->
bool
{
self
==
&
self
.top
()
}
}
impl
SizedDomain
for
MockDomain
{
fn
bytesize
(
&
self
)
->
ByteSize
{
self
.
1
}
fn
new_top
(
bytesize
:
ByteSize
)
->
MockDomain
{
MockDomain
(
0
,
bytesize
)
}
}
impl
HasTop
for
MockDomain
{
fn
top
(
&
self
)
->
Self
{
Self
::
new_top
(
self
.
1
)
}
}
impl
RegisterDomain
for
MockDomain
{
fn
bin_op
(
&
self
,
_op
:
BinOpType
,
_rhs
:
&
Self
)
->
Self
{
Self
::
new_top
(
self
.
1
)
}
fn
un_op
(
&
self
,
_op
:
UnOpType
)
->
Self
{
Self
::
new_top
(
self
.
1
)
}
fn
cast
(
&
self
,
_kind
:
CastOpType
,
width
:
ByteSize
)
->
Self
{
Self
::
new_top
(
width
)
}
fn
subpiece
(
&
self
,
_low_byte
:
ByteSize
,
size
:
ByteSize
)
->
Self
{
Self
::
new_top
(
size
)
(
Some
(
elem
),
None
)
|
(
None
,
Some
(
elem
))
=>
{
let
merged
=
elem
.merge
(
&
T
::
new_top
(
elem
.bytesize
()));
if
!
merged
.is_top
()
{
Some
(
merged
)
}
else
{
None
}
}
(
None
,
None
)
=>
panic!
(),
}
}
fn
mock
(
val
:
i64
,
bytesize
:
impl
Into
<
ByteSize
>
)
->
MockDomain
{
MockDomain
(
val
,
bytesize
.into
())
}
fn
bv
(
val
:
i64
)
->
Bitvector
{
Bitvector
::
from_i64
(
val
)
}
#[test]
fn
mem_region
()
{
let
mut
region
:
MemRegion
<
MockDomain
>
=
MemRegion
::
new
(
ByteSize
::
from
(
8u64
));
region
.add
(
mock
(
5
,
3u64
),
bv
(
5
));
assert_eq!
(
region
.get
(
bv
(
5
),
ByteSize
::
from
(
3u64
)),
mock
(
5
,
3u64
));
region
.add
(
mock
(
7
,
2u64
),
bv
(
8
));
assert_eq!
(
region
.get
(
bv
(
8
),
ByteSize
::
from
(
2u64
)),
mock
(
7
,
2u64
));
assert_eq!
(
region
.get
(
bv
(
5
),
ByteSize
::
from
(
3u64
)),
mock
(
5
,
3u64
));
assert_eq!
(
region
.get
(
bv
(
5
),
ByteSize
::
from
(
2u64
)),
MockDomain
::
new_top
(
ByteSize
::
new
(
2
))
);
region
.add
(
mock
(
9
,
2u64
),
bv
(
6
));
assert_eq!
(
region
.get
(
bv
(
6
),
ByteSize
::
from
(
2u64
)),
mock
(
9
,
2u64
));
assert_eq!
(
region
.get
(
bv
(
5
),
ByteSize
::
from
(
3u64
)),
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!
(
region
.get
(
bv
(
6
),
ByteSize
::
from
(
2u64
)),
MockDomain
::
new_top
(
ByteSize
::
new
(
2
))
);
assert_eq!
(
region
.get
(
bv
(
8
),
ByteSize
::
from
(
2u64
)),
mock
(
7
,
2u64
));
let
mut
other_region
=
MemRegion
::
new
(
ByteSize
::
from
(
8u64
));
other_region
.add
(
mock
(
7
,
2u64
),
bv
(
8
));
assert
!
(
region
!=
other_region
);
let
merged_region
=
region
.merge
(
&
other_region
);
assert_eq!
(
merged_region
.get
(
bv
(
8
),
ByteSize
::
from
(
2u64
)),
mock
(
7
,
2u64
)
);
assert_eq!
(
merged_region
.get
(
bv
(
-
3
),
ByteSize
::
from
(
11u64
)),
MockDomain
::
new_top
(
ByteSize
::
from
(
11u64
))
);
other_region
.add
(
mock
(
9
,
11u64
),
bv
(
-
3
));
assert_eq!
(
region
,
other_region
);
}
#[test]
fn
do_not_save_top_elements
()
{
let
mut
region
:
MemRegionData
<
MockDomain
>
=
MemRegionData
::
new
(
ByteSize
::
from
(
8u64
));
region
.add
(
MockDomain
::
new_top
(
ByteSize
::
from
(
4u64
)),
bv
(
5
));
assert_eq!
(
region
.values
.len
(),
0
);
let
mut
other_region
:
MemRegionData
<
MockDomain
>
=
MemRegionData
::
new
(
ByteSize
::
from
(
8u64
));
region
.add
(
mock
(
5
,
4u64
),
bv
(
5
));
other_region
.add
(
mock
(
7
,
4u64
),
bv
(
5
));
let
merged_region
=
region
.merge
(
&
other_region
);
assert_eq!
(
region
.values
.len
(),
1
);
assert_eq!
(
other_region
.values
.len
(),
1
);
assert_eq!
(
merged_region
.values
.len
(),
0
);
}
#[test]
fn
value_removals
()
{
let
mut
region
:
MemRegionData
<
MockDomain
>
=
MemRegionData
::
new
(
ByteSize
::
from
(
8u64
));
region
.add
(
mock
(
1
,
8u64
),
bv
(
0
));
region
.add
(
mock
(
2
,
8u64
),
bv
(
8
));
region
.add
(
mock
(
3
,
8u64
),
bv
(
16
));
region
.add
(
mock
(
4
,
8u64
),
bv
(
24
));
region
.add
(
mock
(
5
,
8u64
),
bv
(
32
));
assert_eq!
(
region
.values
.len
(),
5
);
region
.remove
(
bv
(
2
),
bv
(
3
));
assert_eq!
(
region
.values
.len
(),
4
);
region
.remove
(
bv
(
7
),
bv
(
1
));
assert_eq!
(
region
.values
.len
(),
4
);
region
.remove
(
bv
(
7
),
bv
(
2
));
assert_eq!
(
region
.values
.len
(),
3
);
region
.clear_interval
(
15
,
1
);
assert_eq!
(
region
.values
.len
(),
3
);
region
.clear_interval
(
15
,
3
);
assert_eq!
(
region
.values
.len
(),
2
);
for
val
in
region
.values_mut
()
{
if
*
val
==
mock
(
5
,
8u64
)
{
*
val
=
mock
(
0
,
8u64
);
// This is a *Top* element
}
/// Helper function computing `index` plus the maximum of the bytesizes of `left` and `right`.
/// Panics if both `left` and `right` are `None`.
fn
compute_range_end
<
T
:
SizedDomain
>
(
index
:
i64
,
left
:
Option
<&
T
>
,
right
:
Option
<&
T
>
)
->
i64
{
match
(
left
,
right
)
{
(
Some
(
left_elem
),
Some
(
right_elem
))
=>
{
let
left_size
=
u64
::
from
(
left_elem
.bytesize
())
as
i64
;
let
right_size
=
u64
::
from
(
right_elem
.bytesize
())
as
i64
;
index
+
std
::
cmp
::
max
(
left_size
,
right_size
)
}
region
.clear_top_values
();
assert_eq!
(
region
.values
.len
(),
1
);
assert_eq!
(
region
.get
(
bv
(
24
),
ByteSize
::
from
(
8u64
)),
mock
(
4
,
8u64
));
(
Some
(
elem
),
None
)
|
(
None
,
Some
(
elem
))
=>
index
+
u64
::
from
(
elem
.bytesize
())
as
i64
,
(
None
,
None
)
=>
panic!
(),
}
}
#[cfg(test)]
mod
tests
;
src/cwe_checker_lib/src/abstract_domain/mem_region/tests.rs
0 → 100644
View file @
76936d8c
use
super
::
*
;
use
crate
::
abstract_domain
::
DataDomain
;
use
crate
::
abstract_domain
::
IntervalDomain
;
use
crate
::
abstract_domain
::
RegisterDomain
;
use
crate
::
intermediate_representation
::
*
;
#[derive(PartialEq,
Eq,
Clone,
Copy,
Debug,
Hash,
PartialOrd,
Ord)]
struct
MockDomain
(
i64
,
ByteSize
);
impl
AbstractDomain
for
MockDomain
{
fn
merge
(
&
self
,
other
:
&
Self
)
->
Self
{
assert_eq!
(
self
.
1
,
other
.
1
);
if
self
==
other
{
self
.clone
()
}
else
{
self
.top
()
}
}
fn
is_top
(
&
self
)
->
bool
{
self
==
&
self
.top
()
}
}
impl
SizedDomain
for
MockDomain
{
fn
bytesize
(
&
self
)
->
ByteSize
{
self
.
1
}
fn
new_top
(
bytesize
:
ByteSize
)
->
MockDomain
{
MockDomain
(
0
,
bytesize
)
}
}
impl
HasTop
for
MockDomain
{
fn
top
(
&
self
)
->
Self
{
Self
::
new_top
(
self
.
1
)
}
}
impl
RegisterDomain
for
MockDomain
{
fn
bin_op
(
&
self
,
_op
:
BinOpType
,
_rhs
:
&
Self
)
->
Self
{
Self
::
new_top
(
self
.
1
)
}
fn
un_op
(
&
self
,
_op
:
UnOpType
)
->
Self
{
Self
::
new_top
(
self
.
1
)
}
fn
cast
(
&
self
,
_kind
:
CastOpType
,
width
:
ByteSize
)
->
Self
{
Self
::
new_top
(
width
)
}
fn
subpiece
(
&
self
,
_low_byte
:
ByteSize
,
size
:
ByteSize
)
->
Self
{
Self
::
new_top
(
size
)
}
}
fn
mock
(
val
:
i64
,
bytesize
:
impl
Into
<
ByteSize
>
)
->
MockDomain
{
MockDomain
(
val
,
bytesize
.into
())
}
fn
bv
(
val
:
i64
)
->
Bitvector
{
Bitvector
::
from_i64
(
val
)
}
#[test]
fn
mem_region
()
{
let
mut
region
:
MemRegion
<
MockDomain
>
=
MemRegion
::
new
(
ByteSize
::
from
(
8u64
));
region
.add
(
mock
(
5
,
3u64
),
bv
(
5
));
assert_eq!
(
region
.get
(
bv
(
5
),
ByteSize
::
from
(
3u64
)),
mock
(
5
,
3u64
));
region
.add
(
mock
(
7
,
2u64
),
bv
(
8
));
assert_eq!
(
region
.get
(
bv
(
8
),
ByteSize
::
from
(
2u64
)),
mock
(
7
,
2u64
));
assert_eq!
(
region
.get
(
bv
(
5
),
ByteSize
::
from
(
3u64
)),
mock
(
5
,
3u64
));
assert_eq!
(
region
.get
(
bv
(
5
),
ByteSize
::
from
(
2u64
)),
MockDomain
::
new_top
(
ByteSize
::
new
(
2
))
);
region
.add
(
mock
(
9
,
2u64
),
bv
(
6
));
assert_eq!
(
region
.get
(
bv
(
6
),
ByteSize
::
from
(
2u64
)),
mock
(
9
,
2u64
));
assert_eq!
(
region
.get
(
bv
(
5
),
ByteSize
::
from
(
3u64
)),
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!
(
region
.get
(
bv
(
6
),
ByteSize
::
from
(
2u64
)),
MockDomain
::
new_top
(
ByteSize
::
new
(
2
))
);
assert_eq!
(
region
.get
(
bv
(
8
),
ByteSize
::
from
(
2u64
)),
mock
(
7
,
2u64
));
let
mut
other_region
=
MemRegion
::
new
(
ByteSize
::
from
(
8u64
));
other_region
.add
(
mock
(
7
,
2u64
),
bv
(
8
));
assert
!
(
region
!=
other_region
);
let
merged_region
=
region
.merge
(
&
other_region
);
assert_eq!
(
merged_region
.get
(
bv
(
8
),
ByteSize
::
from
(
2u64
)),
mock
(
7
,
2u64
)
);
assert_eq!
(
merged_region
.get
(
bv
(
-
3
),
ByteSize
::
from
(
11u64
)),
MockDomain
::
new_top
(
ByteSize
::
from
(
11u64
))
);
other_region
.add
(
mock
(
9
,
11u64
),
bv
(
-
3
));
assert_eq!
(
region
,
other_region
);
}
#[test]
fn
merge_test
()
{
let
data
:
fn
(
u64
)
->
DataDomain
<
IntervalDomain
>
=
|
val
|
DataDomain
::
from
(
Bitvector
::
from_u64
(
val
));
let
mut
region
:
MemRegion
<
DataDomain
<
IntervalDomain
>>
=
MemRegion
::
new
(
ByteSize
::
new
(
8
));
region
.add
(
data
(
0
),
Bitvector
::
from_u64
(
0
));
region
.add
(
data
(
8
),
Bitvector
::
from_u64
(
8
));
region
.add
(
data
(
22
),
Bitvector
::
from_u64
(
32
));
region
.add
(
data
(
42
),
Bitvector
::
from_u64
(
50
));
region
.add
(
data
(
70
),
Bitvector
::
from_u64
(
70
));
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
(
15
),
Bitvector
::
from_u64
(
15
));
other_region
.add
(
data
(
26
),
Bitvector
::
from_u64
(
25
));
other_region
.add
(
data
(
42
),
Bitvector
::
from_u64
(
58
));
other_region
.add
(
Bitvector
::
from_u8
(
70
)
.into
(),
Bitvector
::
from_u64
(
70
));
let
merged_region
=
region
.merge
(
&&
other_region
);
// Merge elements at target address.
assert_eq!
(
merged_region
.get_unsized
(
Bitvector
::
from_u64
(
0
)),
Some
(
IntervalDomain
::
mock
(
0
,
1
)
.into
())
);
// 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
(
Bitvector
::
from_u64
(
15
)),
None
);
assert_eq!
(
merged_region
.get_unsized
(
Bitvector
::
from_u64
(
25
)),
None
);
assert_eq!
(
merged_region
.get_unsized
(
Bitvector
::
from_u64
(
32
)),
None
);
// Elements only contained in one region are merged with `Top`.
let
mut
elem_plus_top
:
DataDomain
<
IntervalDomain
>
=
Bitvector
::
from_u64
(
42
)
.into
();
elem_plus_top
.set_contains_top_flag
();
assert
!
(
!
elem_plus_top
.is_top
());
assert_eq!
(
merged_region
.get_unsized
(
Bitvector
::
from_u64
(
50
)),
Some
(
elem_plus_top
.clone
())
);
assert_eq!
(
merged_region
.get_unsized
(
Bitvector
::
from_u64
(
58
)),
Some
(
elem_plus_top
)
);
// Elements with differing bytesizes are not added to the merged domain.
assert_eq!
(
merged_region
.get_unsized
(
Bitvector
::
from_u64
(
70
)),
None
);
// Check that no other unexpected elements are contained in the merged region.
assert_eq!
(
merged_region
.values
()
.len
(),
3
);
}
#[test]
fn
do_not_save_top_elements
()
{
let
mut
region
:
MemRegion
<
MockDomain
>
=
MemRegion
::
new
(
ByteSize
::
from
(
8u64
));
region
.add
(
MockDomain
::
new_top
(
ByteSize
::
from
(
4u64
)),
bv
(
5
));
assert_eq!
(
region
.values
()
.len
(),
0
);
let
mut
other_region
:
MemRegion
<
MockDomain
>
=
MemRegion
::
new
(
ByteSize
::
from
(
8u64
));
region
.add
(
mock
(
5
,
4u64
),
bv
(
5
));
other_region
.add
(
mock
(
7
,
4u64
),
bv
(
5
));
let
merged_region
=
region
.merge
(
&
other_region
);
assert_eq!
(
region
.values
()
.len
(),
1
);
assert_eq!
(
other_region
.values
()
.len
(),
1
);
assert_eq!
(
merged_region
.values
()
.len
(),
0
);
}
#[test]
fn
value_removals
()
{
let
mut
region
:
MemRegion
<
MockDomain
>
=
MemRegion
::
new
(
ByteSize
::
from
(
8u64
));
region
.add
(
mock
(
1
,
8u64
),
bv
(
0
));
region
.add
(
mock
(
2
,
8u64
),
bv
(
8
));
region
.add
(
mock
(
3
,
8u64
),
bv
(
16
));
region
.add
(
mock
(
4
,
8u64
),
bv
(
24
));
region
.add
(
mock
(
5
,
8u64
),
bv
(
32
));
assert_eq!
(
region
.values
()
.len
(),
5
);
region
.remove
(
bv
(
2
),
bv
(
3
));
assert_eq!
(
region
.values
()
.len
(),
4
);
region
.remove
(
bv
(
7
),
bv
(
1
));
assert_eq!
(
region
.values
()
.len
(),
4
);
region
.remove
(
bv
(
7
),
bv
(
2
));
assert_eq!
(
region
.values
()
.len
(),
3
);
region
.clear_interval
(
15
,
1
);
assert_eq!
(
region
.values
()
.len
(),
3
);
region
.clear_interval
(
15
,
3
);
assert_eq!
(
region
.values
()
.len
(),
2
);
for
val
in
region
.values_mut
()
{
if
*
val
==
mock
(
5
,
8u64
)
{
*
val
=
mock
(
0
,
8u64
);
// This is a *Top* element
}
}
region
.clear_top_values
();
assert_eq!
(
region
.values
()
.len
(),
1
);
assert_eq!
(
region
.get
(
bv
(
24
),
ByteSize
::
from
(
8u64
)),
mock
(
4
,
8u64
));
}
#[test]
fn
merge_writes_with_top
()
{
let
data
:
DataDomain
<
IntervalDomain
>
=
DataDomain
::
from
(
Bitvector
::
from_u64
(
0
));
let
mut
data_with_top
=
data
.clone
();
data_with_top
.set_contains_top_flag
();
let
mut
region
:
MemRegion
<
DataDomain
<
IntervalDomain
>>
=
MemRegion
::
new
(
ByteSize
::
new
(
8
));
// Test `merge_write_top` method.
region
.add
(
data
.clone
(),
bv
(
0
));
region
.merge_write_top
(
bv
(
0
),
ByteSize
::
new
(
8
));
assert_eq!
(
region
.get_unsized
(
bv
(
0
)),
Some
(
data_with_top
.clone
()));
// `merge_write_top` removes intersecting values if position or size do not match.
region
.add
(
data
.clone
(),
bv
(
8
));
region
.merge_write_top
(
bv
(
5
),
ByteSize
::
new
(
8
));
assert
!
(
region
.inner.values
.is_empty
());
// Test `mark_interval_values_as_top` method.
region
.add
(
data
.clone
(),
bv
(
0
));
region
.add
(
data
.clone
(),
bv
(
8
));
region
.add
(
data
.clone
(),
bv
(
16
));
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
(
bv
(
8
)),
Some
(
data_with_top
.clone
()));
assert_eq!
(
region
.get_unsized
(
bv
(
16
)),
Some
(
data_with_top
.clone
()));
}
src/cwe_checker_lib/src/analysis/pointer_inference/object.rs
View file @
76936d8c
...
...
@@ -470,9 +470,14 @@ mod tests {
other_object
.set_value
(
new_data
(
0
),
&
bv
(
0
))
.unwrap
();
let
merged_object
=
object
.merge
(
&
other_object
);
assert_eq!
(
merged_object
.get_value
(
Bitvector
::
from_i64
(
-
12
),
ByteSize
::
new
(
8
)),
Data
::
new_top
(
ByteSize
::
new
(
8
))
merged_object
.get_value
(
Bitvector
::
from_i64
(
-
12
),
ByteSize
::
new
(
8
))
.get_absolute_value
(),
Some
(
&
IntervalDomain
::
mock
(
4
,
23
)
.with_stride
(
19
)
.into
())
);
assert
!
(
merged_object
.get_value
(
Bitvector
::
from_i64
(
-
12
),
ByteSize
::
new
(
8
))
.contains_top
());
assert_eq!
(
merged_object
.get_value
(
Bitvector
::
from_i64
(
0
),
ByteSize
::
new
(
8
)),
new_data
(
0
)
...
...
src/cwe_checker_lib/src/analysis/pointer_inference/object_list.rs
View file @
76936d8c
...
...
@@ -491,10 +491,10 @@ mod tests {
let
mut
merged
=
obj_list
.merge
(
&
other_obj_list
);
assert_eq!
(
merged
.get_value
(
&
pointer
,
ByteSize
::
new
(
8
)),
bv
(
42
)
.into
());
assert_eq!
(
merged
.get_value
(
&
second_pointer
,
ByteSize
::
new
(
8
)),
Data
::
new_top
(
ByteSize
::
new
(
8
))
);
assert
!
(
merged
.get_value
(
&
second_pointer
,
ByteSize
::
new
(
8
))
.contains_top
()
);
assert_eq!
(
merged
.get_value
(
&
heap_pointer
,
ByteSize
::
new
(
8
)),
bv
(
3
)
.into
()
...
...
src/cwe_checker_lib/src/analysis/pointer_inference/state/tests.rs
View file @
76936d8c
...
...
@@ -61,12 +61,10 @@ fn state() {
let
merged_state
=
state
.merge
(
&
other_state
);
assert_eq!
(
merged_state
.register
[
&
register
(
"RAX"
)],
bv
(
42
)
.into
());
assert_eq!
(
merged_state
.register
.get
(
&
register
(
"RBX"
)),
None
);
assert_eq!
(
merged_state
.load_value
(
&
Var
(
register
(
"RSP"
)),
ByteSize
::
new
(
8
),
&
global_memory
)
.unwrap
(),
Data
::
new_top
(
ByteSize
::
new
(
8
))
);
assert
!
(
merged_state
.load_value
(
&
Var
(
register
(
"RSP"
)),
ByteSize
::
new
(
8
),
&
global_memory
)
.unwrap
()
.contains_top
());
// Test pointer adjustment on reads
state
.memory
.add_abstract_object
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment