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