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-gitdep
cwe_checker
Commits
af32d275
Commit
af32d275
authored
Oct 05, 2020
by
Enkelmann
Committed by
Enkelmann
Nov 03, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adapt the analysis modules to use the internal IR instead of the BAP IR.
parent
d701731b
Hide whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
977 additions
and
926 deletions
+977
-926
Makefile
Makefile
+8
-5
Cargo.toml
cwe_checker_rs/Cargo.toml
+1
-0
bitvector.rs
cwe_checker_rs/src/abstract_domain/bitvector.rs
+175
-111
data.rs
cwe_checker_rs/src/abstract_domain/data.rs
+75
-67
identifier.rs
cwe_checker_rs/src/abstract_domain/identifier.rs
+3
-3
mem_region.rs
cwe_checker_rs/src/abstract_domain/mem_region.rs
+94
-84
mod.rs
cwe_checker_rs/src/abstract_domain/mod.rs
+17
-20
pointer.rs
cwe_checker_rs/src/abstract_domain/pointer.rs
+9
-9
graph.rs
cwe_checker_rs/src/analysis/graph.rs
+48
-51
interprocedural_fixpoint.rs
cwe_checker_rs/src/analysis/interprocedural_fixpoint.rs
+1
-2
mod.rs
cwe_checker_rs/src/analysis/pointer_inference/context/mod.rs
+78
-67
tests.rs
...hecker_rs/src/analysis/pointer_inference/context/tests.rs
+45
-69
trait_impls.rs
..._rs/src/analysis/pointer_inference/context/trait_impls.rs
+52
-106
mod.rs
cwe_checker_rs/src/analysis/pointer_inference/mod.rs
+1
-2
object.rs
cwe_checker_rs/src/analysis/pointer_inference/object.rs
+28
-26
object_list.rs
cwe_checker_rs/src/analysis/pointer_inference/object_list.rs
+37
-24
access_handling.rs
...s/src/analysis/pointer_inference/state/access_handling.rs
+53
-150
mod.rs
cwe_checker_rs/src/analysis/pointer_inference/state/mod.rs
+23
-18
tests.rs
cwe_checker_rs/src/analysis/pointer_inference/state/tests.rs
+114
-106
analysis.rs
cwe_checker_rs/src/ffi/analysis.rs
+2
-2
expression.rs
cwe_checker_rs/src/intermediate_representation/expression.rs
+26
-0
mod.rs
cwe_checker_rs/src/intermediate_representation/mod.rs
+6
-0
term.rs
cwe_checker_rs/src/intermediate_representation/term.rs
+39
-0
variable.rs
cwe_checker_rs/src/intermediate_representation/variable.rs
+1
-1
lib.rs
cwe_checker_rs/src/lib.rs
+1
-0
term.rs
cwe_checker_rs/src/pcode/term.rs
+8
-3
mod.rs
cwe_checker_rs/src/term/mod.rs
+2
-0
mod.rs
cwe_checker_rs/src/utils/mod.rs
+30
-0
No files found.
Makefile
View file @
af32d275
...
...
@@ -5,11 +5,13 @@ all:
cp target/release/libcwe_checker_rs.so src/dllcwe_checker_rs.so
dune build
dune install
cd
plugins/cwe_checker
;
make all
;
cd
../..
cd
plugins/cwe_checker_emulation
;
make all
;
cd
../..
cd
plugins/cwe_checker_type_inference
;
make all
;
cd
../..
cd
plugins/cwe_checker_type_inference_print
;
make all
;
cd
../..
cd
plugins/cwe_checker_pointer_inference_debug
;
make all
;
cd
../..
cd
plugins/cwe_checker
&&
make all
cd
plugins/cwe_checker_emulation
&&
make all
cd
plugins/cwe_checker_type_inference
&&
make all
cd
plugins/cwe_checker_type_inference_print
&&
make all
cd
plugins/cwe_checker_pointer_inference_debug
&&
make all
mkdir
${
HOME
}
/.config/cwe_checker
cp src/utils/registers.json
${
HOME
}
/.config/cwe_checker/registers.json
test
:
cargo
test
...
...
@@ -43,6 +45,7 @@ uninstall:
cd
plugins/cwe_checker_type_inference
;
make uninstall
;
cd
../..
cd
plugins/cwe_checker_type_inference_print
;
make uninstall
;
cd
../..
cd
plugins/cwe_checker_pointer_inference_debug
;
make uninstall
;
cd
../..
rm
-f
-r
${
HOME
}
/.config/cwe_checker
documentation
:
dune build @doc
...
...
cwe_checker_rs/Cargo.toml
View file @
af32d275
...
...
@@ -15,6 +15,7 @@ fnv = "1.0" # a faster hash function for small keys like integers
anyhow
=
"1.0"
# for easy error types
crossbeam-channel
=
"0.4"
derive_more
=
"0.99"
directories
=
"3.0"
[lib]
name
=
"cwe_checker_rs"
...
...
cwe_checker_rs/src/abstract_domain/bitvector.rs
View file @
af32d275
use
super
::{
AbstractDomain
,
HasBitSize
,
HasTop
,
RegisterDomain
};
use
crate
::
bil
::
*
;
use
super
::{
AbstractDomain
,
HasByteSize
,
HasTop
,
RegisterDomain
};
use
crate
::
bil
::
BitSize
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
prelude
::
*
;
/// The `BitvectorDomain` is a simple abstract domain describing a bitvector of known length.
///
/// As values it can only assume a known bitvector or *Top(b
it
size)*.
/// As values it can only assume a known bitvector or *Top(b
yte
size)*.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone)]
pub
enum
BitvectorDomain
{
Top
(
B
it
Size
),
Top
(
B
yte
Size
),
Value
(
Bitvector
),
}
...
...
@@ -28,27 +29,27 @@ impl AbstractDomain for BitvectorDomain {
}
impl
HasTop
for
BitvectorDomain
{
/// Return a *Top* value with the same b
it
size as `self`.
/// Return a *Top* value with the same b
yte
size as `self`.
fn
top
(
&
self
)
->
BitvectorDomain
{
BitvectorDomain
::
Top
(
self
.b
it
size
())
BitvectorDomain
::
Top
(
self
.b
yte
size
())
}
}
impl
HasB
it
Size
for
BitvectorDomain
{
/// Return the b
it
size of `self`.
fn
b
itsize
(
&
self
)
->
Bit
Size
{
impl
HasB
yte
Size
for
BitvectorDomain
{
/// Return the b
yte
size of `self`.
fn
b
ytesize
(
&
self
)
->
Byte
Size
{
use
BitvectorDomain
::
*
;
match
self
{
Top
(
b
itsize
)
=>
*
bit
size
,
Value
(
bitvec
)
=>
bitvec
.width
()
.
to_usize
()
as
u16
,
Top
(
b
ytesize
)
=>
*
byte
size
,
Value
(
bitvec
)
=>
bitvec
.width
()
.
into
()
,
}
}
}
impl
RegisterDomain
for
BitvectorDomain
{
/// Get a *Top* element with the given bitsize.
fn
new_top
(
b
itsize
:
Bit
Size
)
->
BitvectorDomain
{
BitvectorDomain
::
Top
(
b
it
size
)
fn
new_top
(
b
ytesize
:
Byte
Size
)
->
BitvectorDomain
{
BitvectorDomain
::
Top
(
b
yte
size
)
}
/// Evaluate the given binary operation.
...
...
@@ -57,27 +58,83 @@ impl RegisterDomain for BitvectorDomain {
fn
bin_op
(
&
self
,
op
:
BinOpType
,
rhs
:
&
Self
)
->
Self
{
use
BinOpType
::
*
;
match
op
{
LSHIFT
|
RSHIFT
|
ARSHIFT
=>
(),
_
=>
assert_eq!
(
self
.b
itsize
(),
rhs
.bit
size
()),
Piece
|
IntLeft
|
IntRight
|
IntSRight
=>
(),
_
=>
assert_eq!
(
self
.b
ytesize
(),
rhs
.byte
size
()),
}
match
(
self
,
rhs
)
{
(
BitvectorDomain
::
Value
(
lhs_bitvec
),
BitvectorDomain
::
Value
(
rhs_bitvec
))
=>
match
op
{
PLUS
=>
BitvectorDomain
::
Value
(
lhs_bitvec
+
rhs_bitvec
),
MINUS
=>
BitvectorDomain
::
Value
(
lhs_bitvec
-
rhs_bitvec
),
TIMES
=>
BitvectorDomain
::
Value
(
lhs_bitvec
*
rhs_bitvec
),
DIVIDE
=>
BitvectorDomain
::
Value
(
Piece
=>
{
let
new_bitwidth
=
BitSize
::
from
(
self
.bytesize
()
+
rhs
.bytesize
());
let
upper_bits
=
lhs_bitvec
.clone
()
.into_zero_extend
(
new_bitwidth
as
usize
)
.unwrap
()
.into_checked_shl
(
BitSize
::
from
(
rhs
.bytesize
())
as
usize
)
.unwrap
();
let
lower_bits
=
rhs_bitvec
.clone
()
.into_zero_extend
(
new_bitwidth
as
usize
)
.unwrap
();
BitvectorDomain
::
Value
(
upper_bits
|
&
lower_bits
)
}
IntAdd
=>
BitvectorDomain
::
Value
(
lhs_bitvec
+
rhs_bitvec
),
IntSub
=>
BitvectorDomain
::
Value
(
lhs_bitvec
-
rhs_bitvec
),
IntCarry
=>
{
let
result
=
lhs_bitvec
+
rhs_bitvec
;
if
result
.checked_ult
(
lhs_bitvec
)
.unwrap
()
||
result
.checked_ult
(
rhs_bitvec
)
.unwrap
()
{
Bitvector
::
from_u8
(
1
)
.into
()
}
else
{
Bitvector
::
from_u8
(
0
)
.into
()
}
}
IntSCarry
=>
{
let
result
=
apint
::
Int
::
from
(
lhs_bitvec
+
rhs_bitvec
);
let
lhs_bitvec
=
apint
::
Int
::
from
(
lhs_bitvec
.clone
());
let
rhs_bitvec
=
apint
::
Int
::
from
(
rhs_bitvec
.clone
());
if
(
result
.is_negative
()
&&
lhs_bitvec
.is_positive
()
&&
rhs_bitvec
.is_positive
())
||
(
!
result
.is_negative
()
&&
lhs_bitvec
.is_negative
()
&&
rhs_bitvec
.is_negative
())
{
Bitvector
::
from_u8
(
1
)
.into
()
}
else
{
Bitvector
::
from_u8
(
0
)
.into
()
}
}
IntSBorrow
=>
{
let
result
=
apint
::
Int
::
from
(
lhs_bitvec
-
rhs_bitvec
);
let
lhs_bitvec
=
apint
::
Int
::
from
(
lhs_bitvec
.clone
());
let
rhs_bitvec
=
apint
::
Int
::
from
(
rhs_bitvec
.clone
());
if
(
result
.is_negative
()
&&
!
lhs_bitvec
.is_positive
()
&&
rhs_bitvec
.is_negative
())
||
(
result
.is_positive
()
&&
lhs_bitvec
.is_negative
()
&&
rhs_bitvec
.is_positive
())
{
Bitvector
::
from_u8
(
1
)
.into
()
}
else
{
Bitvector
::
from_u8
(
0
)
.into
()
}
}
IntMult
=>
BitvectorDomain
::
Value
(
lhs_bitvec
*
rhs_bitvec
),
IntDiv
=>
BitvectorDomain
::
Value
(
lhs_bitvec
.clone
()
.into_checked_udiv
(
rhs_bitvec
)
.unwrap
(),
),
SDIVIDE
=>
BitvectorDomain
::
Value
(
IntSDiv
=>
BitvectorDomain
::
Value
(
lhs_bitvec
.clone
()
.into_checked_sdiv
(
rhs_bitvec
)
.unwrap
(),
),
MOD
=>
BitvectorDomain
::
Value
(
IntRem
=>
BitvectorDomain
::
Value
(
lhs_bitvec
.clone
()
.into_checked_urem
(
rhs_bitvec
)
.unwrap
(),
),
SMOD
=>
BitvectorDomain
::
Value
(
IntSRem
=>
BitvectorDomain
::
Value
(
lhs_bitvec
.clone
()
.into_checked_srem
(
rhs_bitvec
)
.unwrap
(),
),
LSHIFT
=>
{
IntLeft
=>
{
let
shift_amount
=
rhs_bitvec
.try_to_u64
()
.unwrap
()
as
usize
;
if
shift_amount
<
lhs_bitvec
.width
()
.to_usize
()
{
BitvectorDomain
::
Value
(
...
...
@@ -87,7 +144,7 @@ impl RegisterDomain for BitvectorDomain {
BitvectorDomain
::
Value
(
Bitvector
::
zero
(
lhs_bitvec
.width
()))
}
}
RSHIFT
=>
{
IntRight
=>
{
let
shift_amount
=
rhs_bitvec
.try_to_u64
()
.unwrap
()
as
usize
;
if
shift_amount
<
lhs_bitvec
.width
()
.to_usize
()
{
BitvectorDomain
::
Value
(
...
...
@@ -97,7 +154,7 @@ impl RegisterDomain for BitvectorDomain {
BitvectorDomain
::
Value
(
Bitvector
::
zero
(
lhs_bitvec
.width
()))
}
}
ARSHIFT
=>
{
IntSRight
=>
{
let
shift_amount
=
rhs_bitvec
.try_to_u64
()
.unwrap
()
as
usize
;
if
shift_amount
<
lhs_bitvec
.width
()
.to_usize
()
{
BitvectorDomain
::
Value
(
...
...
@@ -114,34 +171,48 @@ impl RegisterDomain for BitvectorDomain {
}
}
}
AND
=>
BitvectorDomain
::
Value
(
lhs_bitvec
&
rhs_bitvec
),
OR
=>
BitvectorDomain
::
Value
(
lhs_bitvec
|
rhs_bitvec
),
XOR
=>
BitvectorDomain
::
Value
(
lhs_bitvec
^
rhs_bitvec
),
EQ
=>
{
IntAnd
|
BoolAnd
=>
BitvectorDomain
::
Value
(
lhs_bitvec
&
rhs_bitvec
),
IntOr
|
BoolOr
=>
BitvectorDomain
::
Value
(
lhs_bitvec
|
rhs_bitvec
),
IntXOr
|
BoolXOr
=>
BitvectorDomain
::
Value
(
lhs_bitvec
^
rhs_bitvec
),
IntEqual
=>
{
assert_eq!
(
lhs_bitvec
.width
(),
rhs_bitvec
.width
());
BitvectorDomain
::
Value
(
Bitvector
::
from
(
lhs_bitvec
==
rhs_bitvec
))
}
NEQ
=>
{
IntNotEqual
=>
{
assert_eq!
(
lhs_bitvec
.width
(),
rhs_bitvec
.width
());
BitvectorDomain
::
Value
(
Bitvector
::
from
(
lhs_bitvec
!=
rhs_bitvec
))
}
LT
=>
BitvectorDomain
::
Value
(
Bitvector
::
from
(
IntLess
=>
BitvectorDomain
::
Value
(
Bitvector
::
from
(
lhs_bitvec
.checked_ult
(
rhs_bitvec
)
.unwrap
(),
)),
LE
=>
BitvectorDomain
::
Value
(
Bitvector
::
from
(
IntLessEqual
=>
BitvectorDomain
::
Value
(
Bitvector
::
from
(
lhs_bitvec
.checked_ule
(
rhs_bitvec
)
.unwrap
(),
)),
SLT
=>
BitvectorDomain
::
Value
(
Bitvector
::
from
(
IntSLess
=>
BitvectorDomain
::
Value
(
Bitvector
::
from
(
lhs_bitvec
.checked_slt
(
rhs_bitvec
)
.unwrap
(),
)),
SLE
=>
BitvectorDomain
::
Value
(
Bitvector
::
from
(
IntSLessEqual
=>
BitvectorDomain
::
Value
(
Bitvector
::
from
(
lhs_bitvec
.checked_sle
(
rhs_bitvec
)
.unwrap
(),
)),
FloatEqual
|
FloatNotEqual
|
FloatLess
|
FloatLessEqual
=>
{
// TODO: Implement floating point comparison operators!
BitvectorDomain
::
new_top
(
ByteSize
::
new
(
1
))
}
FloatAdd
|
FloatSub
|
FloatMult
|
FloatDiv
=>
{
// TODO: Implement floating point arithmetic operators!
BitvectorDomain
::
new_top
(
self
.bytesize
())
}
},
_
=>
match
op
{
PLUS
|
MINUS
|
TIMES
|
DIVIDE
|
SDIVIDE
|
MOD
|
SMOD
|
LSHIFT
|
RSHIFT
|
ARSHIFT
|
AND
|
OR
|
XOR
=>
BitvectorDomain
::
new_top
(
self
.bitsize
()),
EQ
|
NEQ
|
LT
|
LE
|
SLT
|
SLE
=>
BitvectorDomain
::
new_top
(
1
),
Piece
=>
BitvectorDomain
::
new_top
(
self
.bytesize
()
+
rhs
.bytesize
()),
IntAdd
|
IntSub
|
IntMult
|
IntDiv
|
IntSDiv
|
IntRem
|
IntSRem
|
IntLeft
|
IntRight
|
IntSRight
|
IntAnd
|
IntOr
|
IntXOr
|
FloatAdd
|
FloatSub
|
FloatMult
|
FloatDiv
=>
BitvectorDomain
::
new_top
(
self
.bytesize
()),
IntEqual
|
IntNotEqual
|
IntLess
|
IntLessEqual
|
IntSLess
|
IntSLessEqual
|
IntCarry
|
IntSCarry
|
IntSBorrow
|
BoolAnd
|
BoolOr
|
BoolXOr
|
FloatEqual
|
FloatNotEqual
|
FloatLess
|
FloatLessEqual
=>
{
BitvectorDomain
::
new_top
(
ByteSize
::
new
(
1
))
}
},
}
}
...
...
@@ -151,82 +222,72 @@ impl RegisterDomain for BitvectorDomain {
use
UnOpType
::
*
;
if
let
BitvectorDomain
::
Value
(
bitvec
)
=
self
{
match
op
{
NEG
=>
BitvectorDomain
::
Value
(
-
bitvec
),
NOT
=>
BitvectorDomain
::
Value
(
bitvec
.clone
()
.into_bitnot
()),
Int2Comp
=>
BitvectorDomain
::
Value
(
-
bitvec
),
IntNegate
=>
BitvectorDomain
::
Value
(
bitvec
.clone
()
.into_bitnot
()),
BoolNegate
=>
{
if
bitvec
.is_zero
()
{
BitvectorDomain
::
Value
(
Bitvector
::
from_u8
(
1
))
}
else
{
BitvectorDomain
::
Value
(
Bitvector
::
from_u8
(
0
))
}
}
FloatNegate
|
FloatAbs
|
FloatSqrt
|
FloatCeil
|
FloatFloor
|
FloatRound
|
FloatNaN
=>
BitvectorDomain
::
new_top
(
self
.bytesize
()),
}
}
else
{
BitvectorDomain
::
new_top
(
self
.bitsize
())
match
op
{
BoolNegate
=>
BitvectorDomain
::
new_top
(
ByteSize
::
new
(
1
)),
_
=>
BitvectorDomain
::
new_top
(
self
.bytesize
()),
}
}
}
/// Extract a sub-bitvector out of a bitvector
fn
extract
(
&
self
,
low_bit
:
BitSize
,
high_bit
:
Bit
Size
)
->
Self
{
fn
subpiece
(
&
self
,
low_byte
:
ByteSize
,
size
:
Byte
Size
)
->
Self
{
if
let
BitvectorDomain
::
Value
(
bitvec
)
=
self
{
BitvectorDomain
::
Value
(
bitvec
.clone
()
.into_checked_lshr
(
low_bit
as
usize
)
.into_checked_lshr
(
BitSize
::
from
(
low_byte
)
as
usize
)
.unwrap
()
.into_truncate
(
(
high_bit
-
low_bit
+
1
)
as
usize
)
.into_truncate
(
BitSize
::
from
(
size
)
as
usize
)
.unwrap
(),
)
}
else
{
BitvectorDomain
::
new_top
(
high_bit
-
low_bit
+
1
)
BitvectorDomain
::
new_top
(
size
)
}
}
/// Perform a size-changing cast on a bitvector.
fn
cast
(
&
self
,
kind
:
Cast
Type
,
width
:
Bit
Size
)
->
Self
{
fn
cast
(
&
self
,
kind
:
Cast
OpType
,
width
:
Byte
Size
)
->
Self
{
if
let
BitvectorDomain
::
Value
(
bitvec
)
=
self
{
use
CastType
::
*
;
use
Cast
Op
Type
::
*
;
match
kind
{
UNSIGNED
=>
{
BitvectorDomain
::
Value
(
bitvec
.clone
()
.into_zero_extend
(
width
as
usize
)
.unwrap
())
}
SIGNED
=>
{
BitvectorDomain
::
Value
(
bitvec
.clone
()
.into_sign_extend
(
width
as
usize
)
.unwrap
())
}
HIGH
=>
BitvectorDomain
::
Value
(
IntZExt
=>
BitvectorDomain
::
Value
(
bitvec
.clone
()
.into_checked_lshr
((
self
.bitsize
()
-
width
)
as
usize
)
.unwrap
()
.into_truncate
(
width
as
usize
)
.into_zero_extend
(
apint
::
BitWidth
::
from
(
width
))
.unwrap
(),
),
LOW
=>
{
BitvectorDomain
::
Value
(
bitvec
.clone
()
.into_truncate
(
width
as
usize
)
.unwrap
())
}
IntSExt
=>
BitvectorDomain
::
Value
(
bitvec
.clone
()
.into_sign_extend
(
apint
::
BitWidth
::
from
(
width
))
.unwrap
(),
),
Int2Float
|
Float2Float
|
Trunc
=>
BitvectorDomain
::
new_top
(
width
),
}
}
else
{
BitvectorDomain
::
new_top
(
width
)
}
}
/// Concatenate two bitvectors.
fn
concat
(
&
self
,
other
:
&
Self
)
->
Self
{
match
(
self
,
other
)
{
(
BitvectorDomain
::
Value
(
left_bitvec
),
BitvectorDomain
::
Value
(
right_bitvec
))
=>
{
let
new_bitwidth
=
(
self
.bitsize
()
+
other
.bitsize
())
as
usize
;
let
upper_bits
=
left_bitvec
.clone
()
.into_zero_extend
(
new_bitwidth
)
.unwrap
()
.into_checked_shl
(
other
.bitsize
()
as
usize
)
.unwrap
();
let
lower_bits
=
right_bitvec
.clone
()
.into_zero_extend
(
new_bitwidth
)
.unwrap
();
BitvectorDomain
::
Value
(
upper_bits
|
&
lower_bits
)
}
_
=>
BitvectorDomain
::
new_top
(
self
.bitsize
()
+
other
.bitsize
()),
}
}
}
impl
std
::
ops
::
Add
for
BitvectorDomain
{
type
Output
=
BitvectorDomain
;
fn
add
(
self
,
rhs
:
Self
)
->
Self
{
self
.bin_op
(
crate
::
bil
::
BinOpType
::
PLUS
,
&
rhs
)
self
.bin_op
(
BinOpType
::
IntAdd
,
&
rhs
)
}
}
...
...
@@ -234,7 +295,7 @@ impl std::ops::Sub for BitvectorDomain {
type
Output
=
BitvectorDomain
;
fn
sub
(
self
,
rhs
:
Self
)
->
Self
{
self
.bin_op
(
crate
::
bil
::
BinOpType
::
MINUS
,
&
rhs
)
self
.bin_op
(
BinOpType
::
IntSub
,
&
rhs
)
}
}
...
...
@@ -242,7 +303,7 @@ impl std::ops::Neg for BitvectorDomain {
type
Output
=
BitvectorDomain
;
fn
neg
(
self
)
->
Self
{
self
.un_op
(
crate
::
bil
::
UnOpType
::
NEG
)
self
.un_op
(
UnOpType
::
Int2Comp
)
}
}
...
...
@@ -286,55 +347,55 @@ mod tests {
#[test]
fn
bitvector_domain_as_value_domain
()
{
use
crate
::
bil
::
BinOpType
::
*
;
use
crate
::
bil
::
CastType
::
*
;
use
crate
::
bil
::
UnOpType
::
*
;
use
BinOpType
::
*
;
use
UnOpType
::
*
;
let
eight
=
bv
(
8
);
let
sixteen
=
bv
(
16
);
assert_eq!
(
sixteen
.bin_op
(
PLUS
,
&
eight
),
bv
(
24
));
assert_eq!
(
sixteen
.bin_op
(
MINUS
,
&
eight
),
bv
(
8
));
assert_eq!
(
sixteen
.bin_op
(
TIMES
,
&
eight
),
bv
(
16
*
8
));
assert_eq!
(
sixteen
.bin_op
(
DIVIDE
,
&
eight
),
bv
(
2
));
assert_eq!
(
sixteen
.bin_op
(
SDIVIDE
,
&
eight
),
bv
(
2
));
assert_eq!
(
sixteen
.bin_op
(
MOD
,
&
eight
),
bv
(
0
));
assert_eq!
(
sixteen
.bin_op
(
SMOD
,
&
eight
),
bv
(
0
));
assert_eq!
(
sixteen
.bin_op
(
LSHIFT
,
&
bv
(
2
)),
bv
(
64
));
assert_eq!
(
sixteen
.bin_op
(
RSHIFT
,
&
bv
(
2
)),
bv
(
4
));
assert_eq!
(
sixteen
.bin_op
(
ARSHIFT
,
&
bv
(
2
)),
bv
(
4
));
assert_eq!
(
sixteen
.bin_op
(
AND
,
&
eight
),
bv
(
0
));
assert_eq!
(
sixteen
.bin_op
(
OR
,
&
eight
),
bv
(
24
));
assert_eq!
(
sixteen
.bin_op
(
XOR
,
&
eight
),
bv
(
24
));
assert_eq!
(
sixteen
.bin_op
(
IntAdd
,
&
eight
),
bv
(
24
));
assert_eq!
(
sixteen
.bin_op
(
IntSub
,
&
eight
),
bv
(
8
));
assert_eq!
(
sixteen
.bin_op
(
IntMult
,
&
eight
),
bv
(
16
*
8
));
assert_eq!
(
sixteen
.bin_op
(
IntDiv
,
&
eight
),
bv
(
2
));
assert_eq!
(
sixteen
.bin_op
(
IntSDiv
,
&
eight
),
bv
(
2
));
assert_eq!
(
sixteen
.bin_op
(
IntRem
,
&
eight
),
bv
(
0
));
assert_eq!
(
sixteen
.bin_op
(
IntSRem
,
&
eight
),
bv
(
0
));
assert_eq!
(
sixteen
.bin_op
(
IntLeft
,
&
bv
(
2
)),
bv
(
64
));
assert_eq!
(
sixteen
.bin_op
(
IntRight
,
&
bv
(
2
)),
bv
(
4
));
assert_eq!
(
sixteen
.bin_op
(
IntSRight
,
&
bv
(
2
)),
bv
(
4
));
assert_eq!
(
sixteen
.bin_op
(
IntAnd
,
&
eight
),
bv
(
0
));
assert_eq!
(
sixteen
.bin_op
(
IntOr
,
&
eight
),
bv
(
24
));
assert_eq!
(
sixteen
.bin_op
(
IntXOr
,
&
eight
),
bv
(
24
));
assert_eq!
(
sixteen
.bin_op
(
EQ
,
&
bv
(
16
)),
sixteen
.bin_op
(
IntEqual
,
&
bv
(
16
)),
BitvectorDomain
::
Value
(
Bitvector
::
from_bit
(
true
))
);
assert_eq!
(
sixteen
.bin_op
(
NEQ
,
&
bv
(
16
)),
sixteen
.bin_op
(
IntNotEqual
,
&
bv
(
16
)),
BitvectorDomain
::
Value
(
Bitvector
::
from_bit
(
false
))
);
assert_eq!
(
sixteen
.un_op
(
NEG
),
bv
(
-
16
));
assert_eq!
(
bv
(
0
)
.un_op
(
NOT
),
bv
(
-
1
));
assert_eq!
(
sixteen
.un_op
(
Int2Comp
),
bv
(
-
16
));
assert_eq!
(
bv
(
0
)
.un_op
(
IntNegate
),
bv
(
-
1
));
assert_eq!
(
sixteen
.
extract
(
0
,
31
),
sixteen
.
subpiece
(
ByteSize
::
new
(
0
),
ByteSize
::
new
(
4
)
),
BitvectorDomain
::
Value
(
Bitvector
::
from_i32
(
16
))
);
assert_eq!
(
sixteen
.
extract
(
32
,
63
),
sixteen
.
subpiece
(
ByteSize
::
new
(
4
),
ByteSize
::
new
(
4
)
),
BitvectorDomain
::
Value
(
Bitvector
::
from_i32
(
0
))
);
assert_eq!
(
BitvectorDomain
::
Value
(
Bitvector
::
from_i32
(
2
)),
BitvectorDomain
::
Value
(
Bitvector
::
from_i64
(
2
<<
32
))
.cast
(
HIGH
,
32
)
BitvectorDomain
::
Value
(
Bitvector
::
from_i64
(
2
<<
32
))
.subpiece
(
ByteSize
::
new
(
4
),
ByteSize
::
new
(
4
))
);
assert_eq!
(
BitvectorDomain
::
Value
(
Bitvector
::
from_i32
(
-
1
))
.
concat
(
&
BitvectorDomain
::
Value
(
Bitvector
::
from_i32
(
-
1
))),
.
bin_op
(
Piece
,
&
BitvectorDomain
::
Value
(
Bitvector
::
from_i32
(
-
1
))),
bv
(
-
1
)
);
}
...
...
@@ -342,32 +403,35 @@ mod tests {
#[test]
fn
bitvector_domain_as_abstract_domain
()
{
assert_eq!
(
bv
(
17
)
.merge
(
&
bv
(
17
)),
bv
(
17
));
assert_eq!
(
bv
(
17
)
.merge
(
&
bv
(
16
)),
BitvectorDomain
::
new_top
(
64
));
assert_eq!
(
bv
(
17
)
.merge
(
&
bv
(
16
)),
BitvectorDomain
::
new_top
(
ByteSize
::
new
(
8
))
);
assert
!
(
!
bv
(
17
)
.is_top
());
assert
!
(
BitvectorDomain
::
new_top
(
64
)
.is_top
());
assert
!
(
BitvectorDomain
::
new_top
(
ByteSize
::
new
(
8
)
)
.is_top
());
}
#[test]
fn
arshift
()
{
use
crate
::
bil
::
BinOpType
::
ARSHIFT
;
use
BinOpType
::
IntSRight
;
let
positive_x
=
BitvectorDomain
::
Value
(
Bitvector
::
from_i64
(
31
));
let
negative_x
=
BitvectorDomain
::
Value
(
Bitvector
::
from_i64
(
-
31
));
let
shift_3
=
BitvectorDomain
::
Value
(
Bitvector
::
from_u8
(
3
));
let
shift_70
=
BitvectorDomain
::
Value
(
Bitvector
::
from_u8
(
70
));
assert_eq!
(
positive_x
.bin_op
(
ARSHIFT
,
&
shift_3
),
positive_x
.bin_op
(
IntSRight
,
&
shift_3
),
BitvectorDomain
::
Value
(
Bitvector
::
from_i64
(
3
))
);
assert_eq!
(
positive_x
.bin_op
(
ARSHIFT
,
&
shift_70
),
positive_x
.bin_op
(
IntSRight
,
&
shift_70
),
BitvectorDomain
::
Value
(
Bitvector
::
from_i64
(
0
))
);
assert_eq!
(
negative_x
.bin_op
(
ARSHIFT
,
&
shift_3
),
negative_x
.bin_op
(
IntSRight
,
&
shift_3
),
BitvectorDomain
::
Value
(
Bitvector
::
from_i64
(
-
4
))
);
assert_eq!
(
negative_x
.bin_op
(
ARSHIFT
,
&
shift_70
),
negative_x
.bin_op
(
IntSRight
,
&
shift_70
),
BitvectorDomain
::
Value
(
Bitvector
::
from_i64
(
-
1
))
);
}
...
...
cwe_checker_rs/src/abstract_domain/data.rs
View file @
af32d275
use
super
::{
AbstractDomain
,
AbstractIdentifier
,
HasB
it
Size
,
HasTop
,
PointerDomain
,
RegisterDomain
,
AbstractDomain
,
AbstractIdentifier
,
HasB
yte
Size
,
HasTop
,
PointerDomain
,
RegisterDomain
,
};
use
crate
::
bil
::
*
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
prelude
::
*
;
use
std
::
collections
::{
BTreeMap
,
BTreeSet
};
use
std
::
fmt
::
Display
;
...
...
@@ -10,7 +10,7 @@ use std::fmt::Display;
/// Both non-pointer values and offsets of pointers are represented by the same abstract domain `T`.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Clone)]
pub
enum
DataDomain
<
T
:
RegisterDomain
>
{
Top
(
B
it
Size
),
Top
(
B
yte
Size
),
Pointer
(
PointerDomain
<
T
>
),
Value
(
T
),
}
...
...
@@ -54,7 +54,7 @@ impl<T: RegisterDomain> DataDomain<T> {
})
.collect
();
if
remaining_targets
.is_empty
()
{
*
self
=
Self
::
new_top
(
self
.b
it
size
());
*
self
=
Self
::
new_top
(
self
.b
yte
size
());
}
else
{
*
self
=
Self
::
Pointer
(
PointerDomain
::
with_targets
(
remaining_targets
));
}
...
...
@@ -62,14 +62,14 @@ impl<T: RegisterDomain> DataDomain<T> {
}
}
impl
<
T
:
RegisterDomain
>
HasB
it
Size
for
DataDomain
<
T
>
{
impl
<
T
:
RegisterDomain
>
HasB
yte
Size
for
DataDomain
<
T
>
{
// Return the bitsize of `self`.
fn
b
itsize
(
&
self
)
->
Bit
Size
{
fn
b
ytesize
(
&
self
)
->
Byte
Size
{
use
DataDomain
::
*
;
match
self
{
Top
(
size
)
=>
*
size
,
Pointer
(
pointer
)
=>
pointer
.b
it
size
(),
Value
(
bitvec
)
=>
bitvec
.b
it
size
(),
Pointer
(
pointer
)
=>
pointer
.b
yte
size
(),
Value
(
bitvec
)
=>
bitvec
.b
yte
size
(),
}
}
}
...
...
@@ -77,14 +77,14 @@ impl<T: RegisterDomain> HasBitSize for DataDomain<T> {
impl
<
T
:
RegisterDomain
>
HasTop
for
DataDomain
<
T
>
{
// Generate a new *Top* element with the same bitsize as `self`.
fn
top
(
&
self
)
->
Self
{
DataDomain
::
new_top
(
self
.b
it
size
())
DataDomain
::
new_top
(
self
.b
yte
size
())
}
}
impl
<
T
:
RegisterDomain
>
RegisterDomain
for
DataDomain
<
T
>
{
// Return a new *Top* element with the given b
it
size
fn
new_top
(
b
itsize
:
Bit
Size
)
->
Self
{
Self
::
Top
(
b
it
size
)
// Return a new *Top* element with the given b
yte
size
fn
new_top
(
b
ytesize
:
Byte
Size
)
->
Self
{
Self
::
Top
(
b
yte
size
)
}
/// Compute the (abstract) result of a binary operation
...
...
@@ -93,40 +93,58 @@ impl<T: RegisterDomain> RegisterDomain for DataDomain<T> {
use
DataDomain
::
*
;
match
(
self
,
op
,
rhs
)
{
(
Value
(
left
),
_
,
Value
(
right
))
=>
Value
(
left
.bin_op
(
op
,
right
)),
(
Pointer
(
pointer
),
PLUS
,
Value
(
value
))
|
(
Value
(
value
),
PLUS
,
Pointer
(
pointer
))
=>
{
(
Pointer
(
pointer
),
IntAdd
,
Value
(
value
))
|
(
Value
(
value
),
IntAdd
,
Pointer
(
pointer
))
=>
{
Pointer
(
pointer
.add_to_offset
(
value
))
}
(
Pointer
(
pointer
),
MINUS
,
Value
(
value
))
=>
Pointer
(
pointer
.sub_from_offset
(
value
)),
(
Pointer
(
pointer_lhs
),
MINUS
,
Pointer
(
pointer_rhs
))
=>
{
(
Pointer
(
pointer
),
IntSub
,
Value
(
value
))
=>
Pointer
(
pointer
.sub_from_offset
(
value
)),
(
Pointer
(
pointer_lhs
),
IntSub
,
Pointer
(
pointer_rhs
))
=>
{
if
pointer_lhs
.ids
()
.len
()
==
1
&&
pointer_rhs
.ids
()
.len
()
==
1
{
let
(
id_lhs
,
offset_lhs
)
=
pointer_lhs
.targets
()
.iter
()
.next
()
.unwrap
();
let
(
id_rhs
,
offset_rhs
)
=
pointer_rhs
.targets
()
.iter
()
.next
()
.unwrap
();
if
id_lhs
==
id_rhs
{
Self
::
Value
(
offset_lhs
.bin_op
(
MINUS
,
offset_rhs
))
Self
::
Value
(
offset_lhs
.bin_op
(
IntSub
,
offset_rhs
))
}
else
{
Self
::
Top
(
self
.b
it
size
())
Self
::
Top
(
self
.b
yte
size
())
}
}
else
{
// We cannot be sure that both pointers point to the same target
Self
::
Top
(
self
.b
it
size
())
Self
::
Top
(
self
.b
yte
size
())
}
}
(
_
,
EQ
,
_
)
|
(
_
,
NEQ
,
_
)
|
(
_
,
LT
,
_
)
|
(
_
,
LE
,
_
)
|
(
_
,
SLT
,
_
)
|
(
_
,
SLE
,
_
)
=>
{
T
::
new_top
(
1
)
.into
()
}
(
_
,
PLUS
,
_
)
|
(
_
,
MINUS
,
_
)
|
(
_
,
TIMES
,
_
)
|
(
_
,
DIVIDE
,
_
)
|
(
_
,
SDIVIDE
,
_
)
|
(
_
,
MOD
,
_
)
|
(
_
,
SMOD
,
_
)
|
(
_
,
LSHIFT
,
_
)
|
(
_
,
RSHIFT
,
_
)
|
(
_
,
ARSHIFT
,
_
)
|
(
_
,
AND
,
_
)
|
(
_
,
OR
,
_
)
|
(
_
,
XOR
,
_
)
=>
Self
::
new_top
(
self
.bitsize
()),
(
_
,
IntEqual
,
_
)
|
(
_
,
IntNotEqual
,
_
)
|
(
_
,
IntLess
,
_
)
|
(
_
,
IntLessEqual
,
_
)
|
(
_
,
IntSLess
,
_
)
|
(
_
,
IntSLessEqual
,
_
)
|
(
_
,
IntCarry
,
_
)
|
(
_
,
IntSCarry
,
_
)
|
(
_
,
IntSBorrow
,
_
)
|
(
_
,
BoolXOr
,
_
)
|
(
_
,
BoolOr
,
_
)
|
(
_
,
BoolAnd
,
_
)
|
(
_
,
FloatEqual
,
_
)
|
(
_
,
FloatNotEqual
,
_
)
|
(
_
,
FloatLess
,
_
)
|
(
_
,
FloatLessEqual
,
_
)
=>
T
::
new_top
(
ByteSize
::
new
(
1
))
.into
(),
(
_
,
IntAdd
,
_
)
|
(
_
,
IntSub
,
_
)
|
(
_
,
IntMult
,
_
)
|
(
_
,
IntDiv
,
_
)
|
(
_
,
IntSDiv
,
_
)
|
(
_
,
IntRem
,
_
)
|
(
_
,
IntSRem
,
_
)
|
(
_
,
IntLeft
,
_
)
|
(
_
,
IntRight
,
_
)
|
(
_
,
IntSRight
,
_
)
|
(
_
,
IntAnd
,
_
)
|
(
_
,
IntOr
,
_
)
|
(
_
,
IntXOr
,
_
)
|
(
_
,
FloatAdd
,
_
)
|
(
_
,
FloatSub
,
_
)
|
(
_
,
FloatMult
,
_
)
|
(
_
,
FloatDiv
,
_
)
=>
Self
::
new_top
(
self
.bytesize
()),
(
_
,
Piece
,
_
)
=>
Self
::
new_top
(
self
.bytesize
()
+
rhs
.bytesize
()),
}
}
...
...
@@ -135,41 +153,28 @@ impl<T: RegisterDomain> RegisterDomain for DataDomain<T> {
if
let
Self
::
Value
(
value
)
=
self
{
Self
::
Value
(
value
.un_op
(
op
))
}
else
{
Self
::
new_top
(
self
.b
it
size
())
Self
::
new_top
(
self
.b
yte
size
())
}
}
/// extract a sub-bitvector
fn
extract
(
&
self
,
low_bit
:
BitSize
,
high_bit
:
Bit
Size
)
->
Self
{
fn
subpiece
(
&
self
,
low_byte
:
ByteSize
,
size
:
Byte
Size
)
->
Self
{
if
let
Self
::
Value
(
value
)
=
self
{
Self
::
Value
(
value
.
extract
(
low_bit
,
high_bit
))
Self
::
Value
(
value
.
subpiece
(
low_byte
,
size
))
}
else
{
Self
::
new_top
(
high_bit
-
low_bit
+
1
)
Self
::
new_top
(
size
)
}
}
/// Extend or shrink a bitvector using the given cast type
fn
cast
(
&
self
,
kind
:
CastType
,
width
:
BitSize
)
->
Self
{
if
self
.bitsize
()
==
width
{
// The cast is a no-op.
return
self
.clone
();
}
/// Cast a bitvector using the given cast type
fn
cast
(
&
self
,
kind
:
CastOpType
,
width
:
ByteSize
)
->
Self
{
if
let
Self
::
Value
(
value
)
=
self
{
Self
::
Value
(
value
.cast
(
kind
,
width
))
}
else
{
// The result of
extending or shrink
ing pointers is undefined.
// The result of
cast
ing pointers is undefined.
Self
::
new_top
(
width
)
}
}
/// Concatenate two bitvectors.
fn
concat
(
&
self
,
other
:
&
Self
)
->
Self
{
if
let
(
Self
::
Value
(
upper_bits
),
Self
::
Value
(
lower_bits
))
=
(
self
,
other
)
{
Self
::
Value
(
upper_bits
.concat
(
lower_bits
))
}
else
{
Self
::
new_top
(
self
.bitsize
()
+
other
.bitsize
())
}
}
}
impl
<
T
:
RegisterDomain
>
AbstractDomain
for
DataDomain
<
T
>
{
...
...
@@ -177,10 +182,10 @@ impl<T: RegisterDomain> AbstractDomain for DataDomain<T> {
fn
merge
(
&
self
,
other
:
&
Self
)
->
Self
{
use
DataDomain
::
*
;
match
(
self
,
other
)
{
(
Top
(
b
itsize
),
_
)
|
(
_
,
Top
(
bitsize
))
=>
Top
(
*
bit
size
),
(
Top
(
b
ytesize
),
_
)
|
(
_
,
Top
(
bytesize
))
=>
Top
(
*
byte
size
),
(
Pointer
(
pointer1
),
Pointer
(
pointer2
))
=>
Pointer
(
pointer1
.merge
(
pointer2
)),
(
Value
(
val1
),
Value
(
val2
))
=>
Value
(
val1
.merge
(
val2
)),
(
Pointer
(
_
),
Value
(
_
))
|
(
Value
(
_
),
Pointer
(
_
))
=>
Top
(
self
.b
it
size
()),
(
Pointer
(
_
),
Value
(
_
))
|
(
Value
(
_
),
Pointer
(
_
))
=>
Top
(
self
.b
yte
size
()),
}
}
...
...
@@ -245,7 +250,7 @@ mod tests {
fn
new_id
(
name
:
&
str
)
->
AbstractIdentifier
{
AbstractIdentifier
::
new
(
Tid
::
new
(
"time0"
),
AbstractLocation
::
Register
(
name
.into
(),
64
),
AbstractLocation
::
Register
(
name
.into
(),
ByteSize
::
new
(
8
)
),
)
}
...
...
@@ -267,10 +272,10 @@ mod tests {
let
pointer
=
new_pointer
(
"Rax"
.into
(),
0
);
let
data
=
new_value
(
42
);
assert_eq!
(
pointer
.merge
(
&
pointer
),
pointer
);
assert_eq!
(
pointer
.merge
(
&
data
),
Data
::
new_top
(
64
));
assert_eq!
(
pointer
.merge
(
&
data
),
Data
::
new_top
(
ByteSize
::
new
(
8
)
));
assert_eq!
(
data
.merge
(
&
new_value
(
41
)),
Data
::
Value
(
BitvectorDomain
::
new_top
(
64
))
Data
::
Value
(
BitvectorDomain
::
new_top
(
ByteSize
::
new
(
8
)
))
);
let
other_pointer
=
new_pointer
(
"Rbx"
.into
(),
0
);
...
...
@@ -282,27 +287,30 @@ mod tests {
#[test]
fn
data_register_domain
()
{
use
crate
::
bil
::
BinOpType
::
*
;
use
BinOpType
::
*
;
let
data
=
new_value
(
42
);
assert_eq!
(
data
.b
itsize
(),
64
);
assert_eq!
(
data
.b
ytesize
(),
ByteSize
::
new
(
8
)
);
let
three
=
new_value
(
3
);
let
pointer
=
new_pointer
(
"Rax"
.into
(),
0
);
assert_eq!
(
data
.bin_op
(
PLUS
,
&
three
),
new_value
(
45
));
assert_eq!
(
pointer
.bin_op
(
PLUS
,
&
three
),
new_pointer
(
"Rax"
.into
(),
3
));
assert_eq!
(
three
.un_op
(
crate
::
bil
::
UnOpType
::
NEG
),
new_value
(
-
3
));
assert_eq!
(
data
.bin_op
(
IntAdd
,
&
three
),
new_value
(
45
));
assert_eq!
(
pointer
.bin_op
(
IntAdd
,
&
three
),
new_pointer
(
"Rax"
.into
(),
3
));
assert_eq!
(
three
.un_op
(
UnOpType
::
Int2Comp
),
new_value
(
-
3
));
assert_eq!
(
three
.
extract
(
0
,
31
),
three
.
subpiece
(
ByteSize
::
new
(
0
),
ByteSize
::
new
(
4
)
),
Data
::
Value
(
BitvectorDomain
::
Value
(
Bitvector
::
from_i32
(
3
)))
);
assert_eq!
(
data
.cast
(
crate
::
bil
::
CastType
::
SIGNED
,
128
)
.bitsize
(),
128
);
assert_eq!
(
data
.cast
(
CastOpType
::
IntSExt
,
ByteSize
::
new
(
16
))
.bytesize
(),
ByteSize
::
new
(
16
)
);
let
one
=
Data
::
Value
(
BitvectorDomain
::
Value
(
Bitvector
::
from_i32
(
1
)));
let
two
=
Data
::
Value
(
BitvectorDomain
::
Value
(
Bitvector
::
from_i32
(
2
)));
let
concat
=
new_value
((
1
<<
32
)
+
2
);
assert_eq!
(
one
.
concat
(
&
two
),
concat
);
assert_eq!
(
one
.
bin_op
(
Piece
,
&
two
),
concat
);
}
#[test]
...
...
cwe_checker_rs/src/abstract_domain/identifier.rs
View file @
af32d275
use
crate
::
bil
::
variable
::
*
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
prelude
::
*
;
use
derive_more
::
Deref
;
use
std
::
sync
::
Arc
;
...
...
@@ -57,7 +57,7 @@ impl std::fmt::Display for AbstractIdentifier {
/// It is also impossible to accidently describe circular references.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone,
PartialOrd,
Ord)]
pub
enum
AbstractLocation
{
Register
(
String
,
B
it
Size
),
Register
(
String
,
B
yte
Size
),
Pointer
(
String
,
AbstractMemoryLocation
),
}
...
...
@@ -81,7 +81,7 @@ impl AbstractLocation {
}
Ok
(
AbstractLocation
::
Register
(
variable
.name
.clone
(),
variable
.
bitsize
()
?
,
variable
.
size
,
))
}
}
...
...
cwe_checker_rs/src/abstract_domain/mem_region.rs
View file @
af32d275
use
super
::{
AbstractDomain
,
HasBitSize
,
HasTop
,
RegisterDomain
};
use
crate
::
bil
::{
BitSize
,
Bitvector
};
use
super
::{
AbstractDomain
,
HasByteSize
,
HasTop
,
RegisterDomain
};
use
crate
::
bil
::
Bitvector
;
use
crate
::
intermediate_representation
::
ByteSize
;
use
apint
::{
Int
,
Width
};
use
derive_more
::
Deref
;
use
serde
::{
Deserialize
,
Serialize
};
...
...
@@ -20,17 +21,17 @@ use std::sync::Arc;
/// 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
+
HasB
it
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
(
pub
struct
MemRegion
<
T
:
AbstractDomain
+
HasB
yte
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
(
Arc
<
MemRegionData
<
T
>>
,
);
impl
<
T
:
AbstractDomain
+
HasB
it
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
DerefMut
for
MemRegion
<
T
>
{
impl
<
T
:
AbstractDomain
+
HasB
yte
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
DerefMut
for
MemRegion
<
T
>
{
fn
deref_mut
(
&
mut
self
)
->
&
mut
MemRegionData
<
T
>
{
Arc
::
make_mut
(
&
mut
self
.
0
)
}
}
impl
<
T
:
AbstractDomain
+
HasB
it
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
AbstractDomain
impl
<
T
:
AbstractDomain
+
HasB
yte
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
AbstractDomain
for
MemRegion
<
T
>
{
/// Short-circuting the `MemRegionData::merge` function if `self==other`,
...
...
@@ -49,39 +50,39 @@ impl<T: AbstractDomain + HasBitSize + RegisterDomain + std::fmt::Debug> Abstract
}
}
impl
<
T
:
AbstractDomain
+
HasB
it
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
HasTop
for
MemRegion
<
T
>
{
/// Return a new, empty memory region with the same address b
it
size as `self`, representing the *Top* element of the abstract domain.
impl
<
T
:
AbstractDomain
+
HasB
yte
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
HasTop
for
MemRegion
<
T
>
{
/// Return a new, empty memory region with the same address b
yte
size as `self`, representing the *Top* element of the abstract domain.
fn
top
(
&
self
)
->
Self
{
Self
::
new
(
self
.get_address_b
it
size
())
Self
::
new
(
self
.get_address_b
yte
size
())
}
}
impl
<
T
:
AbstractDomain
+
HasB
it
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
MemRegion
<
T
>
{
impl
<
T
:
AbstractDomain
+
HasB
yte
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
MemRegion
<
T
>
{
// Create a new, empty memory region.
pub
fn
new
(
address_b
itsize
:
Bit
Size
)
->
Self
{
MemRegion
(
Arc
::
new
(
MemRegionData
::
new
(
address_b
it
size
)))
pub
fn
new
(
address_b
ytesize
:
Byte
Size
)
->
Self
{
MemRegion
(
Arc
::
new
(
MemRegionData
::
new
(
address_b
yte
size
)))
}
}
/// 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
+
HasB
it
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
{
address_b
itsize
:
Bit
Size
,
pub
struct
MemRegionData
<
T
:
AbstractDomain
+
HasB
yte
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
{
address_b
ytesize
:
Byte
Size
,
values
:
BTreeMap
<
i64
,
T
>
,
}
impl
<
T
:
AbstractDomain
+
HasB
it
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
MemRegionData
<
T
>
{
impl
<
T
:
AbstractDomain
+
HasB
yte
Size
+
RegisterDomain
+
std
::
fmt
::
Debug
>
MemRegionData
<
T
>
{
/// create a new, empty MemRegion
pub
fn
new
(
address_b
itsize
:
Bit
Size
)
->
MemRegionData
<
T
>
{
pub
fn
new
(
address_b
ytesize
:
Byte
Size
)
->
MemRegionData
<
T
>
{
MemRegionData
{
address_b
it
size
,
address_b
yte
size
,
values
:
BTreeMap
::
new
(),
}
}
/// Get the bitsize of pointers for the address space that the memory region belongs to.
pub
fn
get_address_b
itsize
(
&
self
)
->
Bit
Size
{
self
.address_b
it
size
pub
fn
get_address_b
ytesize
(
&
self
)
->
Byte
Size
{
self
.address_b
yte
size
}
/// Remove all elements intersecting the provided interval.
...
...
@@ -91,7 +92,7 @@ impl<T: AbstractDomain + HasBitSize + RegisterDomain + std::fmt::Debug> MemRegio
if
let
Some
((
prev_pos
,
prev_size
))
=
self
.values
.range
(
..
position
)
.map
(|(
pos
,
elem
)|
(
*
pos
,
elem
.bitsize
()
as
i64
/
8
))
.map
(|(
pos
,
elem
)|
(
*
pos
,
u64
::
from
(
elem
.bytesize
())
as
i64
))
.last
()
{
if
prev_pos
+
prev_size
>
position
{
...
...
@@ -111,10 +112,9 @@ impl<T: AbstractDomain + HasBitSize + RegisterDomain + std::fmt::Debug> MemRegio
/// Add a value to the memory region.
pub
fn
add
(
&
mut
self
,
value
:
T
,
position
:
Bitvector
)
{
assert_eq!
(
position
.width
()
.to_usize
(),
self
.address_bitsize
as
u
size
);
assert_eq!
(
ByteSize
::
from
(
position
.width
()),
self
.address_byte
size
);
let
position
=
Int
::
from
(
position
)
.try_to_i64
()
.unwrap
();
assert
!
(
value
.bitsize
()
%
8
==
0
);
let
size_in_bytes
=
value
.bitsize
()
as
i64
/
8
;
let
size_in_bytes
=
u64
::
from
(
value
.bytesize
())
as
i64
;
assert
!
(
size_in_bytes
>
0
);
self
.clear_interval
(
position
,
size_in_bytes
);
...
...
@@ -126,24 +126,21 @@ impl<T: AbstractDomain + HasBitSize + RegisterDomain + std::fmt::Debug> MemRegio
/// 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
:
u64
)
->
T
{
assert_eq!
(
position
.width
()
.to_usize
(),
self
.address_bitsize
as
u
size
);
pub
fn
get
(
&
self
,
position
:
Bitvector
,
size_in_bytes
:
ByteSize
)
->
T
{
assert_eq!
(
ByteSize
::
from
(
position
.width
()),
self
.address_byte
size
);
let
position
=
Int
::
from
(
position
)
.try_to_i64
()
.unwrap
();
let
size
=
size_in_bytes
as
i64
;
assert
!
(
size
>
0
);
if
let
Some
(
elem
)
=
self
.values
.get
(
&
position
)
{
if
(
elem
.bitsize
()
as
i64
)
==
(
size
*
8
)
{
if
elem
.bytesize
()
==
size_in_bytes
{
return
elem
.clone
();
}
}
let
bitsize
=
8
*
size
as
u16
;
T
::
new_top
(
bitsize
)
T
::
new_top
(
size_in_bytes
)
}
/// Remove all elements intersecting the provided interval.
pub
fn
remove
(
&
mut
self
,
position
:
Bitvector
,
size_in_bytes
:
Bitvector
)
{
assert_eq!
(
position
.width
()
.to_usize
(),
self
.address_bitsize
as
u
size
);
assert_eq!
(
ByteSize
::
from
(
position
.width
()),
self
.address_byte
size
);
let
position
=
Int
::
from
(
position
)
.try_to_i64
()
.unwrap
();
let
size
=
Int
::
from
(
size_in_bytes
)
.try_to_i64
()
.unwrap
();
assert
!
(
size
>
0
);
...
...
@@ -156,14 +153,14 @@ impl<T: AbstractDomain + HasBitSize + RegisterDomain + std::fmt::Debug> MemRegio
/// 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_b
itsize
,
other
.address_bit
size
);
assert_eq!
(
self
.address_b
ytesize
,
other
.address_byte
size
);
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
.b
itsize
()
==
elem_right
.bit
size
()
{
if
elem_left
.b
ytesize
()
==
elem_right
.byte
size
()
{
let
merged_val
=
elem_left
.merge
(
&
elem_right
);
if
!
merged_val
.is_top
()
{
// we discard top()-values, as they don't contain information
...
...
@@ -174,7 +171,7 @@ impl<T: AbstractDomain + HasBitSize + RegisterDomain + std::fmt::Debug> MemRegio
}
MemRegionData
{
address_b
itsize
:
self
.address_bit
size
,
address_b
ytesize
:
self
.address_byte
size
,
values
:
merged_values
,
}
}
...
...
@@ -221,9 +218,11 @@ impl<T: AbstractDomain + HasBitSize + RegisterDomain + std::fmt::Debug> MemRegio
#[cfg(test)]
mod
tests
{
use
super
::
*
;
use
crate
::
bil
::
Bitvector
;
use
crate
::
intermediate_representation
::
*
;
#[derive(PartialEq,
Eq,
Clone,
Copy,
Debug,
Hash,
PartialOrd,
Ord)]
struct
MockDomain
(
i64
,
B
it
Size
);
struct
MockDomain
(
i64
,
B
yte
Size
);
impl
AbstractDomain
for
MockDomain
{
fn
merge
(
&
self
,
other
:
&
Self
)
->
Self
{
...
...
@@ -240,8 +239,8 @@ mod tests {
}
}
impl
HasB
it
Size
for
MockDomain
{
fn
b
itsize
(
&
self
)
->
Bit
Size
{
impl
HasB
yte
Size
for
MockDomain
{
fn
b
ytesize
(
&
self
)
->
Byte
Size
{
self
.
1
}
}
...
...
@@ -253,33 +252,29 @@ mod tests {
}
impl
RegisterDomain
for
MockDomain
{
fn
new_top
(
b
itsize
:
Bit
Size
)
->
MockDomain
{
MockDomain
(
0
,
b
it
size
)
fn
new_top
(
b
ytesize
:
Byte
Size
)
->
MockDomain
{
MockDomain
(
0
,
b
yte
size
)
}
fn
bin_op
(
&
self
,
_op
:
crate
::
bil
::
BinOpType
,
_rhs
:
&
Self
)
->
Self
{
fn
bin_op
(
&
self
,
_op
:
BinOpType
,
_rhs
:
&
Self
)
->
Self
{
Self
::
new_top
(
self
.
1
)
}
fn
un_op
(
&
self
,
_op
:
crate
::
bil
::
UnOpType
)
->
Self
{
fn
un_op
(
&
self
,
_op
:
UnOpType
)
->
Self
{
Self
::
new_top
(
self
.
1
)
}
fn
cast
(
&
self
,
_kind
:
crate
::
bil
::
CastType
,
width
:
Bit
Size
)
->
Self
{
fn
cast
(
&
self
,
_kind
:
CastOpType
,
width
:
Byte
Size
)
->
Self
{
Self
::
new_top
(
width
)
}
fn
extract
(
&
self
,
low_bit
:
BitSize
,
high_bit
:
BitSize
)
->
Self
{
Self
::
new_top
(
high_bit
-
low_bit
+
1
)
}
fn
concat
(
&
self
,
other
:
&
Self
)
->
Self
{
Self
::
new_top
(
self
.bitsize
()
+
other
.bitsize
())
fn
subpiece
(
&
self
,
_low_byte
:
ByteSize
,
size
:
ByteSize
)
->
Self
{
Self
::
new_top
(
size
)
}
}
fn
mock
(
val
:
i64
,
b
itsize
:
BitSize
)
->
MockDomain
{
MockDomain
(
val
,
b
itsize
)
fn
mock
(
val
:
i64
,
b
ytesize
:
impl
Into
<
ByteSize
>
)
->
MockDomain
{
MockDomain
(
val
,
b
ytesize
.into
()
)
}
fn
bv
(
val
:
i64
)
->
Bitvector
{
...
...
@@ -288,41 +283,56 @@ mod tests {
#[test]
fn
mem_region
()
{
let
mut
region
:
MemRegion
<
MockDomain
>
=
MemRegion
::
new
(
64
);
region
.add
(
mock
(
5
,
3
*
8
),
bv
(
5
));
assert_eq!
(
region
.get
(
bv
(
5
),
3
),
mock
(
5
,
3
*
8
));
region
.add
(
mock
(
7
,
2
*
8
),
bv
(
8
));
assert_eq!
(
region
.get
(
bv
(
8
),
2
),
mock
(
7
,
2
*
8
));
assert_eq!
(
region
.get
(
bv
(
5
),
3
),
mock
(
5
,
3
*
8
));
assert_eq!
(
region
.get
(
bv
(
5
),
2
),
MockDomain
::
new_top
(
2
*
8
));
region
.add
(
mock
(
9
,
2
*
8
),
bv
(
6
));
assert_eq!
(
region
.get
(
bv
(
6
),
2
),
mock
(
9
,
2
*
8
));
assert_eq!
(
region
.get
(
bv
(
5
),
3
),
MockDomain
::
new_top
(
3
*
8
));
assert_eq!
(
region
.get
(
bv
(
8
),
2
),
mock
(
7
,
2
*
8
));
region
.add
(
mock
(
9
,
11
*
8
),
bv
(
-
3
));
assert_eq!
(
region
.get
(
bv
(
-
3
),
11
),
mock
(
9
,
11
*
8
));
assert_eq!
(
region
.get
(
bv
(
6
),
2
),
MockDomain
::
new_top
(
2
*
8
));
assert_eq!
(
region
.get
(
bv
(
8
),
2
),
mock
(
7
,
2
*
8
));
let
mut
other_region
=
MemRegion
::
new
(
64
);
other_region
.add
(
mock
(
7
,
2
*
8
),
bv
(
8
));
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
),
2
),
mock
(
7
,
2
*
8
));
assert_eq!
(
merged_region
.get
(
bv
(
-
3
),
11
),
MockDomain
::
new_top
(
11
*
8
));
other_region
.add
(
mock
(
9
,
11
*
8
),
bv
(
-
3
));
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
(
64
);
region
.add
(
MockDomain
::
new_top
(
4
*
8
),
bv
(
5
));
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
(
64
);
region
.add
(
mock
(
5
,
4
*
8
),
bv
(
5
));
other_region
.add
(
mock
(
7
,
4
*
8
),
bv
(
5
));
let
mut
other_region
:
MemRegionData
<
MockDomain
>
=
MemRegionData
::
new
(
ByteSize
::
from
(
8u64
)
);
region
.add
(
mock
(
5
,
4
u64
),
bv
(
5
));
other_region
.add
(
mock
(
7
,
4
u64
),
bv
(
5
));
let
merged_region
=
region
.merge
(
&
other_region
);
assert_eq!
(
region
.values
.len
(),
1
);
assert_eq!
(
other_region
.values
.len
(),
1
);
...
...
@@ -331,12 +341,12 @@ mod tests {
#[test]
fn
value_removals
()
{
let
mut
region
:
MemRegionData
<
MockDomain
>
=
MemRegionData
::
new
(
64
);
region
.add
(
mock
(
1
,
64
),
bv
(
0
));
region
.add
(
mock
(
2
,
64
),
bv
(
8
));
region
.add
(
mock
(
3
,
64
),
bv
(
16
));
region
.add
(
mock
(
4
,
64
),
bv
(
24
));
region
.add
(
mock
(
5
,
64
),
bv
(
32
));
let
mut
region
:
MemRegionData
<
MockDomain
>
=
MemRegionData
::
new
(
ByteSize
::
from
(
8u64
)
);
region
.add
(
mock
(
1
,
8u
64
),
bv
(
0
));
region
.add
(
mock
(
2
,
8u
64
),
bv
(
8
));
region
.add
(
mock
(
3
,
8u
64
),
bv
(
16
));
region
.add
(
mock
(
4
,
8u
64
),
bv
(
24
));
region
.add
(
mock
(
5
,
8u
64
),
bv
(
32
));
assert_eq!
(
region
.values
.len
(),
5
);
region
.remove
(
bv
(
2
),
bv
(
3
));
...
...
@@ -352,12 +362,12 @@ mod tests {
assert_eq!
(
region
.values
.len
(),
2
);
for
val
in
region
.values_mut
()
{
if
*
val
==
mock
(
5
,
64
)
{
*
val
=
mock
(
0
,
64
);
// This is a *Top* element
if
*
val
==
mock
(
5
,
8u
64
)
{
*
val
=
mock
(
0
,
8u
64
);
// This is a *Top* element
}
}
region
.clear_top_values
();
assert_eq!
(
region
.values
.len
(),
1
);
assert_eq!
(
region
.get
(
bv
(
24
),
8
),
mock
(
4
,
64
));
assert_eq!
(
region
.get
(
bv
(
24
),
ByteSize
::
from
(
8u64
)),
mock
(
4
,
8u
64
));
}
}
cwe_checker_rs/src/abstract_domain/mod.rs
View file @
af32d275
//! This module defines traits describing general properties of abstract domains
//! as well as several abstract domain types implementing these traits.
use
crate
::
bil
::
*
;
use
crate
::
intermediate_representation
::
*
;
mod
bitvector
;
pub
use
bitvector
::
*
;
...
...
@@ -29,14 +29,14 @@ pub trait AbstractDomain: Sized + Eq + Clone {
fn
is_top
(
&
self
)
->
bool
;
}
/// A trait for types representing values with a fixed size (in b
it
s).
/// A trait for types representing values with a fixed size (in b
yte
s).
///
/// For abstract domains, the b
it
size is a parameter of the domain itself,
/// i.e. you cannot merge values of different b
it
sizes,
/// since they lie in different posets (one for each b
it
size).
pub
trait
HasB
it
Size
{
/// Return the size of the represented value in b
it
s.
fn
b
itsize
(
&
self
)
->
Bit
Size
;
/// For abstract domains, the b
yte
size is a parameter of the domain itself,
/// i.e. you cannot merge values of different b
yte
sizes,
/// since they lie in different posets (one for each b
yte
size).
pub
trait
HasB
yte
Size
{
/// Return the size of the represented value in b
yte
s.
fn
b
ytesize
(
&
self
)
->
Byte
Size
;
}
/// An abstract domain implementing this trait has a global maximum, i.e. a *Top* element.
...
...
@@ -52,11 +52,11 @@ pub trait HasTop {
/// A trait for abstract domains that can represent values loaded into CPU register.
///
/// The domain implements all general operations used to manipulate register values.
/// The domain is parametrized by its b
it
size (which represents the size of the register).
/// It has a *Top* element, which is only characterized by its b
it
size.
pub
trait
RegisterDomain
:
AbstractDomain
+
HasB
it
Size
+
HasTop
{
/// Return a new top element with the given b
it
size
fn
new_top
(
b
itsize
:
Bit
Size
)
->
Self
;
/// The domain is parametrized by its b
yte
size (which represents the size of the register).
/// It has a *Top* element, which is only characterized by its b
yte
size.
pub
trait
RegisterDomain
:
AbstractDomain
+
HasB
yte
Size
+
HasTop
{
/// Return a new top element with the given b
yte
size
fn
new_top
(
b
ytesize
:
Byte
Size
)
->
Self
;
/// Compute the (abstract) result of a binary operation
fn
bin_op
(
&
self
,
op
:
BinOpType
,
rhs
:
&
Self
)
->
Self
;
...
...
@@ -64,12 +64,9 @@ pub trait RegisterDomain: AbstractDomain + HasBitSize + HasTop {
/// Compute the (abstract) result of a unary operation
fn
un_op
(
&
self
,
op
:
UnOpType
)
->
Self
;
///
e
xtract a sub-bitvector
fn
extract
(
&
self
,
low_bit
:
BitSize
,
high_bit
:
Bit
Size
)
->
Self
;
///
E
xtract a sub-bitvector
fn
subpiece
(
&
self
,
low_byte
:
ByteSize
,
size
:
Byte
Size
)
->
Self
;
/// Extend a bitvector using the given cast type
fn
cast
(
&
self
,
kind
:
CastType
,
width
:
BitSize
)
->
Self
;
/// Concatenate two bitvectors
fn
concat
(
&
self
,
other
:
&
Self
)
->
Self
;
/// Perform a typecast to extend a bitvector or to cast between integer and floating point types.
fn
cast
(
&
self
,
kind
:
CastOpType
,
width
:
ByteSize
)
->
Self
;
}
cwe_checker_rs/src/abstract_domain/pointer.rs
View file @
af32d275
use
super
::{
AbstractDomain
,
AbstractIdentifier
,
HasB
it
Size
,
RegisterDomain
};
use
crate
::
bil
::
BinOpType
;
use
super
::{
AbstractDomain
,
AbstractIdentifier
,
HasB
yte
Size
,
RegisterDomain
};
use
crate
::
intermediate_representation
::{
BinOpType
,
ByteSize
}
;
use
crate
::
prelude
::
*
;
use
std
::
collections
::
BTreeMap
;
use
std
::
fmt
::
Display
;
...
...
@@ -38,15 +38,15 @@ impl<T: RegisterDomain> AbstractDomain for PointerDomain<T> {
}
}
impl
<
T
:
RegisterDomain
>
HasB
it
Size
for
PointerDomain
<
T
>
{
impl
<
T
:
RegisterDomain
>
HasB
yte
Size
for
PointerDomain
<
T
>
{
/// Return the bitsize of the pointer.
/// Should always equal the pointer size of the CPU architecture.
fn
b
itsize
(
&
self
)
->
Bit
Size
{
fn
b
ytesize
(
&
self
)
->
Byte
Size
{
self
.
0
.values
()
.next
()
.expect
(
"Pointer without targets encountered"
)
.b
it
size
()
.b
yte
size
()
}
}
...
...
@@ -84,7 +84,7 @@ impl<T: RegisterDomain> PointerDomain<T> {
offset_adjustment
:
&
T
,
)
{
if
let
Some
(
old_offset
)
=
self
.
0
.get
(
&
old_id
)
{
let
new_offset
=
old_offset
.bin_op
(
BinOpType
::
PLUS
,
offset_adjustment
);
let
new_offset
=
old_offset
.bin_op
(
BinOpType
::
IntAdd
,
offset_adjustment
);
self
.
0
.remove
(
old_id
);
self
.
0
.insert
(
new_id
.clone
(),
new_offset
);
}
...
...
@@ -94,7 +94,7 @@ impl<T: RegisterDomain> PointerDomain<T> {
pub
fn
add_to_offset
(
&
self
,
value
:
&
T
)
->
PointerDomain
<
T
>
{
let
mut
result
=
self
.clone
();
for
offset
in
result
.
0
.values_mut
()
{
*
offset
=
offset
.bin_op
(
BinOpType
::
PLUS
,
value
);
*
offset
=
offset
.bin_op
(
BinOpType
::
IntAdd
,
value
);
}
result
}
...
...
@@ -103,7 +103,7 @@ impl<T: RegisterDomain> PointerDomain<T> {
pub
fn
sub_from_offset
(
&
self
,
value
:
&
T
)
->
PointerDomain
<
T
>
{
let
mut
result
=
self
.clone
();
for
offset
in
result
.
0
.values_mut
()
{
*
offset
=
offset
.bin_op
(
BinOpType
::
MINUS
,
value
);
*
offset
=
offset
.bin_op
(
BinOpType
::
IntSub
,
value
);
}
result
}
...
...
@@ -149,7 +149,7 @@ mod tests {
fn
new_id
(
name
:
&
str
)
->
AbstractIdentifier
{
AbstractIdentifier
::
new
(
Tid
::
new
(
"time0"
),
AbstractLocation
::
Register
(
name
.into
(),
64
),
AbstractLocation
::
Register
(
name
.into
(),
ByteSize
::
new
(
8
)
),
)
}
...
...
cwe_checker_rs/src/analysis/graph.rs
View file @
af32d275
...
...
@@ -39,8 +39,8 @@
//! The artificial *CallReturn* nodes enable enriching the information flowing through a return edge
//! with information recovered from the corresponding callsite during a fixpoint computation.
use
crate
::
intermediate_representation
::
*
;
use
crate
::
prelude
::
*
;
use
crate
::
term
::
*
;
use
petgraph
::
graph
::{
DiGraph
,
NodeIndex
};
use
std
::
collections
::{
HashMap
,
HashSet
};
...
...
@@ -166,47 +166,55 @@ impl<'a> GraphBuilder<'a> {
jump
:
&
'a
Term
<
Jmp
>
,
untaken_conditional
:
Option
<&
'a
Term
<
Jmp
>>
,
)
{
match
&
jump
.term.kind
{
JmpKind
::
Goto
(
Label
::
Direct
(
tid
))
=>
{
match
&
jump
.term
{
Jmp
::
Branch
(
tid
)
|
Jmp
::
CBranch
{
target
:
tid
,
condition
:
_
,
}
=>
{
self
.graph
.add_edge
(
source
,
self
.jump_targets
[
&
tid
]
.
0
,
Edge
::
Jump
(
jump
,
untaken_conditional
),
);
}
JmpKind
::
Goto
(
Label
::
Indirect
(
_
))
=>
(),
// TODO: add handling of indirect edges!
JmpKind
::
Call
(
ref
call
)
=>
{
if
let
Label
::
Direct
(
ref
target_tid
)
=
call
.target
{
if
self
.extern_subs
.contains
(
target_tid
)
{
if
let
Some
(
Label
::
Direct
(
ref
return_tid
))
=
call
.return_
{
self
.graph
.add_edge
(
source
,
self
.jump_targets
[
&
return_tid
]
.
0
,
Edge
::
ExternCallStub
(
jump
),
);
}
}
else
{
if
let
Some
(
target
)
=
self
.jump_targets
.get
(
&
target_tid
)
{
self
.graph
.add_edge
(
source
,
target
.
0
,
Edge
::
Call
(
jump
));
}
if
let
Some
(
Label
::
Direct
(
ref
return_tid
))
=
call
.return_
{
let
return_index
=
self
.jump_targets
[
return_tid
]
.
0
;
self
.return_addresses
.entry
(
target_tid
.clone
())
.and_modify
(|
vec
|
vec
.push
((
source
,
return_index
)))
.or_insert_with
(||
vec!
[(
source
,
return_index
)]);
}
// TODO: Non-returning calls and tail calls both have no return target in BAP.
// Thus we need to distinguish them somehow to correctly handle tail calls.
Jmp
::
BranchInd
(
_
)
=>
(),
// TODO: add handling of indirect edges!
Jmp
::
Call
{
target
,
return_
}
=>
{
if
self
.extern_subs
.contains
(
target
)
{
if
let
Some
(
return_tid
)
=
return_
{
self
.graph
.add_edge
(
source
,
self
.jump_targets
[
&
return_tid
]
.
0
,
Edge
::
ExternCallStub
(
jump
),
);
}
}
else
{
if
let
Some
(
target_node
)
=
self
.jump_targets
.get
(
&
target
)
{
self
.graph
.add_edge
(
source
,
target_node
.
0
,
Edge
::
Call
(
jump
));
}
// TODO: Log message for the else-case?
if
let
Some
(
ref
return_tid
)
=
return_
{
let
return_index
=
self
.jump_targets
[
return_tid
]
.
0
;
self
.return_addresses
.entry
(
target
.clone
())
.and_modify
(|
vec
|
vec
.push
((
source
,
return_index
)))
.or_insert_with
(||
vec!
[(
source
,
return_index
)]);
}
}
}
JmpKind
::
Interrupt
{
value
:
_
,
return_addr
:
_
,
}
=>
(),
// TODO: Add some handling for interrupts
JmpKind
::
Return
(
_
)
=>
{}
// return edges are handled in a different function
Jmp
::
CallInd
{
target
:
_
,
return_
:
_
,
}
=>
{
// TODO: add handling of indirect calls!
}
Jmp
::
CallOther
{
description
:
_
,
return_
:
_
,
}
=>
{
// TODO: Decide how to represent CallOther edges.
// Right now they are dead ends in the control flow graph.
}
Jmp
::
Return
(
_
)
=>
{}
// return edges are handled in a different function
}
}
...
...
@@ -244,7 +252,7 @@ impl<'a> GraphBuilder<'a> {
.term
.jmps
.iter
()
.find
(|
jump
|
matches!
(
jump
.term
.kind
,
JmpKind
::
Call
(
_
)
))
.find
(|
jump
|
matches!
(
jump
.term
,
Jmp
::
Call
{
..
}
))
.unwrap
();
let
cr_combine_node
=
self
.graph
.add_node
(
Node
::
CallReturn
{
call
:
call_block
,
...
...
@@ -267,7 +275,7 @@ impl<'a> GraphBuilder<'a> {
.term
.jmps
.iter
()
.any
(|
jmp
|
matches!
(
jmp
.term
.kind
,
JmpKind
::
Return
(
_
)))
.any
(|
jmp
|
matches!
(
jmp
.term
,
Jmp
::
Return
(
_
)))
{
let
return_from_node
=
self
.jump_targets
[
&
block
.tid
]
.
1
;
self
.add_call_return_node_and_edges
(
sub
,
return_from_node
);
...
...
@@ -326,29 +334,18 @@ mod tests {
use
super
::
*
;
fn
mock_program
()
->
Term
<
Program
>
{
use
Label
::
*
;
let
call
=
Call
{
target
:
Direct
(
Tid
::
new
(
"sub2"
)),
return_
:
Some
(
Direct
(
Tid
::
new
(
"sub1_blk2"
))),
};
let
call_term
=
Term
{
tid
:
Tid
::
new
(
"call"
.to_string
()),
term
:
Jmp
{
condition
:
None
,
kind
:
JmpKind
::
Call
(
call
),
term
:
Jmp
::
Call
{
target
:
Tid
::
new
(
"sub2"
)
,
return_
:
Some
(
Tid
::
new
(
"sub1_blk2"
)
),
},
};
let
return_term
=
Term
{
tid
:
Tid
::
new
(
"return"
.to_string
()),
term
:
Jmp
{
condition
:
None
,
kind
:
JmpKind
::
Return
(
Direct
(
Tid
::
new
(
"sub1_blk2"
))),
},
};
let
jmp
=
Jmp
{
condition
:
None
,
kind
:
JmpKind
::
Goto
(
Direct
(
Tid
::
new
(
"sub1_blk1"
))),
term
:
Jmp
::
Return
(
Expression
::
Const
(
Bitvector
::
zero
(
64
.into
()))),
// The return term does not matter
};
let
jmp
=
Jmp
::
Branch
(
Tid
::
new
(
"sub1_blk1"
));
let
jmp_term
=
Term
{
tid
:
Tid
::
new
(
"jump"
),
term
:
jmp
,
...
...
cwe_checker_rs/src/analysis/interprocedural_fixpoint.rs
View file @
af32d275
...
...
@@ -13,9 +13,8 @@
use
super
::
fixpoint
::
Context
as
GeneralFPContext
;
use
super
::
graph
::
*
;
use
crate
::
bil
::
Expression
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
prelude
::
*
;
use
crate
::
term
::
*
;
use
fnv
::
FnvHashMap
;
use
petgraph
::
graph
::{
EdgeIndex
,
NodeIndex
};
use
std
::
marker
::
PhantomData
;
...
...
cwe_checker_rs/src/analysis/pointer_inference/context/mod.rs
View file @
af32d275
use
super
::
object
::
ObjectType
;
use
crate
::
abstract_domain
::
*
;
use
crate
::
analysis
::
graph
::
Graph
;
use
crate
::
bil
::
Expression
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
prelude
::
*
;
use
crate
::
term
::
symbol
::
ExternSymbol
;
use
crate
::
term
::
*
;
use
crate
::
utils
::
log
::
*
;
use
std
::
collections
::{
BTreeMap
,
BTreeSet
,
HashSet
};
...
...
@@ -84,10 +82,10 @@ impl<'a> Context<'a> {
return_term
:
&
Term
<
Jmp
>
,
)
{
let
expected_stack_pointer_offset
=
match
self
.project.cpu_architecture
.as_str
()
{
"x86"
|
"x86_64"
=>
Bitvector
::
from_u
16
(
self
.project
.get_pointer_bitsize
()
/
8
)
.into_
zero_extend
(
self
.project
.get_pointer_bitsize
()
as
usize
)
"x86"
|
"x86_64"
=>
Bitvector
::
from_u
64
(
u64
::
from
(
self
.project
.get_pointer_bytesize
())
)
.into_
truncate
(
apint
::
BitWidth
::
from
(
self
.project
.get_pointer_bytesize
())
)
.unwrap
(),
_
=>
Bitvector
::
zero
(
(
self
.project
.get_pointer_bitsize
()
as
usize
)
.into
(
)),
_
=>
Bitvector
::
zero
(
apint
::
BitWidth
::
from
(
self
.project
.get_pointer_bytesize
()
)),
};
match
state_before_return
.get_register
(
&
self
.project.stack_pointer_register
)
{
Ok
(
Data
::
Pointer
(
pointer
))
=>
{
...
...
@@ -134,21 +132,18 @@ impl<'a> Context<'a> {
call
.tid
.clone
(),
AbstractLocation
::
from_var
(
return_register
)
.unwrap
(),
);
let
address_b
itsize
=
self
.project.stack_pointer_register
.bitsize
()
.unwrap
();
let
address_b
ytesize
=
self
.project
.get_pointer_bytesize
();
state
.memory
.add_abstract_object
(
object_id
.clone
(),
Bitvector
::
zero
(
(
address_bitsize
as
usize
)
.into
(
))
.into
(),
Bitvector
::
zero
(
apint
::
BitWidth
::
from
(
address_bytesize
))
.into
(),
super
::
object
::
ObjectType
::
Heap
,
address_b
it
size
,
address_b
yte
size
,
);
let
pointer
=
PointerDomain
::
new
(
object_id
,
Bitvector
::
zero
((
address_bitsize
as
usize
)
.into
())
.into
(),
);
self
.log_debug
(
state
.set_register
(
return_register
,
pointer
.into
()),
Some
(
&
call
.tid
),
Bitvector
::
zero
(
apint
::
BitWidth
::
from
(
address_bytesize
))
.into
(),
);
state
.set_register
(
return_register
,
pointer
.into
());
Some
(
state
)
}
Err
(
err
)
=>
{
...
...
@@ -159,6 +154,27 @@ impl<'a> Context<'a> {
}
}
/// Evaluate the value of a parameter of an extern symbol for the given state.
fn
eval_parameter_arg
(
&
self
,
state
:
&
State
,
parameter
:
&
Arg
)
->
Result
<
Data
,
Error
>
{
match
parameter
{
Arg
::
Register
(
var
)
=>
state
.eval
(
&
Expression
::
Var
(
var
.clone
())),
Arg
::
Stack
{
offset
,
size
}
=>
state
.load_value
(
&
Expression
::
BinOp
{
op
:
BinOpType
::
IntAdd
,
lhs
:
Box
::
new
(
Expression
::
Var
(
self
.project.stack_pointer_register
.clone
())),
rhs
:
Box
::
new
(
Expression
::
Const
(
Bitvector
::
from_i64
(
*
offset
)
.into_truncate
(
apint
::
BitWidth
::
from
(
self
.project
.get_pointer_bytesize
(),
))
.unwrap
(),
)),
},
*
size
,
),
}
}
/// Mark the object that the parameter of a call is pointing to as freed.
/// If the object may have been already freed, generate a CWE warning.
/// This models the behaviour of `free` and similar functions.
...
...
@@ -170,43 +186,46 @@ impl<'a> Context<'a> {
extern_symbol
:
&
ExternSymbol
,
)
->
Option
<
State
>
{
match
extern_symbol
.get_unique_parameter
()
{
Ok
(
parameter_expression
)
=>
match
state
.eval
(
parameter_expression
)
{
Ok
(
memory_object_pointer
)
=>
{
if
let
Data
::
Pointer
(
pointer
)
=
memory_object_pointer
{
if
let
Err
(
possible_double_frees
)
=
new_state
.mark_mem_object_as_freed
(
&
pointer
)
{
let
warning
=
CweWarning
{
name
:
"CWE415"
.to_string
(),
version
:
VERSION
.to_string
(),
addresses
:
vec!
[
call
.tid.address
.clone
()],
tids
:
vec!
[
format!
(
"{}"
,
call
.tid
)],
symbols
:
Vec
::
new
(),
other
:
vec!
[
possible_double_frees
.into_iter
()
.map
(|(
id
,
err
)|
format!
(
"{}: {}"
,
id
,
err
))
.collect
()],
description
:
format!
(
"(Double Free) Object may have been freed before at {}"
,
call
.tid.address
),
};
self
.cwe_collector
.send
(
warning
)
.unwrap
();
Ok
(
parameter
)
=>
{
let
parameter_value
=
self
.eval_parameter_arg
(
state
,
parameter
);
match
parameter_value
{
Ok
(
memory_object_pointer
)
=>
{
if
let
Data
::
Pointer
(
pointer
)
=
memory_object_pointer
{
if
let
Err
(
possible_double_frees
)
=
new_state
.mark_mem_object_as_freed
(
&
pointer
)
{
let
warning
=
CweWarning
{
name
:
"CWE415"
.to_string
(),
version
:
VERSION
.to_string
(),
addresses
:
vec!
[
call
.tid.address
.clone
()],
tids
:
vec!
[
format!
(
"{}"
,
call
.tid
)],
symbols
:
Vec
::
new
(),
other
:
vec!
[
possible_double_frees
.into_iter
()
.map
(|(
id
,
err
)|
format!
(
"{}: {}"
,
id
,
err
))
.collect
()],
description
:
format!
(
"(Double Free) Object may have been freed before at {}"
,
call
.tid.address
),
};
self
.cwe_collector
.send
(
warning
)
.unwrap
();
}
}
else
{
self
.log_debug
(
Err
(
anyhow!
(
"Free on a non-pointer value called."
)),
Some
(
&
call
.tid
),
);
}
}
else
{
self
.log_debug
(
Err
(
anyhow!
(
"Free on a non-pointer value called."
)),
Some
(
&
call
.tid
),
);
new_state
.remove_unreferenced_objects
();
Some
(
new_state
)
}
Err
(
err
)
=>
{
self
.log_debug
(
Err
(
err
),
Some
(
&
call
.tid
));
Some
(
new_state
)
}
new_state
.remove_unreferenced_objects
();
Some
(
new_state
)
}
Err
(
err
)
=>
{
self
.log_debug
(
Err
(
err
),
Some
(
&
call
.tid
));
Some
(
new_state
)
}
}
,
}
Err
(
err
)
=>
{
// We do not know which memory object to free
self
.log_debug
(
Err
(
err
),
Some
(
&
call
.tid
));
...
...
@@ -222,12 +241,8 @@ impl<'a> Context<'a> {
call
:
&
Term
<
Jmp
>
,
extern_symbol
:
&
ExternSymbol
,
)
{
for
argument
in
extern_symbol
.arguments
.iter
()
.filter
(|
arg
|
arg
.intent
.is_input
())
{
match
state
.eval
(
&
argument
.location
)
{
for
parameter
in
extern_symbol
.parameters
.iter
()
{
match
self
.eval_parameter_arg
(
state
,
parameter
)
{
Ok
(
value
)
=>
{
if
state
.memory
.is_dangling_pointer
(
&
value
,
true
)
{
let
warning
=
CweWarning
{
...
...
@@ -247,8 +262,8 @@ impl<'a> Context<'a> {
}
Err
(
err
)
=>
self
.log_debug
(
Err
(
err
.context
(
format!
(
"Function
argument expression
{:?} could not be evaluated"
,
argument
.location
"Function
parameter
{:?} could not be evaluated"
,
parameter
))),
Some
(
&
call
.tid
),
),
...
...
@@ -266,11 +281,11 @@ impl<'a> Context<'a> {
extern_symbol
:
&
ExternSymbol
,
)
->
Option
<
State
>
{
self
.log_debug
(
new_state
.clear_stack_parameter
(
extern_symbol
),
new_state
.clear_stack_parameter
(
extern_symbol
,
&
self
.project.stack_pointer_register
),
Some
(
&
call
.tid
),
);
let
mut
possible_referenced_ids
=
BTreeSet
::
new
();
if
extern_symbol
.
argument
s
.is_empty
()
{
if
extern_symbol
.
parameters
.is_empty
()
&&
extern_symbol
.return_value
s
.is_empty
()
{
// We assume here that we do not know the parameters and approximate them by all possible parameter registers.
// This approximation is wrong if the function is known but has neither parameters nor return values.
// We cannot distinguish these two cases yet.
...
...
@@ -280,12 +295,8 @@ impl<'a> Context<'a> {
}
}
}
else
{
for
parameter
in
extern_symbol
.arguments
.iter
()
.filter
(|
arg
|
arg
.intent
.is_input
())
{
if
let
Ok
(
data
)
=
state
.eval
(
&
parameter
.location
)
{
for
parameter
in
extern_symbol
.parameters
.iter
()
{
if
let
Ok
(
data
)
=
self
.eval_parameter_arg
(
state
,
parameter
)
{
possible_referenced_ids
.append
(
&
mut
data
.referenced_ids
());
}
}
...
...
@@ -312,13 +323,13 @@ impl<'a> Context<'a> {
if
*
stack_id
==
state
.stack_id
{
stack_offset_domain
.clone
()
}
else
{
BitvectorDomain
::
new_top
(
stack_pointer
.b
it
size
())
BitvectorDomain
::
new_top
(
stack_pointer
.b
yte
size
())
}
}
else
{
BitvectorDomain
::
new_top
(
self
.project.stack_pointer_register
.
bitsize
()
.unwrap
()
)
BitvectorDomain
::
new_top
(
self
.project.stack_pointer_register.
size
)
}
}
else
{
BitvectorDomain
::
new_top
(
self
.project.stack_pointer_register
.
bitsize
()
.unwrap
()
)
BitvectorDomain
::
new_top
(
self
.project.stack_pointer_register.
size
)
}
}
}
...
...
cwe_checker_rs/src/analysis/pointer_inference/context/tests.rs
View file @
af32d275
use
super
::
*
;
use
crate
::
bil
::
variable
::
*
;
fn
bv
(
value
:
i64
)
->
BitvectorDomain
{
BitvectorDomain
::
Value
(
Bitvector
::
from_i64
(
value
))
...
...
@@ -8,59 +7,51 @@ fn bv(value: i64) -> BitvectorDomain {
fn
new_id
(
time
:
&
str
,
reg_name
:
&
str
)
->
AbstractIdentifier
{
AbstractIdentifier
::
new
(
Tid
::
new
(
time
),
AbstractLocation
::
Register
(
reg_name
.to_string
(),
64
),
AbstractLocation
::
Register
(
reg_name
.to_string
(),
ByteSize
::
new
(
8
)
),
)
}
fn
mock_extern_symbol
(
name
:
&
str
)
->
ExternSymbol
{
use
crate
::
bil
;
let
arg
=
Arg
{
var
:
register
(
"RAX"
),
location
:
bil
::
Expression
::
Var
(
register
(
"RAX"
)),
intent
:
ArgIntent
::
Both
,
};
let
arg
=
Arg
::
Register
(
register
(
"RAX"
));
ExternSymbol
{
tid
:
Tid
::
new
(
"extern_"
.to_string
()
+
name
),
address
:
"somewhere"
.into
(),
name
:
name
.into
(),
calling_convention
:
None
,
arguments
:
vec!
[
arg
],
parameters
:
vec!
[
arg
.clone
()],
return_values
:
vec!
[
arg
],
no_return
:
false
,
}
}
fn
register
(
name
:
&
str
)
->
Variable
{
Variable
{
name
:
name
.into
(),
type_
:
crate
::
bil
::
variable
::
Type
::
Immediate
(
64
),
size
:
ByteSize
::
new
(
8
),
is_temp
:
false
,
}
}
fn
reg_add_term
(
name
:
&
str
,
value
:
i64
,
tid_name
:
&
str
)
->
Term
<
Def
>
{
let
add_expr
=
Expression
::
BinOp
{
op
:
crate
::
bil
::
BinOpType
::
PLUS
,
op
:
BinOpType
::
IntAdd
,
lhs
:
Box
::
new
(
Expression
::
Var
(
register
(
name
))),
rhs
:
Box
::
new
(
Expression
::
Const
(
Bitvector
::
from_i64
(
value
))),
};
Term
{
tid
:
Tid
::
new
(
format!
(
"{}"
,
tid_name
)),
term
:
Def
{
lhs
:
register
(
name
),
rhs
:
add_expr
,
term
:
Def
::
Assign
{
var
:
register
(
name
),
value
:
add_expr
,
},
}
}
fn
call_term
(
target_name
:
&
str
)
->
Term
<
Jmp
>
{
let
call
=
Call
{
target
:
Label
::
Direct
(
Tid
::
new
(
target_name
)),
return_
:
None
,
};
Term
{
tid
:
Tid
::
new
(
format!
(
"call_{}"
,
target_name
)),
term
:
Jmp
{
condition
:
None
,
kind
:
JmpKind
::
Call
(
call
)
,
term
:
Jmp
::
Call
{
target
:
Tid
::
new
(
target_name
)
,
return_
:
None
,
},
}
}
...
...
@@ -68,10 +59,10 @@ fn call_term(target_name: &str) -> Term<Jmp> {
fn
return_term
(
target_name
:
&
str
)
->
Term
<
Jmp
>
{
Term
{
tid
:
Tid
::
new
(
format!
(
"return"
)),
term
:
Jmp
{
condition
:
None
,
kind
:
JmpKind
::
Return
(
Label
::
Direct
(
Tid
::
new
(
target_name
))
),
},
term
:
Jmp
::
Return
(
Expression
::
Unknown
{
description
:
target_name
.into
()
,
size
:
ByteSize
::
new
(
8
),
}
)
,
}
}
...
...
@@ -102,7 +93,6 @@ fn mock_project() -> Project {
fn
context_problem_implementation
()
{
use
crate
::
analysis
::
interprocedural_fixpoint
::
Context
as
IpFpContext
;
use
crate
::
analysis
::
pointer_inference
::
Data
;
use
crate
::
bil
::
*
;
use
Expression
::
*
;
let
project
=
mock_project
();
...
...
@@ -113,10 +103,10 @@ fn context_problem_implementation() {
let
def
=
Term
{
tid
:
Tid
::
new
(
"def"
),
term
:
Def
{
lhs
:
register
(
"RSP"
),
rhs
:
BinOp
{
op
:
BinOpType
::
PLUS
,
term
:
Def
::
Assign
{
var
:
register
(
"RSP"
),
value
:
BinOp
{
op
:
BinOpType
::
IntAdd
,
lhs
:
Box
::
new
(
Var
(
register
(
"RSP"
))),
rhs
:
Box
::
new
(
Const
(
Bitvector
::
from_i64
(
-
16
))),
},
...
...
@@ -124,15 +114,9 @@ fn context_problem_implementation() {
};
let
store_term
=
Term
{
tid
:
Tid
::
new
(
"store"
),
term
:
Def
{
lhs
:
register
(
"memory"
),
// technically false, but not checked at the moment
rhs
:
Store
{
address
:
Box
::
new
(
Var
(
register
(
"RSP"
))),
endian
:
Endianness
::
LittleEndian
,
memory
:
Box
::
new
(
Var
(
register
(
"memory"
))),
// This is technically false, but the field is ignored at the moment
value
:
Box
::
new
(
Const
(
Bitvector
::
from_i64
(
42
))),
size
:
64
,
},
term
:
Def
::
Store
{
address
:
Var
(
register
(
"RSP"
)),
value
:
Const
(
Bitvector
::
from_i64
(
42
)),
},
};
...
...
@@ -183,12 +167,8 @@ fn context_problem_implementation() {
state
.get_register
(
&
register
(
"RSP"
))
.unwrap
()
);
state
.set_register
(
&
register
(
"callee_saved_reg"
),
Data
::
Value
(
bv
(
13
)))
.unwrap
();
state
.set_register
(
&
register
(
"other_reg"
),
Data
::
Value
(
bv
(
14
)))
.unwrap
();
state
.set_register
(
&
register
(
"callee_saved_reg"
),
Data
::
Value
(
bv
(
13
)));
state
.set_register
(
&
register
(
"other_reg"
),
Data
::
Value
(
bv
(
14
)));
let
malloc
=
call_term
(
"extern_malloc"
);
let
mut
state_after_malloc
=
context
.update_call_stub
(
&
state
,
&
malloc
)
.unwrap
();
...
...
@@ -205,7 +185,7 @@ fn context_problem_implementation() {
state
.get_register
(
&
register
(
"RSP"
))
.unwrap
()
.bin_op
(
BinOpType
::
PLUS
,
&
Data
::
Value
(
bv
(
8
)))
.bin_op
(
BinOpType
::
IntAdd
,
&
Data
::
Value
(
bv
(
8
)))
);
assert_eq!
(
state_after_malloc
...
...
@@ -218,15 +198,13 @@ fn context_problem_implementation() {
.unwrap
()
.is_top
());
state_after_malloc
.set_register
(
&
register
(
"callee_saved_reg"
),
Data
::
Pointer
(
PointerDomain
::
new
(
new_id
(
"call_extern_malloc"
,
"RAX"
),
bv
(
0
),
)),
)
.unwrap
();
state_after_malloc
.set_register
(
&
register
(
"callee_saved_reg"
),
Data
::
Pointer
(
PointerDomain
::
new
(
new_id
(
"call_extern_malloc"
,
"RAX"
),
bv
(
0
),
)),
);
let
free
=
call_term
(
"extern_free"
);
let
state_after_free
=
context
.update_call_stub
(
&
state_after_malloc
,
&
free
)
...
...
@@ -254,7 +232,7 @@ fn context_problem_implementation() {
state
.get_register
(
&
register
(
"RSP"
))
.unwrap
()
.bin_op
(
BinOpType
::
PLUS
,
&
Data
::
Value
(
bv
(
8
)))
.bin_op
(
BinOpType
::
IntAdd
,
&
Data
::
Value
(
bv
(
8
)))
);
assert_eq!
(
state_after_other_fn
...
...
@@ -290,7 +268,7 @@ fn update_return() {
callsite_id
.clone
(),
bv
(
0
)
.into
(),
ObjectType
::
Stack
,
64
,
ByteSize
::
new
(
8
)
,
);
state_before_return
.caller_stack_ids
...
...
@@ -304,7 +282,7 @@ fn update_return() {
other_callsite_id
.clone
(),
bv
(
0
)
.into
(),
ObjectType
::
Stack
,
64
,
ByteSize
::
new
(
8
)
,
);
state_before_return
.caller_stack_ids
...
...
@@ -312,15 +290,13 @@ fn update_return() {
state_before_return
.ids_known_to_caller
.insert
(
other_callsite_id
.clone
());
state_before_return
.set_register
(
&
register
(
"RAX"
),
Data
::
Pointer
(
PointerDomain
::
new
(
new_id
(
"call_callee_other"
,
"RSP"
),
bv
(
-
32
),
)),
)
.unwrap
();
state_before_return
.set_register
(
&
register
(
"RAX"
),
Data
::
Pointer
(
PointerDomain
::
new
(
new_id
(
"call_callee_other"
,
"RSP"
),
bv
(
-
32
),
)),
);
let
state_before_call
=
State
::
new
(
&
register
(
"RSP"
),
Tid
::
new
(
"original_caller_id"
));
let
mut
state_before_call
=
context
...
...
@@ -334,7 +310,7 @@ fn update_return() {
caller_caller_id
.clone
(),
bv
(
0
)
.into
(),
ObjectType
::
Stack
,
64
,
ByteSize
::
new
(
8
)
,
);
state_before_call
.caller_stack_ids
...
...
cwe_checker_rs/src/analysis/pointer_inference/context/trait_impls.rs
View file @
af32d275
...
...
@@ -16,7 +16,7 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a>
/// Update the state according to the effects of the given `Def` term.
fn
update_def
(
&
self
,
state
:
&
Self
::
Value
,
def
:
&
Term
<
Def
>
)
->
Option
<
Self
::
Value
>
{
// first check for use-after-frees
if
state
.contains_access_of_dangling_memory
(
&
def
.term
.rhs
)
{
if
state
.contains_access_of_dangling_memory
(
&
def
.term
)
{
let
warning
=
CweWarning
{
name
:
"CWE416"
.to_string
(),
version
:
VERSION
.to_string
(),
...
...
@@ -32,63 +32,27 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a>
self
.cwe_collector
.send
(
warning
)
.unwrap
();
}
match
&
def
.term.rhs
{
Expression
::
IfThenElse
{
condition
,
true_exp
,
false_exp
,
}
=>
{
// IfThenElse needs special handling, because it may encode conditional store instructions.
let
mut
true_state
=
state
.clone
();
if
let
Expression
::
Store
{
..
}
=
**
true_exp
{
self
.log_debug
(
true_state
.handle_store_exp
(
true_exp
),
Some
(
&
def
.tid
));
}
else
{
self
.log_debug
(
true_state
.handle_register_assign
(
&
def
.term.lhs
,
true_exp
),
Some
(
&
def
.tid
),
);
};
let
mut
false_state
=
state
.clone
();
if
let
Expression
::
Store
{
..
}
=
**
false_exp
{
self
.log_debug
(
false_state
.handle_store_exp
(
false_exp
),
Some
(
&
def
.tid
));
}
else
{
self
.log_debug
(
false_state
.handle_register_assign
(
&
def
.term.lhs
,
false_exp
),
Some
(
&
def
.tid
),
);
};
match
state
.eval
(
condition
)
{
Ok
(
Data
::
Value
(
cond
))
if
!
cond
.is_top
()
=>
{
if
cond
==
Bitvector
::
from_bit
(
true
)
.into
()
{
Some
(
true_state
)
}
else
if
cond
==
Bitvector
::
from_bit
(
false
)
.into
()
{
Some
(
false_state
)
}
else
{
panic!
(
"IfThenElse with wrong condition bitsize encountered"
)
}
}
Ok
(
_
)
=>
Some
(
true_state
.merge
(
&
false_state
)),
Err
(
err
)
=>
panic!
(
"IfThenElse-Condition evaluation failed: {}"
,
err
),
}
}
Expression
::
Store
{
..
}
=>
{
match
&
def
.term
{
Def
::
Store
{
address
,
value
}
=>
{
let
mut
state
=
state
.clone
();
self
.log_debug
(
state
.handle_store
_exp
(
&
def
.term.rhs
),
Some
(
&
def
.tid
));
self
.log_debug
(
state
.handle_store
(
address
,
value
),
Some
(
&
def
.tid
));
Some
(
state
)
}
expression
=>
{
Def
::
Assign
{
var
,
value
}
=>
{
let
mut
new_state
=
state
.clone
();
self
.log_debug
(
new_state
.handle_register_assign
(
&
def
.term.lhs
,
expression
),
Some
(
&
def
.tid
),
);
self
.log_debug
(
new_state
.handle_register_assign
(
var
,
value
),
Some
(
&
def
.tid
));
Some
(
new_state
)
}
Def
::
Load
{
var
,
address
}
=>
{
let
mut
new_state
=
state
.clone
();
self
.log_debug
(
new_state
.handle_load
(
var
,
address
),
Some
(
&
def
.tid
));
Some
(
new_state
)
}
}
}
/// Update the state according to the effects of the given `Jmp` term.
/// Right now th
is only removes virtual registers from the state
,
/// Right now th
e state is not changed
,
/// as specialization for conditional jumps is not implemented yet.
fn
update_jump
(
&
self
,
...
...
@@ -97,8 +61,7 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a>
_untaken_conditional
:
Option
<&
Term
<
Jmp
>>
,
_target
:
&
Term
<
Blk
>
,
)
->
Option
<
State
>
{
let
mut
new_value
=
value
.clone
();
new_value
.remove_virtual_register
();
let
new_value
=
value
.clone
();
Some
(
new_value
)
}
...
...
@@ -110,13 +73,11 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a>
call_term
:
&
Term
<
Jmp
>
,
_target_node
:
&
crate
::
analysis
::
graph
::
Node
,
)
->
Option
<
State
>
{
let
call
=
if
let
JmpKind
::
Call
(
ref
call
)
=
call_term
.term.kind
{
call
}
else
{
panic!
(
"Malformed control flow graph: Encountered call edge with a non-call jump term."
)
};
if
let
Label
::
Direct
(
ref
callee_tid
)
=
call
.target
{
if
let
Jmp
::
Call
{
target
:
ref
callee_tid
,
return_
:
_
,
}
=
call_term
.term
{
let
callee_stack_id
=
AbstractIdentifier
::
new
(
callee_tid
.clone
(),
AbstractLocation
::
from_var
(
&
self
.project.stack_pointer_register
)
.unwrap
(),
...
...
@@ -126,7 +87,7 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a>
AbstractLocation
::
from_var
(
&
self
.project.stack_pointer_register
)
.unwrap
(),
);
let
stack_offset_adjustment
=
self
.get_current_stack_offset
(
state
);
let
address_b
itsize
=
self
.project.stack_pointer_register
.bitsize
()
.unwrap
()
;
let
address_b
ytesize
=
self
.project.stack_pointer_register.size
;
let
mut
callee_state
=
state
.clone
();
callee_state
.remove_virtual_register
();
...
...
@@ -140,25 +101,21 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a>
// add a new memory object for the callee stack frame
callee_state
.memory
.add_abstract_object
(
callee_stack_id
.clone
(),
Bitvector
::
zero
(
apint
::
BitWidth
::
new
(
address_bitsize
as
usize
)
.unwrap
(
))
.into
(),
Bitvector
::
zero
(
apint
::
BitWidth
::
from
(
address_bytesize
))
.into
(),
ObjectType
::
Stack
,
address_b
it
size
,
address_b
yte
size
,
);
// set the new stack_id
callee_state
.stack_id
=
callee_stack_id
.clone
();
// Set the stack pointer register to the callee stack id.
// At the beginning of a function this is the only known pointer to the new stack frame.
self
.log_debug
(
callee_state
.set_register
(
&
self
.project.stack_pointer_register
,
PointerDomain
::
new
(
callee_stack_id
.clone
(),
Bitvector
::
zero
(
apint
::
BitWidth
::
new
(
address_bitsize
as
usize
)
.unwrap
())
.into
(),
)
.into
(),
),
Some
(
&
call_term
.tid
),
callee_state
.set_register
(
&
self
.project.stack_pointer_register
,
PointerDomain
::
new
(
callee_stack_id
.clone
(),
Bitvector
::
zero
(
apint
::
BitWidth
::
from
(
address_bytesize
))
.into
(),
)
.into
(),
);
// set the list of caller stack ids to only this caller id
callee_state
.caller_stack_ids
=
BTreeSet
::
new
();
...
...
@@ -171,8 +128,10 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a>
callee_state
.ids_known_to_caller
.remove
(
&
callee_stack_id
);
Some
(
callee_state
)
}
else
{
}
else
if
let
Jmp
::
CallInd
{
..
}
=
call_term
.term
{
panic!
(
"Indirect call edges not yet supported."
)
}
else
{
panic!
(
"Malformed control flow graph: Call edge was not a call."
)
}
}
...
...
@@ -245,8 +204,9 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a>
/// Update the state according to the effect of a call to an extern symbol.
fn
update_call_stub
(
&
self
,
state
:
&
State
,
call
:
&
Term
<
Jmp
>
)
->
Option
<
State
>
{
let
mut
new_state
=
state
.clone
();
let
call_target
=
match
&
call
.term.kind
{
JmpKind
::
Call
(
call_inner
)
=>
&
call_inner
.target
,
let
call_target
=
match
&
call
.term
{
Jmp
::
Call
{
target
,
..
}
=>
target
,
Jmp
::
CallInd
{
..
}
=>
panic!
(
"Indirect calls to extern symbols not yet supported."
),
_
=>
panic!
(
"Malformed control flow graph encountered."
),
};
// Clear non-callee-saved registers from the state.
...
...
@@ -258,45 +218,31 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a>
let
stack_pointer
=
state
.get_register
(
stack_register
)
.unwrap
();
match
self
.project.cpu_architecture
.as_str
()
{
"x86"
|
"x86_64"
=>
{
let
offset
=
Bitvector
::
from_u
16
(
stack_register
.bitsize
()
.unwrap
()
/
8
)
.into_
zero_extend
(
stack_register
.bitsize
()
.unwrap
()
as
usize
)
let
offset
=
Bitvector
::
from_u
64
(
stack_register
.size
.into
()
)
.into_
truncate
(
apint
::
BitWidth
::
from
(
stack_register
.size
)
)
.unwrap
();
self
.log_debug
(
new_state
.set_register
(
stack_register
,
stack_pointer
.bin_op
(
crate
::
bil
::
BinOpType
::
PLUS
,
&
offset
.into
()),
),
Some
(
&
call
.tid
),
new_state
.set_register
(
stack_register
,
stack_pointer
.bin_op
(
BinOpType
::
IntAdd
,
&
offset
.into
()),
);
}
_
=>
self
.log_debug
(
new_state
.set_register
(
stack_register
,
stack_pointer
),
Some
(
&
call
.tid
),
),
_
=>
new_state
.set_register
(
stack_register
,
stack_pointer
),
}
if
let
Some
(
extern_symbol
)
=
self
.extern_symbol_map
.get
(
call_target
)
{
// Check parameter for possible use-after-frees
self
.check_parameter_register_for_dangling_pointer
(
state
,
call
,
extern_symbol
);
match
call_target
{
Label
::
Direct
(
tid
)
=>
{
if
let
Some
(
extern_symbol
)
=
self
.extern_symbol_map
.get
(
tid
)
{
// Check parameter for possible use-after-frees
self
.check_parameter_register_for_dangling_pointer
(
state
,
call
,
extern_symbol
);
match
extern_symbol
.name
.as_str
()
{
"malloc"
|
"calloc"
|
"realloc"
|
"xmalloc"
=>
self
.add_new_object_in_call_return_register
(
new_state
,
call
,
extern_symbol
),
"free"
=>
self
.mark_parameter_object_as_freed
(
state
,
new_state
,
call
,
extern_symbol
,
),
_
=>
self
.handle_generic_extern_call
(
state
,
new_state
,
call
,
extern_symbol
),
}
}
else
{
panic!
(
"Extern symbol not found."
);
match
extern_symbol
.name
.as_str
()
{
"malloc"
|
"calloc"
|
"realloc"
|
"xmalloc"
=>
{
self
.add_new_object_in_call_return_register
(
new_state
,
call
,
extern_symbol
)
}
"free"
=>
{
self
.mark_parameter_object_as_freed
(
state
,
new_state
,
call
,
extern_symbol
)
}
_
=>
self
.handle_generic_extern_call
(
state
,
new_state
,
call
,
extern_symbol
),
}
Label
::
Indirect
(
_
)
=>
unimplemented!
(
"Handling of indirect edges not yet implemented"
),
// Right now this case should not exist. Decide how to handle only after it can actually occur.
}
else
{
panic!
(
"Extern symbol not found."
);
}
}
...
...
cwe_checker_rs/src/analysis/pointer_inference/mod.rs
View file @
af32d275
...
...
@@ -14,8 +14,7 @@
use
super
::
interprocedural_fixpoint
::{
Computation
,
NodeValue
};
use
crate
::
abstract_domain
::{
BitvectorDomain
,
DataDomain
};
use
crate
::
analysis
::
graph
::{
Graph
,
Node
};
use
crate
::
prelude
::
*
;
use
crate
::
term
::
*
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
utils
::
log
::
*
;
use
petgraph
::
graph
::
NodeIndex
;
use
petgraph
::
visit
::
IntoNodeReferences
;
...
...
cwe_checker_rs/src/analysis/pointer_inference/object.rs
View file @
af32d275
...
...
@@ -22,8 +22,8 @@ impl DerefMut for AbstractObject {
impl
AbstractObject
{
/// Create a new abstract object with given object type and address bitsize.
pub
fn
new
(
type_
:
ObjectType
,
address_b
itsize
:
Bit
Size
)
->
AbstractObject
{
AbstractObject
(
Arc
::
new
(
AbstractObjectInfo
::
new
(
type_
,
address_b
it
size
)))
pub
fn
new
(
type_
:
ObjectType
,
address_b
ytesize
:
Byte
Size
)
->
AbstractObject
{
AbstractObject
(
Arc
::
new
(
AbstractObjectInfo
::
new
(
type_
,
address_b
yte
size
)))
}
/// Short-circuits the `AbstractObjectInfo::merge` function if `self==other`.
...
...
@@ -53,20 +53,19 @@ pub struct AbstractObjectInfo {
impl
AbstractObjectInfo
{
/// Create a new abstract object with known object type and address bitsize
pub
fn
new
(
type_
:
ObjectType
,
address_b
itsize
:
Bit
Size
)
->
AbstractObjectInfo
{
pub
fn
new
(
type_
:
ObjectType
,
address_b
ytesize
:
Byte
Size
)
->
AbstractObjectInfo
{
AbstractObjectInfo
{
pointer_targets
:
BTreeSet
::
new
(),
is_unique
:
true
,
state
:
Some
(
ObjectState
::
Alive
),
type_
:
Some
(
type_
),
memory
:
MemRegion
::
new
(
address_b
it
size
),
memory
:
MemRegion
::
new
(
address_b
yte
size
),
}
}
/// Read the value at the given offset of the given size (in bits, not bytes) inside the memory region.
pub
fn
get_value
(
&
self
,
offset
:
Bitvector
,
bitsize
:
BitSize
)
->
Data
{
assert_eq!
(
bitsize
%
8
,
0
);
self
.memory
.get
(
offset
,
(
bitsize
/
8
)
as
u64
)
pub
fn
get_value
(
&
self
,
offset
:
Bitvector
,
bytesize
:
ByteSize
)
->
Data
{
self
.memory
.get
(
offset
,
bytesize
)
}
/// Write a value at the given offset to the memory region.
...
...
@@ -83,12 +82,12 @@ impl AbstractObjectInfo {
}
else
{
let
merged_value
=
self
.memory
.get
(
concrete_offset
.clone
(),
(
value
.bitsize
()
/
8
)
as
u64
)
.get
(
concrete_offset
.clone
(),
value
.bytesize
()
)
.merge
(
&
value
);
self
.memory
.add
(
merged_value
,
concrete_offset
.clone
());
};
}
else
{
self
.memory
=
MemRegion
::
new
(
self
.memory
.get_address_b
it
size
());
self
.memory
=
MemRegion
::
new
(
self
.memory
.get_address_b
yte
size
());
}
Ok
(())
}
...
...
@@ -101,11 +100,11 @@ impl AbstractObjectInfo {
if
let
BitvectorDomain
::
Value
(
ref
concrete_offset
)
=
offset
{
let
merged_value
=
self
.memory
.get
(
concrete_offset
.clone
(),
(
value
.bitsize
()
/
8
)
as
u64
)
.get
(
concrete_offset
.clone
(),
value
.bytesize
()
)
.merge
(
&
value
);
self
.memory
.add
(
merged_value
,
concrete_offset
.clone
());
}
else
{
self
.memory
=
MemRegion
::
new
(
self
.memory
.get_address_b
it
size
());
self
.memory
=
MemRegion
::
new
(
self
.memory
.get_address_b
yte
size
());
}
}
...
...
@@ -164,7 +163,7 @@ impl AbstractObjectInfo {
/// Represents the effect of unknown write instructions to the object
/// which may include writing pointers to targets from the `additional_targets` set to the object.
pub
fn
assume_arbitrary_writes
(
&
mut
self
,
additional_targets
:
&
BTreeSet
<
AbstractIdentifier
>
)
{
self
.memory
=
MemRegion
::
new
(
self
.memory
.get_address_b
it
size
());
self
.memory
=
MemRegion
::
new
(
self
.memory
.get_address_b
yte
size
());
self
.pointer_targets
.extend
(
additional_targets
.iter
()
.cloned
());
}
...
...
@@ -294,7 +293,7 @@ mod tests {
is_unique
:
true
,
state
:
Some
(
ObjectState
::
Alive
),
type_
:
Some
(
ObjectType
::
Heap
),
memory
:
MemRegion
::
new
(
64
),
memory
:
MemRegion
::
new
(
ByteSize
::
new
(
8
)
),
};
AbstractObject
(
Arc
::
new
(
obj_info
))
}
...
...
@@ -310,7 +309,7 @@ mod tests {
fn
new_id
(
tid
:
&
str
,
reg_name
:
&
str
)
->
AbstractIdentifier
{
AbstractIdentifier
::
new
(
Tid
::
new
(
tid
),
AbstractLocation
::
Register
(
reg_name
.into
(),
64
),
AbstractLocation
::
Register
(
reg_name
.into
(),
ByteSize
::
new
(
8
)
),
)
}
...
...
@@ -321,19 +320,22 @@ mod tests {
let
offset
=
bv
(
-
15
);
object
.set_value
(
three
,
&
offset
)
.unwrap
();
assert_eq!
(
object
.get_value
(
Bitvector
::
from_i64
(
-
16
),
64
),
Data
::
Top
(
64
)
object
.get_value
(
Bitvector
::
from_i64
(
-
16
),
ByteSize
::
new
(
8
)),
Data
::
Top
(
ByteSize
::
new
(
8
))
);
assert_eq!
(
object
.get_value
(
Bitvector
::
from_i64
(
-
15
),
ByteSize
::
new
(
8
)),
new_data
(
3
)
);
assert_eq!
(
object
.get_value
(
Bitvector
::
from_i64
(
-
15
),
64
),
new_data
(
3
));
object
.set_value
(
new_data
(
4
),
&
bv
(
-
12
))
.unwrap
();
assert_eq!
(
object
.get_value
(
Bitvector
::
from_i64
(
-
15
),
64
),
Data
::
Top
(
64
)
object
.get_value
(
Bitvector
::
from_i64
(
-
15
),
ByteSize
::
new
(
8
)
),
Data
::
Top
(
ByteSize
::
new
(
8
)
)
);
object
.merge_value
(
new_data
(
5
),
&
bv
(
-
12
));
assert_eq!
(
object
.get_value
(
Bitvector
::
from_i64
(
-
12
),
64
),
Data
::
Value
(
BitvectorDomain
::
new_top
(
64
))
object
.get_value
(
Bitvector
::
from_i64
(
-
12
),
ByteSize
::
new
(
8
)
),
Data
::
Value
(
BitvectorDomain
::
new_top
(
ByteSize
::
new
(
8
)
))
);
let
mut
other_object
=
new_abstract_object
();
...
...
@@ -341,11 +343,11 @@ 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
),
64
),
Data
::
Top
(
64
)
merged_object
.get_value
(
Bitvector
::
from_i64
(
-
12
),
ByteSize
::
new
(
8
)
),
Data
::
Top
(
ByteSize
::
new
(
8
)
)
);
assert_eq!
(
merged_object
.get_value
(
Bitvector
::
from_i64
(
0
),
64
),
merged_object
.get_value
(
Bitvector
::
from_i64
(
0
),
ByteSize
::
new
(
8
)
),
new_data
(
0
)
);
}
...
...
@@ -370,7 +372,7 @@ mod tests {
target_map
.remove
(
&
new_id
(
"time_1"
,
"RAX"
));
let
modified_pointer
=
PointerDomain
::
with_targets
(
target_map
);
assert_eq!
(
object
.get_value
(
Bitvector
::
from_i64
(
-
15
),
64
),
object
.get_value
(
Bitvector
::
from_i64
(
-
15
),
ByteSize
::
new
(
8
)
),
modified_pointer
.into
()
);
...
...
@@ -384,7 +386,7 @@ mod tests {
target_map
.insert
(
new_id
(
"time_234"
,
"RBX"
),
bv
(
50
));
let
modified_pointer
=
PointerDomain
::
with_targets
(
target_map
);
assert_eq!
(
object
.get_value
(
Bitvector
::
from_i64
(
-
15
),
64
),
object
.get_value
(
Bitvector
::
from_i64
(
-
15
),
ByteSize
::
new
(
8
)
),
modified_pointer
.into
()
);
}
...
...
cwe_checker_rs/src/analysis/pointer_inference/object_list.rs
View file @
af32d275
...
...
@@ -22,15 +22,15 @@ impl AbstractObjectList {
/// The offset into the stack object will be set to zero.
pub
fn
from_stack_id
(
stack_id
:
AbstractIdentifier
,
address_b
itsize
:
Bit
Size
,
address_b
ytesize
:
Byte
Size
,
)
->
AbstractObjectList
{
let
mut
objects
=
BTreeMap
::
new
();
let
stack_object
=
AbstractObject
::
new
(
ObjectType
::
Stack
,
address_b
it
size
);
let
stack_object
=
AbstractObject
::
new
(
ObjectType
::
Stack
,
address_b
yte
size
);
objects
.insert
(
stack_id
,
(
stack_object
,
Bitvector
::
zero
(
(
address_bitsize
as
usize
)
.into
(
))
.into
(),
Bitvector
::
zero
(
apint
::
BitWidth
::
from
(
address_bytesize
))
.into
(),
),
);
AbstractObjectList
{
objects
}
...
...
@@ -68,7 +68,7 @@ impl AbstractObjectList {
/// If the address is not unique, merge the value of all possible addresses.
///
/// Returns an error if the address is a `Data::Value`, i.e. not a pointer.
pub
fn
get_value
(
&
self
,
address
:
&
Data
,
size
:
B
it
Size
)
->
Result
<
Data
,
Error
>
{
pub
fn
get_value
(
&
self
,
address
:
&
Data
,
size
:
B
yte
Size
)
->
Result
<
Data
,
Error
>
{
match
address
{
Data
::
Value
(
value
)
=>
Err
(
anyhow!
(
"Load from non-pointer value:
\n
{:?}"
,
value
)),
Data
::
Top
(
_
)
=>
Ok
(
Data
::
new_top
(
size
)),
...
...
@@ -158,9 +158,9 @@ impl AbstractObjectList {
object_id
:
AbstractIdentifier
,
initial_offset
:
BitvectorDomain
,
type_
:
ObjectType
,
address_b
itsize
:
Bit
Size
,
address_b
ytesize
:
Byte
Size
,
)
{
let
new_object
=
AbstractObject
::
new
(
type_
,
address_b
it
size
);
let
new_object
=
AbstractObject
::
new
(
type_
,
address_b
yte
size
);
if
let
Some
((
object
,
offset
))
=
self
.objects
.get_mut
(
&
object_id
)
{
// If the identifier already exists, we have to assume that more than one object may be referenced by this identifier.
object
.is_unique
=
false
;
...
...
@@ -333,13 +333,14 @@ mod tests {
fn
new_id
(
name
:
&
str
)
->
AbstractIdentifier
{
AbstractIdentifier
::
new
(
Tid
::
new
(
"time0"
),
AbstractLocation
::
Register
(
name
.into
(),
64
),
AbstractLocation
::
Register
(
name
.into
(),
ByteSize
::
new
(
8
)
),
)
}
#[test]
fn
abstract_object_list
()
{
let
mut
obj_list
=
AbstractObjectList
::
from_stack_id
(
new_id
(
"RSP"
.into
()),
64
);
let
mut
obj_list
=
AbstractObjectList
::
from_stack_id
(
new_id
(
"RSP"
.into
()),
ByteSize
::
new
(
8
));
assert_eq!
(
obj_list
.objects
.len
(),
1
);
assert_eq!
(
obj_list
.objects
.values
()
.next
()
.unwrap
()
.
1
,
bv
(
0
));
...
...
@@ -349,12 +350,13 @@ mod tests {
.unwrap
();
assert_eq!
(
obj_list
.get_value
(
&
Data
::
Pointer
(
pointer
.clone
()),
64
)
.get_value
(
&
Data
::
Pointer
(
pointer
.clone
()),
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
Value
(
bv
(
42
))
);
let
mut
other_obj_list
=
AbstractObjectList
::
from_stack_id
(
new_id
(
"RSP"
.into
()),
64
);
let
mut
other_obj_list
=
AbstractObjectList
::
from_stack_id
(
new_id
(
"RSP"
.into
()),
ByteSize
::
new
(
8
));
let
second_pointer
=
PointerDomain
::
new
(
new_id
(
"RSP"
.into
()),
bv
(
-
8
));
other_obj_list
.set_value
(
pointer
.clone
(),
Data
::
Value
(
bv
(
42
)))
...
...
@@ -364,12 +366,17 @@ mod tests {
.unwrap
();
assert_eq!
(
other_obj_list
.get_value
(
&
Data
::
Pointer
(
second_pointer
.clone
()),
64
)
.get_value
(
&
Data
::
Pointer
(
second_pointer
.clone
()),
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
Value
(
bv
(
35
))
);
other_obj_list
.add_abstract_object
(
new_id
(
"RAX"
.into
()),
bv
(
0
),
ObjectType
::
Heap
,
64
);
other_obj_list
.add_abstract_object
(
new_id
(
"RAX"
.into
()),
bv
(
0
),
ObjectType
::
Heap
,
ByteSize
::
new
(
8
),
);
let
heap_pointer
=
PointerDomain
::
new
(
new_id
(
"RAX"
.into
()),
bv
(
8
));
other_obj_list
.set_value
(
heap_pointer
.clone
(),
Data
::
Value
(
bv
(
3
)))
...
...
@@ -378,19 +385,19 @@ mod tests {
let
mut
merged
=
obj_list
.merge
(
&
other_obj_list
);
assert_eq!
(
merged
.get_value
(
&
Data
::
Pointer
(
pointer
.clone
()),
64
)
.get_value
(
&
Data
::
Pointer
(
pointer
.clone
()),
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
Value
(
bv
(
42
))
);
assert_eq!
(
merged
.get_value
(
&
Data
::
Pointer
(
second_pointer
.clone
()),
64
)
.get_value
(
&
Data
::
Pointer
(
second_pointer
.clone
()),
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
new_top
(
64
)
Data
::
new_top
(
ByteSize
::
new
(
8
)
)
);
assert_eq!
(
merged
.get_value
(
&
Data
::
Pointer
(
heap_pointer
.clone
()),
64
)
.get_value
(
&
Data
::
Pointer
(
heap_pointer
.clone
()),
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
Value
(
bv
(
3
))
);
...
...
@@ -401,13 +408,13 @@ mod tests {
.unwrap
();
assert_eq!
(
merged
.get_value
(
&
Data
::
Pointer
(
pointer
.clone
()),
64
)
.get_value
(
&
Data
::
Pointer
(
pointer
.clone
()),
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
Value
(
BitvectorDomain
::
new_top
(
64
))
Data
::
Value
(
BitvectorDomain
::
new_top
(
ByteSize
::
new
(
8
)
))
);
assert_eq!
(
merged
.get_value
(
&
Data
::
Pointer
(
heap_pointer
.clone
()),
64
)
.get_value
(
&
Data
::
Pointer
(
heap_pointer
.clone
()),
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
Value
(
bv
(
3
))
);
...
...
@@ -435,7 +442,7 @@ mod tests {
other_obj_list
.replace_abstract_id
(
&
new_id
(
"RAX"
.into
()),
&
new_id
(
"ID2"
.into
()),
&
bv
(
0
));
assert_eq!
(
other_obj_list
.get_value
(
&
Data
::
Pointer
(
pointer
.clone
()),
64
)
.get_value
(
&
Data
::
Pointer
(
pointer
.clone
()),
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
Pointer
(
modified_heap_pointer
.clone
())
);
...
...
@@ -481,10 +488,16 @@ mod tests {
#[test]
fn
append_unknown_objects_test
()
{
let
mut
obj_list
=
AbstractObjectList
::
from_stack_id
(
new_id
(
"stack"
),
64
);
let
mut
other_obj_list
=
AbstractObjectList
::
from_stack_id
(
new_id
(
"stack"
),
64
);
other_obj_list
.add_abstract_object
(
new_id
(
"heap_obj"
),
bv
(
0
)
.into
(),
ObjectType
::
Heap
,
64
);
let
mut
obj_list
=
AbstractObjectList
::
from_stack_id
(
new_id
(
"stack"
),
ByteSize
::
new
(
8
));
let
mut
other_obj_list
=
AbstractObjectList
::
from_stack_id
(
new_id
(
"stack"
),
ByteSize
::
new
(
8
));
other_obj_list
.add_abstract_object
(
new_id
(
"heap_obj"
),
bv
(
0
)
.into
(),
ObjectType
::
Heap
,
ByteSize
::
new
(
8
),
);
obj_list
.append_unknown_objects
(
&
other_obj_list
);
assert_eq!
(
obj_list
.objects
.len
(),
2
);
...
...
cwe_checker_rs/src/analysis/pointer_inference/state/access_handling.rs
View file @
af32d275
...
...
@@ -8,7 +8,7 @@ impl State {
if
let
Some
(
data
)
=
self
.register
.get
(
variable
)
{
Ok
(
data
.clone
())
}
else
{
Ok
(
Data
::
new_top
(
variable
.
bitsize
()
?
))
Ok
(
Data
::
new_top
(
variable
.
size
))
}
}
...
...
@@ -28,16 +28,11 @@ impl State {
/// Set the value of a register.
///
/// Returns an error if the variable is not a register.
pub
fn
set_register
(
&
mut
self
,
variable
:
&
Variable
,
value
:
Data
)
->
Result
<
(),
Error
>
{
if
let
variable
::
Type
::
Immediate
(
_bitsize
)
=
variable
.type_
{
if
!
value
.is_top
()
{
self
.register
.insert
(
variable
.clone
(),
value
);
}
else
{
self
.register
.remove
(
variable
);
}
Ok
(())
pub
fn
set_register
(
&
mut
self
,
variable
:
&
Variable
,
value
:
Data
)
{
if
!
value
.is_top
()
{
self
.register
.insert
(
variable
.clone
(),
value
);
}
else
{
Err
(
anyhow!
(
"Variable is not a register type"
))
self
.register
.remove
(
variable
);
}
}
...
...
@@ -47,21 +42,13 @@ impl State {
target
:
&
Variable
,
expression
:
&
Expression
,
)
->
Result
<
(),
Error
>
{
if
let
Expression
::
Var
(
variable
)
=
expression
{
if
target
==
variable
{
// The assign does nothing. Occurs as "do nothing"-path in conditional stores.
// Needs special handling, since it is the only case where the target is allowed
// to denote memory instead of a register.
return
Ok
(());
}
}
match
self
.eval
(
expression
)
{
Ok
(
new_value
)
=>
{
self
.set_register
(
target
,
new_value
)
?
;
self
.set_register
(
target
,
new_value
);
Ok
(())
}
Err
(
err
)
=>
{
self
.set_register
(
target
,
Data
::
new_top
(
target
.
bitsize
()
?
))
?
;
self
.set_register
(
target
,
Data
::
new_top
(
target
.
size
))
;
Err
(
err
)
}
}
...
...
@@ -103,31 +90,38 @@ impl State {
}
}
/// Evaluate the given store
express
ion on the given state and return the resulting state.
/// Evaluate the given store
instruct
ion on the given state and return the resulting state.
///
/// The function panics if given anything else than a store expression.
pub
fn
handle_store_exp
(
&
mut
self
,
store_exp
:
&
Expression
)
->
Result
<
(),
Error
>
{
if
let
Expression
::
Store
{
memory
:
_
,
address
,
value
,
endian
:
_
,
size
,
}
=
store_exp
{
match
self
.eval
(
value
)
{
Ok
(
data
)
=>
{
assert_eq!
(
data
.bitsize
(),
*
size
);
self
.write_to_address
(
address
,
&
data
)
}
Err
(
err
)
=>
{
// we still need to write to the target location before reporting the error
self
.write_to_address
(
address
,
&
Data
::
new_top
(
*
size
))
?
;
Err
(
err
)
}
pub
fn
handle_store
(
&
mut
self
,
address
:
&
Expression
,
value
:
&
Expression
)
->
Result
<
(),
Error
>
{
match
self
.eval
(
value
)
{
Ok
(
data
)
=>
self
.write_to_address
(
address
,
&
data
),
Err
(
err
)
=>
{
// we still need to write to the target location before reporting the error
self
.write_to_address
(
address
,
&
Data
::
new_top
(
value
.bytesize
()))
?
;
Err
(
err
)
}
}
}
/// Evaluate the given load instruction and return the data read on success.
pub
fn
load_value
(
&
self
,
address
:
&
Expression
,
size
:
ByteSize
)
->
Result
<
Data
,
Error
>
{
Ok
(
self
.memory
.get_value
(
&
self
.adjust_pointer_for_read
(
&
self
.eval
(
address
)
?
),
size
)
?
)
}
/// Handle a load instruction by assigning the value loaded from the address given by the `address` expression to `var`.
pub
fn
handle_load
(
&
mut
self
,
var
:
&
Variable
,
address
:
&
Expression
)
->
Result
<
(),
Error
>
{
match
self
.load_value
(
address
,
var
.size
)
{
Ok
(
data
)
=>
{
self
.set_register
(
var
,
data
);
Ok
(())
}
Err
(
err
)
=>
{
self
.set_register
(
var
,
Data
::
new_top
(
var
.size
));
Err
(
err
)
}
}
else
{
panic!
(
"Expected store expression"
)
}
}
...
...
@@ -151,7 +145,7 @@ impl State {
new_targets
.insert
(
id
.clone
(),
offset
.clone
());
}
}
BitvectorDomain
::
Top
(
_b
it
size
)
=>
{
BitvectorDomain
::
Top
(
_b
yte
size
)
=>
{
for
caller_id
in
self
.caller_stack_ids
.iter
()
{
new_targets
.insert
(
caller_id
.clone
(),
offset
.clone
());
}
...
...
@@ -175,130 +169,39 @@ impl State {
match
expression
{
Var
(
variable
)
=>
self
.get_register
(
&
variable
),
Const
(
bitvector
)
=>
Ok
(
bitvector
.clone
()
.into
()),
// TODO: implement handling of endianness for loads and writes!
Load
{
memory
:
_
,
address
,
endian
:
_
,
size
,
}
=>
Ok
(
self
.memory
.get_value
(
&
self
.adjust_pointer_for_read
(
&
self
.eval
(
address
)
?
),
*
size
)
?
),
Store
{
..
}
=>
{
// This does not return an error, but panics outright.
// If this would return an error, it would hide a side effect, which is not allowed to happen.
panic!
(
"Store expression cannot be evaluated!"
)
}
BinOp
{
op
,
lhs
,
rhs
}
=>
{
if
*
op
==
crate
::
bil
::
BinOpType
::
XOR
&&
lhs
==
rhs
{
if
*
op
==
BinOpType
::
IntXOr
&&
lhs
==
rhs
{
// the result of `x XOR x` is always zero.
return
Ok
(
Bitvector
::
zero
(
apint
::
BitWidth
::
new
(
self
.eval
(
lhs
)
?
.bitsize
()
as
usize
)
?
)
.into
());
return
Ok
(
Bitvector
::
zero
(
apint
::
BitWidth
::
from
(
lhs
.bytesize
()))
.into
());
}
let
(
left
,
right
)
=
(
self
.eval
(
lhs
)
?
,
self
.eval
(
rhs
)
?
);
Ok
(
left
.bin_op
(
*
op
,
&
right
))
}
UnOp
{
op
,
arg
}
=>
Ok
(
self
.eval
(
arg
)
?
.un_op
(
*
op
)),
Cast
{
kind
,
width
,
arg
}
=>
Ok
(
self
.eval
(
arg
)
?
.cast
(
*
kind
,
*
width
)),
Let
{
var
:
_
,
bound_exp
:
_
,
body_exp
:
_
,
}
=>
Err
(
anyhow!
(
"Let binding expression handling not implemented"
)),
Unknown
{
description
,
type_
}
=>
{
if
let
crate
::
bil
::
variable
::
Type
::
Immediate
(
bitsize
)
=
type_
{
Ok
(
Data
::
new_top
(
*
bitsize
))
}
else
{
Err
(
anyhow!
(
"Unknown Memory operation: {}"
,
description
))
}
}
IfThenElse
{
condition
,
true_exp
,
false_exp
,
}
=>
match
self
.eval
(
condition
)
?
{
x
if
x
==
Bitvector
::
from_bit
(
false
)
.into
()
=>
self
.eval
(
false_exp
),
x
if
x
==
Bitvector
::
from_bit
(
true
)
.into
()
=>
self
.eval
(
true_exp
),
_
=>
Ok
(
self
.eval
(
true_exp
)
?
.merge
(
&
self
.eval
(
false_exp
)
?
)),
},
Extract
{
low_bit
,
high_bit
,
Cast
{
op
,
size
,
arg
}
=>
Ok
(
self
.eval
(
arg
)
?
.cast
(
*
op
,
*
size
)),
Unknown
{
description
:
_
,
size
,
}
=>
Ok
(
Data
::
new_top
(
*
size
)),
Subpiece
{
low_byte
,
size
,
arg
,
}
=>
Ok
(
self
.eval
(
arg
)
?
.extract
(
*
low_bit
,
*
high_bit
)),
Concat
{
left
,
right
}
=>
Ok
(
self
.eval
(
left
)
?
.concat
(
&
self
.eval
(
right
)
?
)),
}
=>
Ok
(
self
.eval
(
arg
)
?
.subpiece
(
*
low_byte
,
*
size
)),
}
}
/// Check if an expression contains a use-after-free
pub
fn
contains_access_of_dangling_memory
(
&
self
,
expression
:
&
Expression
)
->
bool
{
use
Expression
::
*
;
match
expression
{
Var
(
_
)
|
Const
(
_
)
|
Unknown
{
..
}
=>
false
,
Load
{
address
:
address_exp
,
..
}
=>
{
if
let
Ok
(
pointer
)
=
self
.eval
(
address_exp
)
{
pub
fn
contains_access_of_dangling_memory
(
&
self
,
def
:
&
Def
)
->
bool
{
match
def
{
Def
::
Load
{
address
,
..
}
|
Def
::
Store
{
address
,
..
}
=>
{
if
let
Ok
(
pointer
)
=
self
.eval
(
address
)
{
self
.memory
.is_dangling_pointer
(
&
pointer
,
true
)
||
self
.contains_access_of_dangling_memory
(
address_exp
)
}
else
{
false
}
}
Store
{
memory
:
_
,
address
:
address_exp
,
value
:
value_exp
,
..
}
=>
{
let
address_check
=
if
let
Ok
(
pointer
)
=
self
.eval
(
address_exp
)
{
self
.memory
.is_dangling_pointer
(
&
pointer
,
true
)
}
else
{
false
};
address_check
||
self
.contains_access_of_dangling_memory
(
address_exp
)
||
self
.contains_access_of_dangling_memory
(
value_exp
)
}
BinOp
{
op
:
_
,
lhs
,
rhs
}
=>
{
self
.contains_access_of_dangling_memory
(
lhs
)
||
self
.contains_access_of_dangling_memory
(
rhs
)
}
UnOp
{
op
:
_
,
arg
}
=>
self
.contains_access_of_dangling_memory
(
arg
),
Cast
{
kind
:
_
,
width
:
_
,
arg
,
}
=>
self
.contains_access_of_dangling_memory
(
arg
),
Let
{
var
:
_
,
bound_exp
,
body_exp
,
}
=>
{
self
.contains_access_of_dangling_memory
(
bound_exp
)
||
self
.contains_access_of_dangling_memory
(
body_exp
)
}
IfThenElse
{
condition
,
true_exp
,
false_exp
,
}
=>
{
self
.contains_access_of_dangling_memory
(
condition
)
||
self
.contains_access_of_dangling_memory
(
true_exp
)
||
self
.contains_access_of_dangling_memory
(
false_exp
)
}
Extract
{
low_bit
:
_
,
high_bit
:
_
,
arg
,
}
=>
self
.contains_access_of_dangling_memory
(
arg
),
Concat
{
left
,
right
}
=>
{
self
.contains_access_of_dangling_memory
(
left
)
||
self
.contains_access_of_dangling_memory
(
right
)
}
_
=>
false
,
}
}
...
...
cwe_checker_rs/src/analysis/pointer_inference/state/mod.rs
View file @
af32d275
use
super
::
object_list
::
AbstractObjectList
;
use
super
::
Data
;
use
crate
::
abstract_domain
::
*
;
use
crate
::
bil
::
*
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
prelude
::
*
;
use
crate
::
term
::
symbol
::
ExternSymbol
;
use
std
::
collections
::{
BTreeMap
,
BTreeSet
};
mod
access_handling
;
...
...
@@ -50,16 +49,13 @@ impl State {
stack_register
.clone
(),
PointerDomain
::
new
(
stack_id
.clone
(),
Bitvector
::
zero
(
(
stack_register
.bitsize
()
.unwrap
()
as
usize
)
.into
(
))
.into
(),
Bitvector
::
zero
(
apint
::
BitWidth
::
from
(
stack_register
.size
))
.into
(),
)
.into
(),
);
State
{
register
,
memory
:
AbstractObjectList
::
from_stack_id
(
stack_id
.clone
(),
stack_register
.bitsize
()
.unwrap
(),
),
memory
:
AbstractObjectList
::
from_stack_id
(
stack_id
.clone
(),
stack_register
.size
),
stack_id
,
caller_stack_ids
:
BTreeSet
::
new
(),
ids_known_to_caller
:
BTreeSet
::
new
(),
...
...
@@ -89,18 +85,27 @@ impl State {
/// Mark those parameter values of an extern function call, that are passed on the stack,
/// as unknown data (since the function may modify them).
pub
fn
clear_stack_parameter
(
&
mut
self
,
extern_call
:
&
ExternSymbol
)
->
Result
<
(),
Error
>
{
pub
fn
clear_stack_parameter
(
&
mut
self
,
extern_call
:
&
ExternSymbol
,
stack_pointer_register
:
&
Variable
,
)
->
Result
<
(),
Error
>
{
let
mut
result_log
=
Ok
(());
for
arg
in
&
extern_call
.arguments
{
match
&
arg
.location
{
Expression
::
Var
(
_
)
=>
{}
location_expression
=>
{
let
arg_size
=
arg
.var
.bitsize
()
.expect
(
"Encountered argument with unknown size"
);
let
data_top
=
Data
::
new_top
(
arg_size
);
if
let
Err
(
err
)
=
self
.write_to_address
(
location_expression
,
&
data_top
)
{
for
arg
in
&
extern_call
.parameters
{
match
arg
{
Arg
::
Register
(
_
)
=>
(),
Arg
::
Stack
{
offset
,
size
}
=>
{
let
data_top
=
Data
::
new_top
(
*
size
);
let
location_expression
=
Expression
::
BinOp
{
lhs
:
Box
::
new
(
Expression
::
Var
(
stack_pointer_register
.clone
())),
op
:
BinOpType
::
IntAdd
,
rhs
:
Box
::
new
(
Expression
::
Const
(
Bitvector
::
from_i64
(
*
offset
)
.into_truncate
(
apint
::
BitWidth
::
from
(
stack_pointer_register
.size
))
.unwrap
(),
)),
};
if
let
Err
(
err
)
=
self
.write_to_address
(
&
location_expression
,
&
data_top
)
{
result_log
=
Err
(
err
);
}
}
...
...
cwe_checker_rs/src/analysis/pointer_inference/state/tests.rs
View file @
af32d275
...
...
@@ -7,21 +7,21 @@ fn bv(value: i64) -> BitvectorDomain {
fn
new_id
(
time
:
&
str
,
register
:
&
str
)
->
AbstractIdentifier
{
AbstractIdentifier
::
new
(
Tid
::
new
(
time
),
AbstractLocation
::
Register
(
register
.into
(),
64
),
AbstractLocation
::
Register
(
register
.into
(),
ByteSize
::
new
(
8
)
),
)
}
fn
register
(
name
:
&
str
)
->
Variable
{
Variable
{
name
:
name
.into
(),
type_
:
crate
::
bil
::
variable
::
Type
::
Immediate
(
64
),
size
:
ByteSize
::
new
(
8
),
is_temp
:
false
,
}
}
fn
reg_add
(
name
:
&
str
,
value
:
i64
)
->
Expression
{
Expression
::
BinOp
{
op
:
BinOpType
::
PLUS
,
op
:
BinOpType
::
IntAdd
,
lhs
:
Box
::
new
(
Expression
::
Var
(
register
(
name
))),
rhs
:
Box
::
new
(
Expression
::
Const
(
Bitvector
::
from_i64
(
value
))),
}
...
...
@@ -29,51 +29,16 @@ fn reg_add(name: &str, value: i64) -> Expression {
fn
reg_sub
(
name
:
&
str
,
value
:
i64
)
->
Expression
{
Expression
::
BinOp
{
op
:
BinOpType
::
MINUS
,
op
:
BinOpType
::
IntSub
,
lhs
:
Box
::
new
(
Expression
::
Var
(
register
(
name
))),
rhs
:
Box
::
new
(
Expression
::
Const
(
Bitvector
::
from_i64
(
value
))),
}
}
fn
store_exp
(
address
:
Expression
,
value
:
Expression
)
->
Expression
{
let
mem_var
=
Variable
{
name
:
"mem"
.into
(),
type_
:
crate
::
bil
::
variable
::
Type
::
Memory
{
addr_size
:
64
,
elem_size
:
64
,
},
is_temp
:
false
,
};
Expression
::
Store
{
memory
:
Box
::
new
(
Expression
::
Var
(
mem_var
)),
address
:
Box
::
new
(
address
),
value
:
Box
::
new
(
value
),
endian
:
Endianness
::
LittleEndian
,
size
:
64
,
}
}
fn
load_exp
(
address
:
Expression
)
->
Expression
{
let
mem_var
=
Variable
{
name
:
"mem"
.into
(),
type_
:
crate
::
bil
::
variable
::
Type
::
Memory
{
addr_size
:
64
,
elem_size
:
64
,
},
is_temp
:
false
,
};
Expression
::
Load
{
memory
:
Box
::
new
(
Expression
::
Var
(
mem_var
)),
address
:
Box
::
new
(
address
),
endian
:
Endianness
::
LittleEndian
,
size
:
64
,
}
}
#[test]
fn
state
()
{
use
crate
::
analysis
::
pointer_inference
::
object
::
*
;
use
crate
::
bil
::
Expression
::
*
;
use
Expression
::
*
;
let
mut
state
=
State
::
new
(
&
register
(
"RSP"
),
Tid
::
new
(
"time0"
));
let
stack_id
=
new_id
(
"time0"
,
"RSP"
);
let
stack_addr
=
Data
::
Pointer
(
PointerDomain
::
new
(
stack_id
.clone
(),
bv
(
8
)));
...
...
@@ -81,13 +46,12 @@ fn state() {
.store_value
(
&
stack_addr
,
&
Data
::
Value
(
bv
(
42
)))
.unwrap
();
state
.register
.insert
(
register
(
"RSP"
),
stack_addr
.clone
());
let
load_expr
=
Load
{
memory
:
Box
::
new
(
Var
(
register
(
"RSP"
))),
// This is wrong, but the memory var is not checked at the moment (since we have only the one for RAM)
address
:
Box
::
new
(
Var
(
register
(
"RSP"
))),
endian
:
Endianness
::
LittleEndian
,
size
:
64
as
BitSize
,
};
assert_eq!
(
state
.eval
(
&
load_expr
)
.unwrap
(),
Data
::
Value
(
bv
(
42
)));
assert_eq!
(
state
.load_value
(
&
Var
(
register
(
"RSP"
)),
ByteSize
::
new
(
8
))
.unwrap
(),
Data
::
Value
(
bv
(
42
))
);
let
mut
other_state
=
State
::
new
(
&
register
(
"RSP"
),
Tid
::
new
(
"time0"
));
state
.register
.insert
(
register
(
"RAX"
),
Data
::
Value
(
bv
(
42
)));
...
...
@@ -103,12 +67,20 @@ fn state() {
let
merged_state
=
state
.merge
(
&
other_state
);
assert_eq!
(
merged_state
.register
[
&
register
(
"RAX"
)],
Data
::
Value
(
bv
(
42
)));
assert_eq!
(
merged_state
.register
.get
(
&
register
(
"RBX"
)),
None
);
assert_eq!
(
merged_state
.eval
(
&
load_expr
)
.unwrap
(),
Data
::
new_top
(
64
));
assert_eq!
(
merged_state
.load_value
(
&
Var
(
register
(
"RSP"
)),
ByteSize
::
new
(
8
))
.unwrap
(),
Data
::
new_top
(
ByteSize
::
new
(
8
))
);
// Test pointer adjustment on reads
state
.memory
.add_abstract_object
(
new_id
(
"time0"
,
"caller"
),
bv
(
0
),
ObjectType
::
Stack
,
64
);
state
.memory
.add_abstract_object
(
new_id
(
"time0"
,
"caller"
),
bv
(
0
),
ObjectType
::
Stack
,
ByteSize
::
new
(
8
),
);
state
.caller_stack_ids
.insert
(
new_id
(
"time0"
,
"caller"
));
state
.store_value
(
&
stack_addr
,
&
Data
::
Value
(
bv
(
15
)))
...
...
@@ -118,26 +90,41 @@ fn state() {
.memory
.get_value
(
&
Data
::
Pointer
(
PointerDomain
::
new
(
new_id
(
"time0"
,
"caller"
),
bv
(
8
))),
64
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
Value
(
bv
(
15
))
);
assert_eq!
(
state
.eval
(
&
load_expr
)
.unwrap
(),
Data
::
Value
(
bv
(
15
)));
assert_eq!
(
state
.load_value
(
&
Var
(
register
(
"RSP"
)),
ByteSize
::
new
(
8
))
.unwrap
(),
Data
::
Value
(
bv
(
15
))
);
// Test replace_abstract_id
let
pointer
=
Data
::
Pointer
(
PointerDomain
::
new
(
stack_id
.clone
(),
bv
(
-
16
)));
state
.register
.insert
(
register
(
"RSP"
),
pointer
.clone
());
state
.store_value
(
&
pointer
,
&
Data
::
Value
(
bv
(
7
)))
.unwrap
();
assert_eq!
(
state
.eval
(
&
load_expr
)
.unwrap
(),
Data
::
Value
(
bv
(
7
)));
assert_eq!
(
state
.load_value
(
&
Var
(
register
(
"RSP"
)),
ByteSize
::
new
(
8
))
.unwrap
(),
Data
::
Value
(
bv
(
7
))
);
state
.replace_abstract_id
(
&
stack_id
,
&
new_id
(
"time0"
,
"callee"
),
&
bv
(
-
8
));
assert_eq!
(
state
.eval
(
&
load_expr
)
.unwrap
(),
Data
::
Value
(
bv
(
7
)));
assert_eq!
(
state
.load_value
(
&
Var
(
register
(
"RSP"
)),
ByteSize
::
new
(
8
))
.unwrap
(),
Data
::
Value
(
bv
(
7
))
);
assert_eq!
(
state
.memory
.get_value
(
&
Data
::
Pointer
(
PointerDomain
::
new
(
new_id
(
"time0"
,
"callee"
),
bv
(
-
8
))),
64
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
Value
(
bv
(
7
))
...
...
@@ -147,15 +134,18 @@ fn state() {
.memory
.get_value
(
&
Data
::
Pointer
(
PointerDomain
::
new
(
new_id
(
"time0"
,
"callee"
),
bv
(
-
16
))),
64
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
new_top
(
64
)
Data
::
new_top
(
ByteSize
::
new
(
8
)
)
);
state
.memory
.add_abstract_object
(
new_id
(
"time0"
,
"heap_obj"
),
bv
(
0
),
ObjectType
::
Heap
,
64
);
state
.memory
.add_abstract_object
(
new_id
(
"time0"
,
"heap_obj"
),
bv
(
0
),
ObjectType
::
Heap
,
ByteSize
::
new
(
8
),
);
assert_eq!
(
state
.memory
.get_num_objects
(),
3
);
state
.remove_unreferenced_objects
();
assert_eq!
(
state
.memory
.get_num_objects
(),
2
);
...
...
@@ -163,7 +153,7 @@ fn state() {
#[test]
fn
handle_store
()
{
use
crate
::
bil
::
Expression
::
*
;
use
Expression
::
*
;
let
mut
state
=
State
::
new
(
&
register
(
"RSP"
),
Tid
::
new
(
"time0"
));
let
stack_id
=
new_id
(
"time0"
,
"RSP"
);
assert_eq!
(
...
...
@@ -187,31 +177,34 @@ fn handle_store() {
);
state
.handle_store
_exp
(
&
store_exp
(
reg_add
(
"RSP"
,
8
),
Const
(
Bitvector
::
from_i64
(
1
)
)))
.handle_store
(
&
reg_add
(
"RSP"
,
8
),
&
Const
(
Bitvector
::
from_i64
(
1
)))
.unwrap
();
state
.handle_store
_exp
(
&
store_exp
(
reg_sub
(
"RSP"
,
8
),
Const
(
Bitvector
::
from_i64
(
2
)
)))
.handle_store
(
&
reg_sub
(
"RSP"
,
8
),
&
Const
(
Bitvector
::
from_i64
(
2
)))
.unwrap
();
state
.handle_store_exp
(
&
store_exp
(
reg_add
(
"RSP"
,
-
16
),
Const
(
Bitvector
::
from_i64
(
3
)),
))
.handle_store
(
&
reg_add
(
"RSP"
,
-
16
),
&
Const
(
Bitvector
::
from_i64
(
3
)))
.unwrap
();
state
.handle_register_assign
(
&
register
(
"RSP"
),
&
reg_sub
(
"RSP"
,
4
))
.unwrap
();
assert_eq!
(
state
.eval
(
&
load_exp
(
reg_add
(
"RSP"
,
12
)))
.unwrap
(),
state
.load_value
(
&
reg_add
(
"RSP"
,
12
),
ByteSize
::
new
(
8
))
.unwrap
(),
bv
(
1
)
.into
()
);
assert_eq!
(
state
.eval
(
&
load_exp
(
reg_sub
(
"RSP"
,
4
)))
.unwrap
(),
state
.load_value
(
&
reg_sub
(
"RSP"
,
4
),
ByteSize
::
new
(
8
))
.unwrap
(),
bv
(
2
)
.into
()
);
assert_eq!
(
state
.eval
(
&
load_exp
(
reg_add
(
"RSP"
,
-
12
)))
.unwrap
(),
state
.load_value
(
&
reg_add
(
"RSP"
,
-
12
),
ByteSize
::
new
(
8
))
.unwrap
(),
bv
(
3
)
.into
()
);
}
...
...
@@ -219,44 +212,54 @@ fn handle_store() {
#[test]
fn
handle_caller_stack_stores
()
{
use
super
::
super
::
object
::
ObjectType
;
use
crate
::
bil
::
Expression
::
*
;
use
Expression
::
*
;
let
mut
state
=
State
::
new
(
&
register
(
"RSP"
),
Tid
::
new
(
"time0"
));
state
.memory
.add_abstract_object
(
new_id
(
"caller1"
,
"RSP"
),
bv
(
0
),
ObjectType
::
Stack
,
64
);
state
.memory
.add_abstract_object
(
new_id
(
"caller2"
,
"RSP"
),
bv
(
0
),
ObjectType
::
Stack
,
64
);
state
.memory
.add_abstract_object
(
new_id
(
"caller1"
,
"RSP"
),
bv
(
0
),
ObjectType
::
Stack
,
ByteSize
::
new
(
8
),
);
state
.memory
.add_abstract_object
(
new_id
(
"caller2"
,
"RSP"
),
bv
(
0
),
ObjectType
::
Stack
,
ByteSize
::
new
(
8
),
);
state
.caller_stack_ids
.insert
(
new_id
(
"caller1"
,
"RSP"
));
state
.caller_stack_ids
.insert
(
new_id
(
"caller2"
,
"RSP"
));
// store something on the caller stack
state
.handle_store_exp
(
&
store_exp
(
reg_add
(
"RSP"
,
8
),
Const
(
Bitvector
::
from_i64
(
42
)),
))
.handle_store
(
&
reg_add
(
"RSP"
,
8
),
&
Const
(
Bitvector
::
from_i64
(
42
)))
.unwrap
();
// check that it was saved in all caller objects and not on the callee stack object
let
pointer
=
PointerDomain
::
new
(
new_id
(
"time0"
,
"RSP"
),
bv
(
8
))
.into
();
assert_eq!
(
state
.memory
.get_value
(
&
pointer
,
64
)
.unwrap
(),
Data
::
new_top
(
64
)
state
.memory
.get_value
(
&
pointer
,
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
new_top
(
ByteSize
::
new
(
8
)
)
);
let
pointer
=
PointerDomain
::
new
(
new_id
(
"caller1"
,
"RSP"
),
bv
(
8
))
.into
();
assert_eq!
(
state
.memory
.get_value
(
&
pointer
,
64
)
.unwrap
(),
bv
(
42
)
.into
());
assert_eq!
(
state
.memory
.get_value
(
&
pointer
,
ByteSize
::
new
(
8
))
.unwrap
(),
bv
(
42
)
.into
()
);
let
pointer
=
PointerDomain
::
new
(
new_id
(
"caller2"
,
"RSP"
),
bv
(
8
))
.into
();
assert_eq!
(
state
.memory
.get_value
(
&
pointer
,
64
)
.unwrap
(),
bv
(
42
)
.into
());
assert_eq!
(
state
.memory
.get_value
(
&
pointer
,
ByteSize
::
new
(
8
))
.unwrap
(),
bv
(
42
)
.into
()
);
// accessing through a positive stack register offset should yield the value of the caller stacks
assert_eq!
(
state
.eval
(
&
load_exp
(
reg_add
(
"RSP"
,
8
)))
.unwrap
(),
state
.load_value
(
&
reg_add
(
"RSP"
,
8
),
ByteSize
::
new
(
8
))
.unwrap
(),
bv
(
42
)
.into
()
);
}
#[test]
fn
clear_parameters_on_the_stack_on_extern_calls
()
{
use
crate
::
bil
::
Expression
::
*
;
use
crate
::
term
::{
Arg
,
ArgIntent
};
use
Expression
::
*
;
let
mut
state
=
State
::
new
(
&
register
(
"RSP"
),
Tid
::
new
(
"time0"
));
state
.register
.insert
(
register
(
"RSP"
),
...
...
@@ -264,33 +267,35 @@ fn clear_parameters_on_the_stack_on_extern_calls() {
);
// write something onto the stack
state
.handle_store_exp
(
&
store_exp
(
reg_add
(
"RSP"
,
8
),
Const
(
Bitvector
::
from_i64
(
42
)),
))
.handle_store
(
&
reg_add
(
"RSP"
,
8
),
&
Const
(
Bitvector
::
from_i64
(
42
)))
.unwrap
();
// create an extern symbol which uses the value on the stack as a parameter
let
argument
=
Arg
{
var
:
register
(
"my_argument"
),
location
:
reg_add
(
"RSP"
,
8
),
intent
:
ArgIntent
::
Input
,
let
stack_param
=
Arg
::
Stack
{
offset
:
8
,
size
:
ByteSize
::
new
(
8
),
};
let
extern_symbol
=
ExternSymbol
{
tid
:
Tid
::
new
(
"symbol"
),
address
:
"some_address"
.into
(),
name
:
"my_extern_symbol"
.into
(),
calling_convention
:
None
,
arguments
:
vec!
[
argument
],
parameters
:
vec!
[
stack_param
],
return_values
:
Vec
::
new
(),
no_return
:
false
,
};
// check the value before
let
pointer
=
PointerDomain
::
new
(
new_id
(
"time0"
,
"RSP"
),
bv
(
-
12
))
.into
();
assert_eq!
(
state
.memory
.get_value
(
&
pointer
,
64
)
.unwrap
(),
bv
(
42
)
.into
());
assert_eq!
(
state
.memory
.get_value
(
&
pointer
,
ByteSize
::
new
(
8
))
.unwrap
(),
bv
(
42
)
.into
()
);
// clear stack parameter
state
.clear_stack_parameter
(
&
extern_symbol
)
.unwrap
();
state
.clear_stack_parameter
(
&
extern_symbol
,
&
register
(
"RSP"
))
.unwrap
();
// check the value after
assert_eq!
(
state
.memory
.get_value
(
&
pointer
,
64
)
.unwrap
(),
Data
::
new_top
(
64
)
state
.memory
.get_value
(
&
pointer
,
ByteSize
::
new
(
8
)
)
.unwrap
(),
Data
::
new_top
(
ByteSize
::
new
(
8
)
)
);
}
...
...
@@ -298,9 +303,12 @@ fn clear_parameters_on_the_stack_on_extern_calls() {
fn
merge_callee_stack_to_caller_stack
()
{
use
super
::
super
::
object
::
ObjectType
;
let
mut
state
=
State
::
new
(
&
register
(
"RSP"
),
Tid
::
new
(
"callee"
));
state
.memory
.add_abstract_object
(
new_id
(
"callsite"
,
"RSP"
),
bv
(
52
),
ObjectType
::
Stack
,
64
);
state
.memory
.add_abstract_object
(
new_id
(
"callsite"
,
"RSP"
),
bv
(
52
),
ObjectType
::
Stack
,
ByteSize
::
new
(
8
),
);
state
.caller_stack_ids
.insert
(
new_id
(
"callsite"
,
"RSP"
));
// check the state before merging to the caller stack
assert_eq!
(
...
...
cwe_checker_rs/src/ffi/analysis.rs
View file @
af32d275
...
...
@@ -12,7 +12,7 @@ fn run_pointer_inference(program_jsonbuilder_val: ocaml::Value) -> (Vec<CweWarni
serde_json
::
from_value
(
program_json
)
.expect
(
"Project deserialization failed"
);
project
.replace_let_bindings
();
crate
::
analysis
::
pointer_inference
::
run
(
&
project
,
false
)
crate
::
analysis
::
pointer_inference
::
run
(
&
project
.into
()
,
false
)
}
caml!
(
rs_run_pointer_inference
(
program_jsonbuilder_val
)
{
...
...
@@ -31,7 +31,7 @@ fn run_pointer_inference_and_print_debug(program_jsonbuilder_val: ocaml::Value)
serde_json
::
from_value
(
program_json
)
.expect
(
"Project deserialization failed"
);
project
.replace_let_bindings
();
crate
::
analysis
::
pointer_inference
::
run
(
&
project
,
true
);
// Note: This discard all CweWarnings and log messages.
crate
::
analysis
::
pointer_inference
::
run
(
&
project
.into
()
,
true
);
// Note: This discard all CweWarnings and log messages.
}
caml!
(
rs_run_pointer_inference_and_print_debug
(
program_jsonbuilder_val
)
{
...
...
cwe_checker_rs/src/intermediate_representation/expression.rs
View file @
af32d275
...
...
@@ -52,6 +52,32 @@ pub enum Expression {
},
}
impl
Expression
{
/// Return the size (in bytes) of the result value of the expression.
pub
fn
bytesize
(
&
self
)
->
ByteSize
{
use
BinOpType
::
*
;
use
Expression
::
*
;
match
self
{
Var
(
var
)
=>
var
.size
,
Const
(
bitvec
)
=>
bitvec
.width
()
.into
(),
BinOp
{
op
,
lhs
,
rhs
}
=>
match
op
{
Piece
=>
lhs
.bytesize
()
+
rhs
.bytesize
(),
IntEqual
|
IntNotEqual
|
IntLess
|
IntSLess
|
IntLessEqual
|
IntSLessEqual
|
IntCarry
|
IntSCarry
|
IntSBorrow
|
BoolXOr
|
BoolOr
|
BoolAnd
|
FloatEqual
|
FloatNotEqual
|
FloatLess
|
FloatLessEqual
=>
ByteSize
::
new
(
1
),
IntAdd
|
IntSub
|
IntAnd
|
IntOr
|
IntXOr
|
IntLeft
|
IntRight
|
IntSRight
|
IntMult
|
IntDiv
|
IntRem
|
IntSDiv
|
IntSRem
|
FloatAdd
|
FloatSub
|
FloatMult
|
FloatDiv
=>
lhs
.bytesize
(),
},
UnOp
{
op
,
arg
}
=>
match
op
{
UnOpType
::
FloatNaN
=>
ByteSize
::
new
(
1
),
_
=>
arg
.bytesize
(),
},
Cast
{
size
,
..
}
|
Unknown
{
size
,
..
}
|
Subpiece
{
size
,
..
}
=>
*
size
,
}
}
}
/// The type/mnemonic of a binary operation
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone,
Copy)]
pub
enum
BinOpType
{
...
...
cwe_checker_rs/src/intermediate_representation/mod.rs
View file @
af32d275
...
...
@@ -73,6 +73,12 @@ impl From<ByteSize> for apint::BitWidth {
}
}
impl
From
<
apint
::
BitWidth
>
for
ByteSize
{
fn
from
(
bitwidth
:
apint
::
BitWidth
)
->
ByteSize
{
ByteSize
::
new
(
bitwidth
.to_usize
()
as
u64
/
8
)
}
}
impl
ByteSize
{
pub
fn
new
(
value
:
u64
)
->
ByteSize
{
ByteSize
(
value
)
...
...
cwe_checker_rs/src/intermediate_representation/term.rs
View file @
af32d275
...
...
@@ -181,6 +181,30 @@ pub struct ExternSymbol {
pub
no_return
:
bool
,
}
impl
ExternSymbol
{
/// If the extern symbol has exactly one return value that is passed in a register,
/// return the register.
pub
fn
get_unique_return_register
(
&
self
)
->
Result
<&
Variable
,
Error
>
{
if
self
.return_values
.len
()
==
1
{
match
self
.return_values
[
0
]
{
Arg
::
Register
(
ref
var
)
=>
Ok
(
var
),
Arg
::
Stack
{
..
}
=>
Err
(
anyhow!
(
"Return value is passed on the stak"
)),
}
}
else
{
Err
(
anyhow!
(
"Wrong number of return values"
))
}
}
/// If the extern symbol has exactly one parameter, return the parameter.
pub
fn
get_unique_parameter
(
&
self
)
->
Result
<&
Arg
,
Error
>
{
if
self
.parameters
.len
()
==
1
{
Ok
(
&
self
.parameters
[
0
])
}
else
{
Err
(
anyhow!
(
"Wrong number of parameter values"
))
}
}
}
/// The `Program` structure represents a disassembled binary.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone)]
pub
struct
Program
{
...
...
@@ -205,4 +229,19 @@ pub struct Project {
pub
cpu_architecture
:
String
,
/// The stack pointer register for the given CPU architecture.
pub
stack_pointer_register
:
Variable
,
/// The names of callee-saved registers for the standard calling convention
/// for the given CPU architecture.
/// Note that this field may be removed in the future.
pub
callee_saved_registers
:
Vec
<
String
>
,
/// The names of parameter registers for the standard calling convention
/// for the given CPU architecture.
/// Note that this field may be removed in the future.
pub
parameter_registers
:
Vec
<
String
>
,
}
impl
Project
{
/// Return the size (in bytes) for pointers of the given architecture.
pub
fn
get_pointer_bytesize
(
&
self
)
->
ByteSize
{
self
.stack_pointer_register.size
}
}
cwe_checker_rs/src/intermediate_representation/variable.rs
View file @
af32d275
...
...
@@ -9,7 +9,7 @@ use crate::prelude::*;
/// Temporary variables are only valid until the end of the current assembly instruction.
/// However, one assembly instruction may span more than one basic block in the intermediate representation
/// (but never more than one function).
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone)]
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Clone)]
pub
struct
Variable
{
pub
name
:
String
,
pub
size
:
ByteSize
,
...
...
cwe_checker_rs/src/lib.rs
View file @
af32d275
...
...
@@ -21,6 +21,7 @@ mod prelude {
pub
use
serde
::{
Deserialize
,
Serialize
};
pub
use
crate
::
bil
::{
BitSize
,
Bitvector
};
pub
use
crate
::
intermediate_representation
::
ByteSize
;
pub
use
crate
::
intermediate_representation
::{
Term
,
Tid
};
pub
use
anyhow
::{
anyhow
,
Error
};
}
cwe_checker_rs/src/pcode/term.rs
View file @
af32d275
...
...
@@ -375,10 +375,15 @@ impl From<Project> for IrProject {
tid
:
project
.program.tid
,
term
:
project
.program.term
.into
(),
};
let
(
params
,
callee_saved
)
=
crate
::
utils
::
get_generic_parameter_and_callee_saved_register
(
&
project
.cpu_architecture
,
);
IrProject
{
program
,
cpu_architecture
:
project
.cpu_architecture
,
stack_pointer_register
:
project
.stack_pointer_register
.into
(),
callee_saved_registers
:
callee_saved
,
parameter_registers
:
params
,
}
}
}
...
...
@@ -657,11 +662,11 @@ mod tests {
}
},
"
stack_pointer_register
": {
"
name
": "
E
SP
",
"
size
":
32
,
"
name
": "
R
SP
",
"
size
":
8
,
"
is_virtual
": false
},
"
cpu_architecture
": "
x86_
32
"
"
cpu_architecture
": "
x86_
64
"
}
"
#
,
)
...
...
cwe_checker_rs/src/term/mod.rs
View file @
af32d275
...
...
@@ -358,6 +358,8 @@ impl From<Project> for IrProject {
program
,
cpu_architecture
:
project
.cpu_architecture
,
stack_pointer_register
:
project
.stack_pointer_register
.into
(),
callee_saved_registers
:
project
.callee_saved_registers
,
parameter_registers
:
project
.parameter_registers
,
}
}
}
...
...
cwe_checker_rs/src/utils/mod.rs
View file @
af32d275
pub
mod
log
;
/// Get the names of parameter registers and callee saved registers
/// of the standard calling convention for the given architecture.
///
/// The registers are read from a configuration file.
pub
fn
get_generic_parameter_and_callee_saved_register
(
cpu_architecture
:
&
str
,
)
->
(
Vec
<
String
>
,
Vec
<
String
>
)
{
let
project_dirs
=
directories
::
ProjectDirs
::
from
(
""
,
""
,
"cwe_checker"
)
.expect
(
"Could not discern location of configuration files."
);
let
config_dir
=
project_dirs
.config_dir
();
let
register_config_path
=
config_dir
.join
(
"registers.json"
);
let
file
=
std
::
fs
::
read_to_string
(
register_config_path
)
.expect
(
"Could not read register configuration file"
);
let
mut
registers_json
:
serde_json
::
Value
=
serde_json
::
from_str
(
&
file
)
.unwrap
();
match
cpu_architecture
{
"x86"
|
"x86_32"
=>
registers_json
=
registers_json
[
"elf"
][
"x86"
][
"cdecl"
]
.clone
(),
_
=>
registers_json
=
registers_json
[
"elf"
][
cpu_architecture
]
.clone
(),
}
let
mut
callee_saved
:
Vec
<
String
>
=
serde_json
::
from_value
(
registers_json
[
"callee_saved"
]
.clone
())
.unwrap
();
let
mut
callee_saved_float
:
Vec
<
String
>
=
serde_json
::
from_value
(
registers_json
[
"float_callee_saved"
]
.clone
())
.unwrap
();
callee_saved
.append
(
&
mut
callee_saved_float
);
let
mut
params
:
Vec
<
String
>
=
serde_json
::
from_value
(
registers_json
[
"params"
]
.clone
())
.unwrap
();
let
mut
params_float
:
Vec
<
String
>
=
serde_json
::
from_value
(
registers_json
[
"float_params"
]
.clone
())
.unwrap
();
params
.append
(
&
mut
params_float
);
(
params
,
callee_saved
)
}
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