Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
cwe_checker
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
fact-depend
cwe_checker
Commits
170f44b1
Unverified
Commit
170f44b1
authored
2 years ago
by
van den Bosch
Committed by
GitHub
2 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor runtime memory image (#324)
parent
576c3dd9
Hide whitespace changes
Inline
Side-by-side
Showing
37 changed files
with
512 additions
and
567 deletions
+512
-567
main.rs
src/caller/src/main.rs
+6
-7
id_manipulation.rs
...src/analysis/pointer_inference/context/id_manipulation.rs
+1
-1
mod.rs
...checker_lib/src/analysis/pointer_inference/context/mod.rs
+19
-17
trait_impls.rs
...lib/src/analysis/pointer_inference/context/trait_impls.rs
+5
-3
mod.rs
src/cwe_checker_lib/src/analysis/pointer_inference/mod.rs
+5
-1
cwe_helpers.rs
...src/analysis/pointer_inference/object_list/cwe_helpers.rs
+2
-0
mod.rs
...ker_lib/src/analysis/pointer_inference/object_list/mod.rs
+1
-1
access_handling.rs
...b/src/analysis/pointer_inference/state/access_handling.rs
+0
-2
mod.rs
...e_checker_lib/src/analysis/pointer_inference/state/mod.rs
+0
-1
tests.rs
...checker_lib/src/analysis/pointer_inference/state/tests.rs
+0
-1
vsa_result_impl.rs
...ker_lib/src/analysis/pointer_inference/vsa_result_impl.rs
+1
-1
mod.rs
...hecker_lib/src/analysis/string_abstraction/context/mod.rs
+0
-6
symbol_calls.rs
...b/src/analysis/string_abstraction/context/symbol_calls.rs
+3
-2
memcpy.rs
...nalysis/string_abstraction/context/symbol_calls/memcpy.rs
+2
-2
scanf.rs
...analysis/string_abstraction/context/symbol_calls/scanf.rs
+13
-10
sprintf.rs
...alysis/string_abstraction/context/symbol_calls/sprintf.rs
+3
-4
strcat.rs
...nalysis/string_abstraction/context/symbol_calls/strcat.rs
+3
-2
tests.rs
...analysis/string_abstraction/context/symbol_calls/tests.rs
+0
-1
tests.rs
...cker_lib/src/analysis/string_abstraction/context/tests.rs
+0
-2
trait_impls.rs
...ib/src/analysis/string_abstraction/context/trait_impls.rs
+3
-3
tests.rs
.../analysis/string_abstraction/context/trait_impls/tests.rs
+1
-3
mod.rs
src/cwe_checker_lib/src/analysis/string_abstraction/mod.rs
+3
-16
mod.rs
..._checker_lib/src/analysis/string_abstraction/state/mod.rs
+1
-2
cwe_134.rs
src/cwe_checker_lib/src/checkers/cwe_134.rs
+3
-4
cwe_467.rs
src/cwe_checker_lib/src/checkers/cwe_467.rs
+6
-17
cwe_476.rs
src/cwe_checker_lib/src/checkers/cwe_476.rs
+1
-6
context.rs
src/cwe_checker_lib/src/checkers/cwe_476/context.rs
+7
-18
cwe_560.rs
src/cwe_checker_lib/src/checkers/cwe_560.rs
+4
-11
cwe_78.rs
src/cwe_checker_lib/src/checkers/cwe_78.rs
+5
-2
mod.rs
src/cwe_checker_lib/src/intermediate_representation/mod.rs
+2
-0
project.rs
...we_checker_lib/src/intermediate_representation/project.rs
+5
-2
runtime_memory_image.rs
...b/src/intermediate_representation/runtime_memory_image.rs
+400
-0
lib.rs
src/cwe_checker_lib/src/lib.rs
+2
-10
term.rs
src/cwe_checker_lib/src/pcode/term.rs
+2
-0
arguments.rs
src/cwe_checker_lib/src/utils/arguments.rs
+1
-3
tests.rs
src/cwe_checker_lib/src/utils/arguments/tests.rs
+0
-3
binary.rs
src/cwe_checker_lib/src/utils/binary.rs
+2
-403
No files found.
src/caller/src/main.rs
View file @
170f44b1
...
...
@@ -4,7 +4,8 @@
extern
crate
cwe_checker_lib
;
// Needed for the docstring-link to work
use
cwe_checker_lib
::
analysis
::
graph
;
use
cwe_checker_lib
::
utils
::
binary
::{
BareMetalConfig
,
RuntimeMemoryImage
};
use
cwe_checker_lib
::
intermediate_representation
::
RuntimeMemoryImage
;
use
cwe_checker_lib
::
utils
::
binary
::
BareMetalConfig
;
use
cwe_checker_lib
::
utils
::
log
::{
print_all_messages
,
LogLevel
};
use
cwe_checker_lib
::
utils
::{
get_ghidra_plugin_path
,
read_config_file
};
use
cwe_checker_lib
::
AnalysisResults
;
...
...
@@ -159,6 +160,9 @@ fn run_with_ghidra(args: &CmdlineArgs) {
// so that other analyses do not have to adjust their addresses.
runtime_memory_image
.add_global_memory_offset
(
project
.program.term.address_base_offset
);
}
project
.runtime_memory_image
=
runtime_memory_image
;
// Generate the control flow graph of the program
let
extern_sub_tids
=
project
.program
...
...
@@ -169,12 +173,7 @@ fn run_with_ghidra(args: &CmdlineArgs) {
.collect
();
let
control_flow_graph
=
graph
::
get_program_cfg
(
&
project
.program
,
extern_sub_tids
);
let
analysis_results
=
AnalysisResults
::
new
(
&
binary
,
&
runtime_memory_image
,
&
control_flow_graph
,
&
project
,
);
let
analysis_results
=
AnalysisResults
::
new
(
&
binary
,
&
control_flow_graph
,
&
project
);
let
modules_depending_on_string_abstraction
=
BTreeSet
::
from_iter
([
"CWE78"
]);
let
modules_depending_on_pointer_inference
=
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/pointer_inference/context/id_manipulation.rs
View file @
170f44b1
...
...
@@ -22,7 +22,7 @@ impl<'a> Context<'a> {
for
param
in
callee_fn_sig
.parameters
.keys
()
{
let
param_id
=
AbstractIdentifier
::
from_arg
(
callee_tid
,
param
);
if
let
Ok
(
param_value
)
=
state_before_call
.eval_parameter_arg
(
param
,
self
.runtime_memory_image
)
state_before_call
.eval_parameter_arg
(
param
,
&
self
.project
.runtime_memory_image
)
{
id_map
.insert
(
param_id
,
param_value
);
}
else
{
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/pointer_inference/context/mod.rs
View file @
170f44b1
use
crate
::
abstract_domain
::
*
;
use
crate
::
analysis
::
function_signature
::
FunctionSignature
;
use
crate
::
analysis
::
graph
::
Graph
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
prelude
::
*
;
use
crate
::
utils
::
log
::
*
;
use
crate
::{
abstract_domain
::
*
,
utils
::
binary
::
RuntimeMemoryImage
};
use
std
::
collections
::{
BTreeMap
,
BTreeSet
};
use
super
::
state
::
State
;
...
...
@@ -24,9 +24,6 @@ pub struct Context<'a> {
pub
graph
:
&
'a
Graph
<
'a
>
,
/// A reference to the `Project` object representing the binary
pub
project
:
&
'a
Project
,
/// The runtime memory image for reading global read-only variables.
/// Note that values of writeable global memory segments are not tracked.
pub
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
/// Maps the TIDs of functions that shall be treated as extern symbols to the `ExternSymbol` object representing it.
pub
extern_symbol_map
:
&
'a
BTreeMap
<
Tid
,
ExternSymbol
>
,
/// Maps the TIDs of internal functions to the function signatures computed for it.
...
...
@@ -54,7 +51,6 @@ impl<'a> Context<'a> {
Context
{
graph
:
analysis_results
.control_flow_graph
,
project
:
analysis_results
.project
,
runtime_memory_image
:
analysis_results
.runtime_memory_image
,
extern_symbol_map
:
&
analysis_results
.project.program.term.extern_symbols
,
fn_signatures
:
analysis_results
.function_signatures
.unwrap
(),
log_collector
,
...
...
@@ -73,7 +69,9 @@ impl<'a> Context<'a> {
address
:
&
Expression
,
)
->
bool
{
if
self
.project.cpu_architecture
.contains
(
"MIPS"
)
&&
var
.name
==
"gp"
{
if
let
Ok
(
gp_val
)
=
state
.load_value
(
address
,
var
.size
,
self
.runtime_memory_image
)
{
if
let
Ok
(
gp_val
)
=
state
.load_value
(
address
,
var
.size
,
&
self
.project.runtime_memory_image
)
{
gp_val
.is_top
()
}
else
{
true
...
...
@@ -137,23 +135,23 @@ impl<'a> Context<'a> {
"malloc"
=>
{
let
size_parameter
=
extern_symbol
.parameters
.get
(
0
)
.unwrap
();
state
.eval_parameter_arg
(
size_parameter
,
self
.runtime_memory_image
)
.eval_parameter_arg
(
size_parameter
,
&
self
.project
.runtime_memory_image
)
.unwrap_or_else
(|
_
|
Data
::
new_top
(
address_bytesize
))
}
"realloc"
=>
{
let
size_parameter
=
extern_symbol
.parameters
.get
(
1
)
.unwrap
();
state
.eval_parameter_arg
(
size_parameter
,
self
.runtime_memory_image
)
.eval_parameter_arg
(
size_parameter
,
&
self
.project
.runtime_memory_image
)
.unwrap_or_else
(|
_
|
Data
::
new_top
(
address_bytesize
))
}
"calloc"
=>
{
let
size_param1
=
extern_symbol
.parameters
.get
(
0
)
.unwrap
();
let
size_param2
=
extern_symbol
.parameters
.get
(
1
)
.unwrap
();
let
param1_value
=
state
.eval_parameter_arg
(
size_param1
,
self
.runtime_memory_image
)
.eval_parameter_arg
(
size_param1
,
&
self
.project
.runtime_memory_image
)
.unwrap_or_else
(|
_
|
Data
::
new_top
(
address_bytesize
));
let
param2_value
=
state
.eval_parameter_arg
(
size_param2
,
self
.runtime_memory_image
)
.eval_parameter_arg
(
size_param2
,
&
self
.project
.runtime_memory_image
)
.unwrap_or_else
(|
_
|
Data
::
new_top
(
address_bytesize
));
param1_value
.bin_op
(
BinOpType
::
IntMult
,
&
param2_value
)
}
...
...
@@ -225,7 +223,7 @@ impl<'a> Context<'a> {
match
extern_symbol
.get_unique_parameter
()
{
Ok
(
parameter
)
=>
{
let
parameter_value
=
state
.eval_parameter_arg
(
parameter
,
self
.runtime_memory_image
);
state
.eval_parameter_arg
(
parameter
,
&
self
.project
.runtime_memory_image
);
match
parameter_value
{
Ok
(
memory_object_pointer
)
=>
{
if
let
Err
(
possible_double_frees
)
=
...
...
@@ -275,7 +273,7 @@ impl<'a> Context<'a> {
I
:
Iterator
<
Item
=
&
'iter
Arg
>
,
{
for
parameter
in
parameters
{
match
state
.eval_parameter_arg
(
parameter
,
self
.runtime_memory_image
)
{
match
state
.eval_parameter_arg
(
parameter
,
&
self
.project
.runtime_memory_image
)
{
Ok
(
value
)
=>
{
if
state
.memory
.is_dangling_pointer
(
&
value
,
true
)
{
state
...
...
@@ -317,10 +315,12 @@ impl<'a> Context<'a> {
extern_symbol
:
&
ExternSymbol
,
)
{
for
parameter
in
extern_symbol
.parameters
.iter
()
{
match
state
.eval_parameter_arg
(
parameter
,
self
.runtime_memory_image
)
{
match
state
.eval_parameter_arg
(
parameter
,
&
self
.project
.runtime_memory_image
)
{
Ok
(
data
)
=>
{
if
state
.pointer_contains_out_of_bounds_target
(
&
data
,
self
.runtime_memory_image
)
{
if
state
.pointer_contains_out_of_bounds_target
(
&
data
,
&
self
.project.runtime_memory_image
,
)
{
let
warning
=
CweWarning
{
name
:
"CWE119"
.to_string
(),
version
:
VERSION
.to_string
(),
...
...
@@ -394,7 +394,7 @@ impl<'a> Context<'a> {
extern_symbol
:
&
ExternSymbol
,
)
->
State
{
self
.log_debug
(
new_state
.clear_stack_parameter
(
extern_symbol
,
self
.runtime_memory_image
),
new_state
.clear_stack_parameter
(
extern_symbol
,
&
self
.project
.runtime_memory_image
),
Some
(
&
call
.tid
),
);
let
calling_conv
=
self
.project
.get_calling_convention
(
extern_symbol
);
...
...
@@ -413,7 +413,9 @@ impl<'a> Context<'a> {
}
}
else
{
for
parameter
in
extern_symbol
.parameters
.iter
()
{
if
let
Ok
(
data
)
=
state
.eval_parameter_arg
(
parameter
,
self
.runtime_memory_image
)
{
if
let
Ok
(
data
)
=
state
.eval_parameter_arg
(
parameter
,
&
self
.project.runtime_memory_image
)
{
possible_referenced_ids
.extend
(
data
.referenced_ids
()
.cloned
());
}
}
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/pointer_inference/context/trait_impls.rs
View file @
170f44b1
...
...
@@ -42,7 +42,9 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
Ok
(
false
)
=>
(),
// no null dereference detected
}
// check for out-of-bounds memory access
if
new_state
.contains_out_of_bounds_mem_access
(
&
def
.term
,
self
.runtime_memory_image
)
{
if
new_state
.contains_out_of_bounds_mem_access
(
&
def
.term
,
&
self
.project.runtime_memory_image
)
{
let
(
warning_name
,
warning_description
)
=
match
&
def
.term
{
Def
::
Load
{
..
}
=>
(
"CWE125"
,
...
...
@@ -75,7 +77,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
match
&
def
.term
{
Def
::
Store
{
address
,
value
}
=>
{
self
.log_debug
(
new_state
.handle_store
(
address
,
value
,
self
.runtime_memory_image
),
new_state
.handle_store
(
address
,
value
,
&
self
.project
.runtime_memory_image
),
Some
(
&
def
.tid
),
);
Some
(
new_state
)
...
...
@@ -87,7 +89,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
Def
::
Load
{
var
,
address
}
=>
{
if
!
self
.is_mips_gp_load_to_top_value
(
state
,
var
,
address
)
{
self
.log_debug
(
new_state
.handle_load
(
var
,
address
,
self
.runtime_memory_image
),
new_state
.handle_load
(
var
,
address
,
&
self
.project
.runtime_memory_image
),
Some
(
&
def
.tid
),
);
}
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/pointer_inference/mod.rs
View file @
170f44b1
...
...
@@ -275,7 +275,11 @@ impl<'a> PointerInference<'a> {
}
Def
::
Load
{
var
,
address
}
=>
{
let
loaded_value
=
state
.load_value
(
address
,
var
.size
,
context
.runtime_memory_image
)
.load_value
(
address
,
var
.size
,
&
context
.project.runtime_memory_image
,
)
.unwrap_or_else
(|
_
|
Data
::
new_top
(
var
.size
));
self
.values_at_defs
.insert
(
def
.tid
.clone
(),
loaded_value
);
self
.addresses_at_defs
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/pointer_inference/object_list/cwe_helpers.rs
View file @
170f44b1
...
...
@@ -2,6 +2,8 @@
//! or check whether they are violated.
//! E.g. checks for use-after-free or buffer overflow checks.
use
crate
::
intermediate_representation
::
RuntimeMemoryImage
;
use
super
::
*
;
impl
AbstractObjectList
{
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/pointer_inference/object_list/mod.rs
View file @
170f44b1
use
super
::
object
::
*
;
use
super
::{
Data
,
ValueDomain
};
use
crate
::
abstract_domain
::
*
;
use
crate
::
prelude
::
*
;
use
crate
::{
abstract_domain
::
*
,
utils
::
binary
::
RuntimeMemoryImage
};
use
serde
::{
Deserialize
,
Serialize
};
use
std
::
collections
::{
BTreeMap
,
BTreeSet
};
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/pointer_inference/state/access_handling.rs
View file @
170f44b1
//! Methods of [`State`] for handling memory and register access operations.
use
crate
::
utils
::
binary
::
RuntimeMemoryImage
;
use
super
::
*
;
impl
State
{
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/pointer_inference/state/mod.rs
View file @
170f44b1
...
...
@@ -4,7 +4,6 @@ use crate::abstract_domain::*;
use
crate
::
analysis
::
function_signature
::
FunctionSignature
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
prelude
::
*
;
use
crate
::
utils
::
binary
::
RuntimeMemoryImage
;
use
std
::
collections
::{
BTreeMap
,
BTreeSet
};
mod
access_handling
;
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/pointer_inference/state/tests.rs
View file @
170f44b1
use
super
::
super
::
ValueDomain
;
use
super
::
*
;
use
crate
::
analysis
::
pointer_inference
::
object
::
*
;
use
crate
::
utils
::
binary
::
RuntimeMemoryImage
;
use
Expression
::
*
;
fn
bv
(
value
:
i64
)
->
ValueDomain
{
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/pointer_inference/vsa_result_impl.rs
View file @
170f44b1
...
...
@@ -27,7 +27,7 @@ impl<'a> VsaResult for PointerInference<'a> {
let
state
=
self
.states_at_tids
.get
(
jmp_tid
)
?
;
let
context
=
self
.computation
.get_context
()
.get_context
();
state
.eval_parameter_arg
(
parameter
,
contex
t
.runtime_memory_image
)
.eval_parameter_arg
(
parameter
,
&
context
.projec
t.runtime_memory_image
)
.ok
()
}
}
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/string_abstraction/context/mod.rs
View file @
170f44b1
...
...
@@ -17,7 +17,6 @@ use crate::{
pointer_inference
::
State
as
PointerInferenceState
,
},
intermediate_representation
::{
Def
,
ExternSymbol
,
Project
,
Term
,
Tid
},
utils
::
binary
::
RuntimeMemoryImage
,
};
use
super
::{
state
::
State
,
Config
};
...
...
@@ -31,9 +30,6 @@ mod trait_impls;
pub
struct
Context
<
'a
,
T
:
AbstractDomain
+
DomainInsertion
+
HasTop
+
Eq
+
From
<
String
>>
{
/// A reference to the `Project` object representing the binary
pub
project
:
&
'a
Project
,
/// The runtime memory image for reading global read-only variables.
/// Note that values of writeable global memory segments are not tracked.
pub
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
/// A pointer to the results of the pointer inference analysis.
/// They are used to determine the targets of pointers to memory,
/// which in turn is used to keep track of taint on the stack or on the heap.
...
...
@@ -62,7 +58,6 @@ impl<'a, T: AbstractDomain + HasTop + Eq + From<String> + DomainInsertion> Conte
/// Create a new context object for a given project.
pub
fn
new
(
project
:
&
'a
Project
,
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
pointer_inference_results
:
&
'a
PointerInferenceComputation
<
'a
>
,
config
:
Config
,
)
->
Context
<
'a
,
T
>
{
...
...
@@ -95,7 +90,6 @@ impl<'a, T: AbstractDomain + HasTop + Eq + From<String> + DomainInsertion> Conte
Context
{
project
,
runtime_memory_image
,
pointer_inference_results
,
format_string_index_map
:
config
.format_string_index
.into_iter
()
.collect
(),
string_symbol_map
,
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/string_abstraction/context/symbol_calls.rs
View file @
170f44b1
...
...
@@ -169,7 +169,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
/// Inserts a char constant into the format string.
pub
fn
get_constant_char_domain
(
&
self
,
constant
:
Bitvector
)
->
Option
<
T
>
{
if
let
Ok
(
Some
(
char_code
))
=
self
.runtime_memory_image
.read
(
if
let
Ok
(
Some
(
char_code
))
=
self
.
project.
runtime_memory_image
.read
(
&
constant
,
self
.project
.datatype_properties
...
...
@@ -199,6 +199,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
/// Inserts a string constant into the format string.
pub
fn
get_constant_string_domain
(
&
self
,
constant
:
Bitvector
)
->
Option
<
T
>
{
if
let
Ok
(
string
)
=
self
.project
.runtime_memory_image
.read_string_until_null_terminator
(
&
constant
)
{
...
...
@@ -218,7 +219,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
if
let
Some
(
dest_arg
)
=
extern_symbol
.parameters
.first
()
{
if
let
Some
(
pi_state
)
=
state
.get_pointer_inference_state
()
{
if
let
Ok
(
pointer
)
=
pi_state
.eval_parameter_arg
(
dest_arg
,
self
.runtime_memory_image
)
pi_state
.eval_parameter_arg
(
dest_arg
,
&
self
.project
.runtime_memory_image
)
{
let
heap_to_string_map
=
state
.get_heap_to_string_map
();
for
(
target
,
_
)
in
pointer
.get_relative_values
()
.iter
()
{
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/string_abstraction/context/symbol_calls/memcpy.rs
View file @
170f44b1
...
...
@@ -44,7 +44,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
)
->
Result
<
DataDomain
<
IntervalDomain
>
,
Error
>
{
if
let
Some
(
return_arg
)
=
extern_symbol
.parameters
.first
()
{
if
let
Ok
(
return_data
)
=
pi_state
.eval_parameter_arg
(
return_arg
,
self
.runtime_memory_image
)
pi_state
.eval_parameter_arg
(
return_arg
,
&
self
.project
.runtime_memory_image
)
{
if
!
return_data
.get_relative_values
()
.is_empty
()
{
return
Ok
(
return_data
);
...
...
@@ -62,7 +62,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
pi_state
:
&
PointerInferenceState
,
)
->
Result
<
DataDomain
<
IntervalDomain
>
,
Error
>
{
if
let
Some
(
input_arg
)
=
extern_symbol
.parameters
.get
(
1
)
{
return
pi_state
.eval_parameter_arg
(
input_arg
,
self
.runtime_memory_image
);
return
pi_state
.eval_parameter_arg
(
input_arg
,
&
self
.project
.runtime_memory_image
);
}
Err
(
anyhow!
(
"No input values"
))
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/string_abstraction/context/symbol_calls/scanf.rs
View file @
170f44b1
...
...
@@ -25,7 +25,6 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
pi_state
,
extern_symbol
,
&
self
.format_string_index_map
,
self
.runtime_memory_image
,
)
{
self
.create_abstract_domain_entries_for_function_return_values
(
pi_state
,
...
...
@@ -47,7 +46,8 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
)
{
for
(
argument
,
value
)
in
arg_to_value_map
.into_iter
()
{
if
argument
.get_data_type
()
.unwrap
()
==
Datatype
::
Pointer
{
if
let
Ok
(
data
)
=
pi_state
.eval_parameter_arg
(
&
argument
,
self
.runtime_memory_image
)
if
let
Ok
(
data
)
=
pi_state
.eval_parameter_arg
(
&
argument
,
&
self
.project.runtime_memory_image
)
{
if
!
data
.get_relative_values
()
.is_empty
()
{
Context
::
add_constant_or_top_value_to_return_locations
(
...
...
@@ -91,8 +91,8 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
let
mut
new_state
=
state
.clone
();
if
let
Some
(
pi_state
)
=
state
.get_pointer_inference_state
()
{
if
let
Some
(
source_string_arg
)
=
extern_symbol
.parameters
.first
()
{
if
let
Ok
(
source_string
)
=
pi_state
.eval_parameter_arg
(
source_string_arg
,
self
.runtime_memory_image
)
if
let
Ok
(
source_string
)
=
pi_state
.eval_parameter_arg
(
source_string_arg
,
&
self
.project
.runtime_memory_image
)
{
if
self
.source_string_mapped_to_return_locations
(
pi_state
,
...
...
@@ -120,11 +120,15 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
extern_symbol
:
&
ExternSymbol
,
)
->
bool
{
if
let
Some
(
global_address
)
=
source_string
.get_absolute_value
()
{
if
let
Ok
(
source_string
)
=
self
.runtime_memory_image
.read_string_until_null_terminator
(
&
global_address
.try_to_bitvec
()
.expect
(
"Could not translate interval address to bitvector."
),
)
{
if
let
Ok
(
source_string
)
=
self
.project
.runtime_memory_image
.read_string_until_null_terminator
(
&
global_address
.try_to_bitvec
()
.expect
(
"Could not translate interval address to bitvector."
),
)
{
if
let
Ok
(
source_return_string_map
)
=
self
.map_source_string_parameters_to_return_arguments
(
pi_state
,
...
...
@@ -158,7 +162,6 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
pi_state
,
extern_symbol
,
&
self
.format_string_index_map
,
self
.runtime_memory_image
,
)
{
let
return_values
:
Vec
<
String
>
=
source_string
.split
(
' '
)
.map
(|
s
|
s
.to_string
())
.collect
();
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/string_abstraction/context/symbol_calls/sprintf.rs
View file @
170f44b1
...
...
@@ -25,7 +25,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
if
let
Some
(
return_arg
)
=
extern_symbol
.parameters
.first
()
{
if
let
Some
(
pi_state
)
=
state
.get_pointer_inference_state
()
{
if
let
Ok
(
return_pointer
)
=
pi_state
.eval_parameter_arg
(
return_arg
,
self
.runtime_memory_image
)
pi_state
.eval_parameter_arg
(
return_arg
,
&
self
.project
.runtime_memory_image
)
{
if
!
return_pointer
.get_relative_values
()
.is_empty
()
{
let
format_string_index
=
self
...
...
@@ -63,7 +63,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
pi_state
,
extern_symbol
,
format_string_index
,
self
.runtime_memory_image
,
&
self
.project
.runtime_memory_image
,
)
{
let
returned_abstract_domain
=
self
.create_string_domain_for_sprintf_snprintf
(
pi_state
,
...
...
@@ -105,7 +105,6 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
pi_state
,
extern_symbol
,
&
self
.format_string_index_map
,
self
.runtime_memory_image
,
)
{
Ok
(
var_args
)
=>
{
if
var_args
.is_empty
()
{
...
...
@@ -258,7 +257,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
pi_state
:
&
PointerInferenceState
,
state
:
&
State
<
T
>
,
)
->
T
{
if
let
Ok
(
data
)
=
pi_state
.eval_parameter_arg
(
arg
,
self
.runtime_memory_image
)
{
if
let
Ok
(
data
)
=
pi_state
.eval_parameter_arg
(
arg
,
&
self
.project
.runtime_memory_image
)
{
let
constant_domain
:
Option
<
T
>
=
self
.fetch_constant_domain_if_available
(
&
data
,
arg
);
if
let
Some
(
generated_domain
)
=
Context
::
<
T
>
::
fetch_subdomains_if_available
(
&
data
,
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/string_abstraction/context/symbol_calls/strcat.rs
View file @
170f44b1
...
...
@@ -17,7 +17,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
if
let
Some
(
pi_state
)
=
state
.get_pointer_inference_state
()
{
if
let
Some
(
return_arg
)
=
extern_symbol
.parameters
.first
()
{
if
let
Ok
(
return_pointer
)
=
pi_state
.eval_parameter_arg
(
return_arg
,
self
.runtime_memory_image
)
pi_state
.eval_parameter_arg
(
return_arg
,
&
self
.project
.runtime_memory_image
)
{
if
!
return_pointer
.get_relative_values
()
.is_empty
()
{
let
target_domain
=
...
...
@@ -64,7 +64,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
let
mut
input_domain
=
T
::
create_top_value_domain
();
if
let
Some
(
input_arg
)
=
extern_symbol
.parameters
.get
(
1
)
{
if
let
Ok
(
input_value
)
=
pi_state
.eval_parameter_arg
(
input_arg
,
self
.runtime_memory_image
)
pi_state
.eval_parameter_arg
(
input_arg
,
&
self
.project
.runtime_memory_image
)
{
// Check whether the second input string is in read only memory or on stack/heap.
if
!
input_value
.get_relative_values
()
.is_empty
()
{
...
...
@@ -78,6 +78,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
if
let
Some
(
value
)
=
input_value
.get_absolute_value
()
{
if
let
Ok
(
global_address
)
=
value
.try_to_bitvec
()
{
if
let
Ok
(
input_string
)
=
self
.project
.runtime_memory_image
.read_string_until_null_terminator
(
&
global_address
)
{
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/string_abstraction/context/symbol_calls/tests.rs
View file @
170f44b1
...
...
@@ -61,7 +61,6 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String> + Debu
mock_string_symbol_map
(
&
pi_context
.project
),
mock_format_index_map
(),
&
pi_results
,
&
pi_context
.runtime_memory_image
,
);
let
state_before_call
:
State
<
T
>
=
State
::
mock_with_given_pi_state
(
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/string_abstraction/context/tests.rs
View file @
170f44b1
...
...
@@ -6,7 +6,6 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
string_symbols
:
HashMap
<
Tid
,
&
'a
ExternSymbol
>
,
format_string_index
:
HashMap
<
String
,
usize
>
,
pointer_inference_results
:
&
'a
PointerInferenceComputation
<
'a
>
,
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
)
->
Self
{
let
mut
extern_symbol_map
=
HashMap
::
new
();
for
(
tid
,
symbol
)
in
project
.program.term.extern_symbols
.iter
()
{
...
...
@@ -35,7 +34,6 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>> Conte
Context
{
project
,
runtime_memory_image
,
pointer_inference_results
,
string_symbol_map
:
string_symbols
,
extern_symbol_map
,
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/string_abstraction/context/trait_impls.rs
View file @
170f44b1
...
...
@@ -46,7 +46,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>>
new_state
.handle_assign_and_load
(
output
,
input
,
self
.runtime_memory_image
,
&
self
.project
.runtime_memory_image
,
&
self
.block_first_def_set
,
true
,
);
...
...
@@ -58,7 +58,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>>
new_state
.handle_assign_and_load
(
output
,
input
,
self
.runtime_memory_image
,
&
self
.project
.runtime_memory_image
,
&
self
.block_first_def_set
,
false
,
);
...
...
@@ -66,7 +66,7 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>>
Def
::
Store
{
address
,
value
}
=>
new_state
.handle_store
(
address
,
value
,
self
.runtime_memory_image
,
&
self
.project
.runtime_memory_image
,
&
self
.block_first_def_set
,
),
}
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/string_abstraction/context/trait_impls/tests.rs
View file @
170f44b1
...
...
@@ -13,7 +13,6 @@ use crate::{
},
},
intermediate_representation
::{
Bitvector
,
Blk
,
ByteSize
,
ExternSymbol
,
Jmp
,
Tid
,
Variable
},
utils
::
binary
::
RuntimeMemoryImage
,
};
#[test]
...
...
@@ -23,7 +22,6 @@ fn test_update_def() {
vec!
[(
memcpy_symbol
.clone
(),
vec!
[
true
])],
"func"
,
);
let
mem_image
=
RuntimeMemoryImage
::
mock
();
let
mut
pi_results
=
PointerInferenceComputation
::
mock
(
&
project
);
pi_results
.compute
(
false
);
...
...
@@ -62,7 +60,7 @@ fn test_update_def() {
let
_
=
setup
.pi_state_before_symbol_call
.store_value
(
&
pointer_to_pointer
,
&
loaded_pointer
,
&
mem
_image
,
&
project
.runtime_memory
_image
,
);
let
r2_reg
=
Variable
{
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/string_abstraction/mod.rs
View file @
170f44b1
...
...
@@ -10,7 +10,6 @@ use crate::{
abstract_domain
::{
AbstractDomain
,
DomainInsertion
,
HasTop
},
intermediate_representation
::
Project
,
prelude
::
*
,
utils
::
binary
::
RuntimeMemoryImage
,
};
use
self
::
state
::
State
;
...
...
@@ -49,17 +48,11 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>>
/// Generate a new string abstraction computation for a project.
pub
fn
new
(
project
:
&
'a
Project
,
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
control_flow_graph
:
&
'a
Graph
<
'a
>
,
pointer_inference_results
:
&
'a
PointerInferenceComputation
<
'a
>
,
config
:
Config
,
)
->
StringAbstraction
<
'a
,
T
>
{
let
context
=
Context
::
new
(
project
,
runtime_memory_image
,
pointer_inference_results
,
config
,
);
let
context
=
Context
::
new
(
project
,
pointer_inference_results
,
config
);
let
mut
sub_to_entry_blocks_map
=
HashMap
::
new
();
for
sub
in
project
.program.term.subs
.values
()
{
...
...
@@ -132,18 +125,12 @@ impl<'a, T: AbstractDomain + DomainInsertion + HasTop + Eq + From<String>>
/// Compute the string abstraction and return its results.
pub
fn
run
<
'a
,
T
:
AbstractDomain
+
HasTop
+
Eq
+
From
<
String
>
+
DomainInsertion
>
(
project
:
&
'a
Project
,
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
control_flow_graph
:
&
'a
Graph
<
'a
>
,
pointer_inference
:
&
'a
PointerInferenceComputation
<
'a
>
,
config
:
Config
,
)
->
StringAbstraction
<
'a
,
T
>
{
let
mut
string_abstraction
=
StringAbstraction
::
new
(
project
,
runtime_memory_image
,
control_flow_graph
,
pointer_inference
,
config
,
);
let
mut
string_abstraction
=
StringAbstraction
::
new
(
project
,
control_flow_graph
,
pointer_inference
,
config
);
string_abstraction
.compute
();
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/string_abstraction/state/mod.rs
View file @
170f44b1
...
...
@@ -9,14 +9,13 @@ use itertools::Itertools;
use
petgraph
::
graph
::
NodeIndex
;
use
crate
::
abstract_domain
::{
DataDomain
,
DomainInsertion
,
HasTop
,
TryToBitvec
};
use
crate
::
intermediate_representation
::{
ExternSymbol
,
Project
};
use
crate
::
intermediate_representation
::{
ExternSymbol
,
Project
,
RuntimeMemoryImage
};
use
crate
::{
abstract_domain
::
IntervalDomain
,
prelude
::
*
};
use
crate
::{
abstract_domain
::{
AbstractDomain
,
AbstractIdentifier
},
analysis
::
pointer_inference
::
PointerInference
as
PointerInferenceComputation
,
analysis
::
pointer_inference
::
State
as
PointerInferenceState
,
intermediate_representation
::{
Expression
,
Sub
,
Variable
},
utils
::
binary
::
RuntimeMemoryImage
,
};
/// Contains all information known about the state of a program at a specific point of time.
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_134.rs
View file @
170f44b1
...
...
@@ -31,8 +31,8 @@ use crate::analysis::interprocedural_fixpoint_generic::NodeValue;
use
crate
::
analysis
::
pointer_inference
::
PointerInference
;
use
crate
::
intermediate_representation
::
ExternSymbol
;
use
crate
::
intermediate_representation
::
Jmp
;
use
crate
::
intermediate_representation
::
RuntimeMemoryImage
;
use
crate
::
prelude
::
*
;
use
crate
::
utils
::
binary
::
RuntimeMemoryImage
;
use
crate
::
utils
::
log
::
CweWarning
;
use
crate
::
utils
::
log
::
LogMessage
;
use
crate
::
CweModule
;
...
...
@@ -91,7 +91,7 @@ pub fn check_cwe(
symbol
,
&
format_string_index
,
pointer_inference_results
,
analysis_results
.runtime_memory_image
,
&
analysis_results
.project
.runtime_memory_image
,
);
if
matches!
(
...
...
@@ -219,7 +219,6 @@ pub mod tests {
#[test]
fn
test_locate_format_string
()
{
let
sprintf_symbol
=
ExternSymbol
::
mock_string
();
let
runtime_memory_image
=
RuntimeMemoryImage
::
mock
();
let
project
=
mock_project
();
let
graph
=
crate
::
analysis
::
graph
::
get_program_cfg
(
&
project
.program
,
HashSet
::
new
());
let
mut
pi_results
=
PointerInferenceComputation
::
mock
(
&
project
);
...
...
@@ -241,7 +240,7 @@ pub mod tests {
&
sprintf_symbol
,
&
format_string_index
,
&
pi_results
,
&
runtime_memory_image
,
&
project
.
runtime_memory_image
,
),
StringLocation
::
GlobalReadable
);
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_467.rs
View file @
170f44b1
...
...
@@ -24,7 +24,6 @@ use crate::abstract_domain::TryToBitvec;
use
crate
::
analysis
::
pointer_inference
::
State
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
prelude
::
*
;
use
crate
::
utils
::
binary
::
RuntimeMemoryImage
;
use
crate
::
utils
::
log
::{
CweWarning
,
LogMessage
};
use
crate
::
utils
::
symbol_utils
::{
get_callsites
,
get_symbol_map
};
use
crate
::
CweModule
;
...
...
@@ -45,24 +44,20 @@ pub struct Config {
/// Compute the program state at the end of the given basic block
/// assuming nothing is known about the state at the start of the block.
fn
compute_block_end_state
(
project
:
&
Project
,
global_memory
:
&
RuntimeMemoryImage
,
block
:
&
Term
<
Blk
>
,
)
->
State
{
fn
compute_block_end_state
(
project
:
&
Project
,
block
:
&
Term
<
Blk
>
)
->
State
{
let
stack_register
=
&
project
.stack_pointer_register
;
let
mut
state
=
State
::
new
(
stack_register
,
block
.tid
.clone
());
for
def
in
block
.term.defs
.iter
()
{
match
&
def
.term
{
Def
::
Store
{
address
,
value
}
=>
{
let
_
=
state
.handle_store
(
address
,
value
,
global_memory
);
let
_
=
state
.handle_store
(
address
,
value
,
&
project
.runtime_memory_image
);
}
Def
::
Assign
{
var
,
value
}
=>
{
let
_
=
state
.handle_register_assign
(
var
,
value
);
}
Def
::
Load
{
var
,
address
}
=>
{
let
_
=
state
.handle_load
(
var
,
address
,
global_memory
);
let
_
=
state
.handle_load
(
var
,
address
,
&
project
.runtime_memory_image
);
}
}
}
...
...
@@ -72,14 +67,13 @@ fn compute_block_end_state(
/// Check whether a parameter value of the call to `symbol` has value `sizeof(void*)`.
fn
check_for_pointer_sized_arg
(
project
:
&
Project
,
global_memory
:
&
RuntimeMemoryImage
,
block
:
&
Term
<
Blk
>
,
symbol
:
&
ExternSymbol
,
)
->
bool
{
let
pointer_size
=
project
.stack_pointer_register.size
;
let
state
=
compute_block_end_state
(
project
,
global_memory
,
block
);
let
state
=
compute_block_end_state
(
project
,
block
);
for
parameter
in
symbol
.parameters
.iter
()
{
if
let
Ok
(
param
)
=
state
.eval_parameter_arg
(
parameter
,
global_memory
)
{
if
let
Ok
(
param
)
=
state
.eval_parameter_arg
(
parameter
,
&
project
.runtime_memory_image
)
{
if
let
Ok
(
param_value
)
=
param
.try_to_bitvec
()
{
if
Ok
(
u64
::
from
(
pointer_size
))
==
param_value
.try_to_u64
()
{
return
true
;
...
...
@@ -120,12 +114,7 @@ pub fn check_cwe(
let
symbol_map
=
get_symbol_map
(
project
,
&
config
.symbols
);
for
sub
in
project
.program.term.subs
.values
()
{
for
(
block
,
jmp
,
symbol
)
in
get_callsites
(
sub
,
&
symbol_map
)
{
if
check_for_pointer_sized_arg
(
project
,
analysis_results
.runtime_memory_image
,
block
,
symbol
,
)
{
if
check_for_pointer_sized_arg
(
project
,
block
,
symbol
)
{
cwe_warnings
.push
(
generate_cwe_warning
(
jmp
,
symbol
))
}
}
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_476.rs
View file @
170f44b1
...
...
@@ -85,12 +85,7 @@ pub fn check_cwe(
let
config
:
Config
=
serde_json
::
from_value
(
cwe_params
.clone
())
.unwrap
();
let
symbol_map
=
crate
::
utils
::
symbol_utils
::
get_symbol_map
(
project
,
&
config
.symbols
[
..
]);
let
general_context
=
Context
::
new
(
project
,
analysis_results
.runtime_memory_image
,
pointer_inference_results
,
cwe_sender
,
);
let
general_context
=
Context
::
new
(
project
,
pointer_inference_results
,
cwe_sender
);
for
edge
in
general_context
.get_graph
()
.edge_references
()
{
if
let
Edge
::
ExternCallStub
(
jmp
)
=
edge
.weight
()
{
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_476/context.rs
View file @
170f44b1
...
...
@@ -8,7 +8,6 @@ use crate::analysis::interprocedural_fixpoint_generic::NodeValue;
use
crate
::
analysis
::
pointer_inference
::
PointerInference
as
PointerInferenceComputation
;
use
crate
::
analysis
::
pointer_inference
::
State
as
PointerInferenceState
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
utils
::
binary
::
RuntimeMemoryImage
;
use
crate
::
utils
::
log
::
CweWarning
;
use
petgraph
::
graph
::
NodeIndex
;
use
petgraph
::
visit
::
IntoNodeReferences
;
...
...
@@ -26,8 +25,6 @@ use std::sync::Arc;
pub
struct
Context
<
'a
>
{
/// A pointer to the corresponding project struct.
project
:
&
'a
Project
,
/// A pointer to the representation of the runtime memory image.
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
/// A pointer to the results of the pointer inference analysis.
/// They are used to determine the targets of pointers to memory,
/// which in turn is used to keep track of taint on the stack or on the heap.
...
...
@@ -64,7 +61,6 @@ impl<'a> Context<'a> {
/// since this function can be expensive!
pub
fn
new
(
project
:
&
'a
Project
,
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
pointer_inference_results
:
&
'a
PointerInferenceComputation
<
'a
>
,
cwe_collector
:
crossbeam_channel
::
Sender
<
CweWarning
>
,
)
->
Self
{
...
...
@@ -92,7 +88,6 @@ impl<'a> Context<'a> {
}
Context
{
project
,
runtime_memory_image
,
pointer_inference_results
,
block_start_node_map
:
Arc
::
new
(
block_start_node_map
),
extern_symbol_map
:
Arc
::
new
(
extern_symbol_map
),
...
...
@@ -203,8 +198,8 @@ impl<'a> Context<'a> {
{
return
true
;
}
if
let
Ok
(
stack_param
)
=
pi_state
.eval_parameter_arg
(
parameter
,
self
.runtime_memory_image
)
if
let
Ok
(
stack_param
)
=
pi_state
.eval_parameter_arg
(
parameter
,
&
self
.project
.runtime_memory_image
)
{
if
state
.check_if_address_points_to_taint
(
stack_param
,
pi_state
)
{
return
true
;
...
...
@@ -417,16 +412,14 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
#[cfg(test)]
mod
tests
{
use
super
::
*
;
use
crate
::
utils
::
binary
::
RuntimeMemoryImage
;
impl
<
'a
>
Context
<
'a
>
{
pub
fn
mock
(
project
:
&
'a
Project
,
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
pi_results
:
&
'a
PointerInferenceComputation
<
'a
>
,
)
->
Context
<
'a
>
{
let
(
cwe_sender
,
_
)
=
crossbeam_channel
::
unbounded
();
let
mut
context
=
Context
::
new
(
project
,
runtime_memory_image
,
pi_results
,
cwe_sender
);
let
mut
context
=
Context
::
new
(
project
,
pi_results
,
cwe_sender
);
let
taint_source
=
Box
::
new
(
Term
{
tid
:
Tid
::
new
(
"taint_source"
),
term
:
Jmp
::
Call
{
...
...
@@ -445,9 +438,8 @@ mod tests {
#[test]
fn
check_parameter_arg_for_taint
()
{
let
project
=
Project
::
mock_x64
();
let
runtime_memory_image
=
RuntimeMemoryImage
::
mock
();
let
pi_results
=
PointerInferenceComputation
::
mock
(
&
project
);
let
context
=
Context
::
mock
(
&
project
,
&
runtime_memory_image
,
&
pi_results
);
let
context
=
Context
::
mock
(
&
project
,
&
pi_results
);
let
(
mut
state
,
_pi_state
)
=
State
::
mock_with_pi_state
();
assert_eq!
(
...
...
@@ -476,9 +468,8 @@ mod tests {
#[test]
fn
handle_generic_call
()
{
let
project
=
Project
::
mock_x64
();
let
runtime_memory_image
=
RuntimeMemoryImage
::
mock
();
let
pi_results
=
PointerInferenceComputation
::
mock
(
&
project
);
let
context
=
Context
::
mock
(
&
project
,
&
runtime_memory_image
,
&
pi_results
);
let
context
=
Context
::
mock
(
&
project
,
&
pi_results
);
let
mut
state
=
State
::
mock
();
assert
!
(
context
...
...
@@ -497,9 +488,8 @@ mod tests {
#[test]
fn
update_def
()
{
let
project
=
Project
::
mock_x64
();
let
runtime_memory_image
=
RuntimeMemoryImage
::
mock
();
let
pi_results
=
PointerInferenceComputation
::
mock
(
&
project
);
let
context
=
Context
::
mock
(
&
project
,
&
runtime_memory_image
,
&
pi_results
);
let
context
=
Context
::
mock
(
&
project
,
&
pi_results
);
let
(
mut
state
,
pi_state
)
=
State
::
mock_with_pi_state
();
state
.set_pointer_inference_state
(
Some
(
pi_state
));
...
...
@@ -550,9 +540,8 @@ mod tests {
#[test]
fn
update_jump
()
{
let
project
=
Project
::
mock_x64
();
let
runtime_memory_image
=
RuntimeMemoryImage
::
mock
();
let
pi_results
=
PointerInferenceComputation
::
mock
(
&
project
);
let
context
=
Context
::
mock
(
&
project
,
&
runtime_memory_image
,
&
pi_results
);
let
context
=
Context
::
mock
(
&
project
,
&
pi_results
);
let
(
state
,
_pi_state
)
=
State
::
mock_with_pi_state
();
let
jump
=
Term
{
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_560.rs
View file @
170f44b1
...
...
@@ -26,7 +26,6 @@ use crate::abstract_domain::TryToBitvec;
use
crate
::
analysis
::
pointer_inference
::
State
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
prelude
::
*
;
use
crate
::
utils
::
binary
::
RuntimeMemoryImage
;
use
crate
::
utils
::
log
::{
CweWarning
,
LogMessage
};
use
crate
::
utils
::
symbol_utils
::{
get_callsites
,
get_symbol_map
};
use
crate
::
CweModule
;
...
...
@@ -50,7 +49,6 @@ fn get_umask_permission_arg(
block
:
&
Term
<
Blk
>
,
umask_symbol
:
&
ExternSymbol
,
project
:
&
Project
,
global_memory
:
&
RuntimeMemoryImage
,
)
->
Result
<
u64
,
Error
>
{
let
stack_register
=
&
project
.stack_pointer_register
;
let
mut
state
=
State
::
new
(
stack_register
,
block
.tid
.clone
());
...
...
@@ -58,19 +56,19 @@ fn get_umask_permission_arg(
for
def
in
block
.term.defs
.iter
()
{
match
&
def
.term
{
Def
::
Store
{
address
,
value
}
=>
{
let
_
=
state
.handle_store
(
address
,
value
,
global_memory
);
let
_
=
state
.handle_store
(
address
,
value
,
&
project
.runtime_memory_image
);
}
Def
::
Assign
{
var
,
value
}
=>
{
let
_
=
state
.handle_register_assign
(
var
,
value
);
}
Def
::
Load
{
var
,
address
}
=>
{
let
_
=
state
.handle_load
(
var
,
address
,
global_memory
);
let
_
=
state
.handle_load
(
var
,
address
,
&
project
.runtime_memory_image
);
}
}
}
let
parameter
=
umask_symbol
.get_unique_parameter
()
?
;
let
param_value
=
state
.eval_parameter_arg
(
parameter
,
global_memory
)
?
;
let
param_value
=
state
.eval_parameter_arg
(
parameter
,
&
project
.runtime_memory_image
)
?
;
if
let
Ok
(
umask_arg
)
=
param_value
.try_to_bitvec
()
{
Ok
(
umask_arg
.try_to_u64
()
?
)
}
else
{
...
...
@@ -115,12 +113,7 @@ pub fn check_cwe(
if
!
umask_symbol_map
.is_empty
()
{
for
sub
in
project
.program.term.subs
.values
()
{
for
(
block
,
jmp
,
umask_symbol
)
in
get_callsites
(
sub
,
&
umask_symbol_map
)
{
match
get_umask_permission_arg
(
block
,
umask_symbol
,
project
,
analysis_results
.runtime_memory_image
,
)
{
match
get_umask_permission_arg
(
block
,
umask_symbol
,
project
)
{
Ok
(
permission_const
)
=>
{
if
is_chmod_style_arg
(
permission_const
)
{
cwes
.push
(
generate_cwe_warning
(
sub
,
jmp
,
permission_const
));
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_78.rs
View file @
170f44b1
...
...
@@ -45,9 +45,9 @@ use crate::intermediate_representation::Arg;
use
crate
::
intermediate_representation
::
Expression
;
use
crate
::
intermediate_representation
::
ExternSymbol
;
use
crate
::
intermediate_representation
::
Jmp
;
use
crate
::
intermediate_representation
::
RuntimeMemoryImage
;
use
crate
::
intermediate_representation
::
Sub
;
use
crate
::
prelude
::
*
;
use
crate
::
utils
::
binary
::
RuntimeMemoryImage
;
use
crate
::
utils
::
log
::
CweWarning
;
use
crate
::
utils
::
log
::
LogMessage
;
...
...
@@ -117,7 +117,10 @@ pub fn check_cwe(
&
jmp
.tid
,
&
cwe_sender
,
&
log_sender
,
string_abstraction
.get_context
()
.runtime_memory_image
,
&
string_abstraction
.get_context
()
.project
.runtime_memory_image
,
)
}
}
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/intermediate_representation/mod.rs
View file @
170f44b1
...
...
@@ -30,6 +30,8 @@ mod program;
pub
use
program
::
*
;
mod
project
;
pub
use
project
::
*
;
mod
runtime_memory_image
;
pub
use
runtime_memory_image
::
*
;
/// An unsigned number of bytes.
///
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/intermediate_representation/project.rs
View file @
170f44b1
use
super
::
*
;
use
crate
::
utils
::
log
::
LogMessage
;
use
std
::
collections
::{
BTreeMap
,
BTreeSet
,
HashMap
,
HashSet
};
mod
block_duplication_normalization
;
use
crate
::
utils
::
log
::
LogMessage
;
use
block_duplication_normalization
::
*
;
/// The `Project` struct is the main data structure representing a binary.
...
...
@@ -24,6 +23,8 @@ pub struct Project {
pub
register_set
:
BTreeSet
<
Variable
>
,
/// Contains the properties of C data types. (e.g. size)
pub
datatype_properties
:
DatatypeProperties
,
/// Represents the memory after loading the binary.
pub
runtime_memory_image
:
RuntimeMemoryImage
,
}
impl
Project
{
...
...
@@ -319,6 +320,7 @@ mod tests {
calling_conventions
,
register_set
:
integer_register
.iter
()
.cloned
()
.collect
(),
datatype_properties
:
DatatypeProperties
::
mock_x64
(),
runtime_memory_image
:
RuntimeMemoryImage
::
mock
(),
}
}
...
...
@@ -357,6 +359,7 @@ mod tests {
)]),
register_set
:
integer_register
.collect
(),
datatype_properties
:
DatatypeProperties
::
mock_arm32
(),
runtime_memory_image
:
RuntimeMemoryImage
::
mock
(),
}
}
}
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/intermediate_representation/runtime_memory_image.rs
0 → 100644
View file @
170f44b1
use
super
::
*
;
use
crate
::
utils
::
binary
::{
parse_hex_string_to_u64
,
BareMetalConfig
,
MemorySegment
};
use
goblin
::{
elf
,
Object
};
/// A representation of the runtime image of a binary after being loaded into memory by the loader.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone)]
pub
struct
RuntimeMemoryImage
{
memory_segments
:
Vec
<
MemorySegment
>
,
is_little_endian
:
bool
,
}
impl
RuntimeMemoryImage
{
/// Generate a runtime memory image containing no memory segments.
/// Primarily useful in situations where any access to global memory would be an error.
pub
fn
empty
(
is_little_endian
:
bool
)
->
RuntimeMemoryImage
{
RuntimeMemoryImage
{
memory_segments
:
Vec
::
new
(),
is_little_endian
,
}
}
/// Generate a runtime memory image for a given binary.
///
/// The function can parse ELF and PE files as input.
pub
fn
new
(
binary
:
&
[
u8
])
->
Result
<
Self
,
Error
>
{
let
parsed_object
=
Object
::
parse
(
binary
)
?
;
match
parsed_object
{
Object
::
Elf
(
elf_file
)
=>
{
let
mut
memory_segments
=
Vec
::
new
();
for
header
in
elf_file
.program_headers
.iter
()
{
if
header
.p_type
==
elf
::
program_header
::
PT_LOAD
{
memory_segments
.push
(
MemorySegment
::
from_elf_segment
(
binary
,
header
));
}
}
if
memory_segments
.is_empty
()
{
return
Err
(
anyhow!
(
"No loadable segments found"
));
}
Ok
(
RuntimeMemoryImage
{
memory_segments
,
is_little_endian
:
elf_file
.header
.endianness
()
.unwrap
()
.is_little
(),
})
}
Object
::
PE
(
pe_file
)
=>
{
let
mut
memory_segments
=
Vec
::
new
();
for
header
in
pe_file
.sections
.iter
()
{
if
(
header
.characteristics
&
0x02000000
)
==
0
{
// Only load segments which are not discardable
memory_segments
.push
(
MemorySegment
::
from_pe_section
(
binary
,
header
));
}
}
if
memory_segments
.is_empty
()
{
return
Err
(
anyhow!
(
"No loadable segments found"
));
}
let
mut
memory_image
=
RuntimeMemoryImage
{
memory_segments
,
is_little_endian
:
true
,
};
memory_image
.add_global_memory_offset
(
pe_file
.image_base
as
u64
);
Ok
(
memory_image
)
}
_
=>
Err
(
anyhow!
(
"Object type not supported."
)),
}
}
/// Generate a runtime memory image for a bare metal binary.
///
/// The generated runtime memory image contains:
/// * one memory region corresponding to non-volatile memory
/// * one memory region corresponding to volatile memory (RAM)
///
/// See [`BareMetalConfig`] for more information about the assumed memory layout for bare metal binaries.
pub
fn
new_from_bare_metal
(
binary
:
&
[
u8
],
bare_metal_config
:
&
BareMetalConfig
,
)
->
Result
<
Self
,
Error
>
{
let
processor_id_parts
:
Vec
<&
str
>
=
bare_metal_config
.processor_id
.split
(
':'
)
.collect
();
if
processor_id_parts
.len
()
<
3
{
return
Err
(
anyhow!
(
"Could not parse processor ID."
));
}
let
is_little_endian
=
match
processor_id_parts
[
1
]
{
"LE"
=>
true
,
"BE"
=>
false
,
_
=>
return
Err
(
anyhow!
(
"Could not parse endianness of the processor ID."
)),
};
let
flash_base_address
=
parse_hex_string_to_u64
(
&
bare_metal_config
.flash_base_address
)
?
;
let
ram_base_address
=
parse_hex_string_to_u64
(
&
bare_metal_config
.ram_base_address
)
?
;
let
ram_size
=
parse_hex_string_to_u64
(
&
bare_metal_config
.ram_size
)
?
;
// Check that the whole binary is contained in addressable space.
let
address_bit_length
=
processor_id_parts
[
2
]
.parse
::
<
u64
>
()
?
;
match
flash_base_address
.checked_add
(
binary
.len
()
as
u64
)
{
Some
(
max_address
)
=>
{
if
(
max_address
>>
address_bit_length
)
!=
0
{
return
Err
(
anyhow!
(
"Binary too large for given base address"
));
}
}
None
=>
return
Err
(
anyhow!
(
"Binary too large for given base address"
)),
}
Ok
(
RuntimeMemoryImage
{
memory_segments
:
vec!
[
MemorySegment
::
from_bare_metal_file
(
binary
,
flash_base_address
),
MemorySegment
::
new_bare_metal_ram_segment
(
ram_base_address
,
ram_size
),
],
is_little_endian
,
})
}
/// Return whether values in the memory image should be interpreted in little-endian
/// or big-endian byte order.
pub
fn
is_little_endian_byte_order
(
&
self
)
->
bool
{
self
.is_little_endian
}
/// Add a global offset to the base addresses of all memory segments.
/// Useful to align the addresses with those reported by Ghidra
/// if the Ghidra backend added such an offset to all addresses.
pub
fn
add_global_memory_offset
(
&
mut
self
,
offset
:
u64
)
{
for
segment
in
self
.memory_segments
.iter_mut
()
{
segment
.base_address
+=
offset
;
}
}
/// Read the contents of the memory image at the given address
/// to emulate a read instruction to global data at runtime.
///
/// The read method is endian-aware,
/// i.e. values are interpreted with the endianness of the CPU architecture.
/// If the address points to a writeable segment, the returned value is a `Ok(None)` value,
/// since the data may change during program execution.
///
/// Returns an error if the address is not contained in the global data address range.
pub
fn
read
(
&
self
,
address
:
&
Bitvector
,
size
:
ByteSize
)
->
Result
<
Option
<
Bitvector
>
,
Error
>
{
let
address
=
address
.try_to_u64
()
.unwrap
();
for
segment
in
self
.memory_segments
.iter
()
{
if
address
>=
segment
.base_address
&&
u64
::
from
(
size
)
<=
segment
.base_address
+
segment
.bytes
.len
()
as
u64
&&
address
<=
segment
.base_address
+
segment
.bytes
.len
()
as
u64
-
u64
::
from
(
size
)
{
if
segment
.write_flag
{
// The segment is writeable, thus we do not know the content at runtime.
return
Ok
(
None
);
}
let
index
=
(
address
-
segment
.base_address
)
as
usize
;
let
mut
bytes
=
segment
.bytes
[
index
..
index
+
u64
::
from
(
size
)
as
usize
]
.to_vec
();
if
self
.is_little_endian
{
bytes
=
bytes
.into_iter
()
.rev
()
.collect
();
}
let
mut
bytes
=
bytes
.into_iter
();
let
mut
bitvector
=
Bitvector
::
from_u8
(
bytes
.next
()
.unwrap
());
for
byte
in
bytes
{
let
new_byte
=
Bitvector
::
from_u8
(
byte
);
bitvector
=
bitvector
.bin_op
(
BinOpType
::
Piece
,
&
new_byte
)
?
;
}
return
Ok
(
Some
(
bitvector
));
}
}
// No segment fully contains the read.
Err
(
anyhow!
(
"Address is not a valid global memory address."
))
}
/// Read the contents of memory from a given address onwards until a null byte is reached and checks whether the
/// content is a valid UTF8 string.
pub
fn
read_string_until_null_terminator
(
&
self
,
address
:
&
Bitvector
)
->
Result
<&
str
,
Error
>
{
let
address
=
address
.try_to_u64
()
.unwrap
();
for
segment
in
self
.memory_segments
.iter
()
{
if
address
>=
segment
.base_address
&&
address
<=
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
let
start_index
=
(
address
-
segment
.base_address
)
as
usize
;
if
let
Some
(
end_index
)
=
segment
.bytes
[
start_index
..
]
.iter
()
.position
(|
&
b
|
b
==
0
)
{
let
c_str
=
std
::
ffi
::
CStr
::
from_bytes_with_nul
(
&
segment
.bytes
[
start_index
..
start_index
+
end_index
+
1
],
)
?
;
return
Ok
(
c_str
.to_str
()
?
);
}
else
{
return
Err
(
anyhow!
(
"Not a valid string in memory."
));
}
}
}
Err
(
anyhow!
(
"Address is not a valid global memory address."
))
}
/// Checks whether the constant is a global memory address.
pub
fn
is_global_memory_address
(
&
self
,
constant
:
&
Bitvector
)
->
bool
{
if
self
.read
(
constant
,
constant
.bytesize
())
.is_ok
()
{
return
true
;
}
false
}
/// Check whether all addresses in the given interval point to a readable segment in the runtime memory image.
///
/// Returns an error if the address interval intersects more than one memory segment
/// or if it does not point to global memory at all.
pub
fn
is_interval_readable
(
&
self
,
start_address
:
u64
,
end_address
:
u64
,
)
->
Result
<
bool
,
Error
>
{
for
segment
in
self
.memory_segments
.iter
()
{
if
start_address
>=
segment
.base_address
&&
start_address
<
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
if
end_address
<=
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
return
Ok
(
segment
.read_flag
);
}
else
{
return
Err
(
anyhow!
(
"Interval spans more than one segment"
));
}
}
}
Err
(
anyhow!
(
"Address not contained in runtime memory image"
))
}
/// For an address to global read-only memory, return the memory segment it points to
/// and the index inside the segment, where the address points to.
///
/// Returns an error if the target memory segment is marked as writeable
/// or if the pointer does not point to global memory.
pub
fn
get_ro_data_pointer_at_address
(
&
self
,
address
:
&
Bitvector
,
)
->
Result
<
(
&
[
u8
],
usize
),
Error
>
{
let
address
=
address
.try_to_u64
()
.unwrap
();
for
segment
in
self
.memory_segments
.iter
()
{
if
address
>=
segment
.base_address
&&
address
<
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
if
segment
.write_flag
{
return
Err
(
anyhow!
(
"Target segment is writeable"
));
}
else
{
return
Ok
((
&
segment
.bytes
,
(
address
-
segment
.base_address
)
as
usize
));
}
}
}
Err
(
anyhow!
(
"Pointer target not in global memory."
))
}
/// Check whether the given address points to a writeable segment in the runtime memory image.
///
/// Returns an error if the address does not point to global memory.
pub
fn
is_address_writeable
(
&
self
,
address
:
&
Bitvector
)
->
Result
<
bool
,
Error
>
{
let
address
=
address
.try_to_u64
()
.unwrap
();
for
segment
in
self
.memory_segments
.iter
()
{
if
address
>=
segment
.base_address
&&
address
<
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
return
Ok
(
segment
.write_flag
);
}
}
Err
(
anyhow!
(
"Address not contained in runtime memory image"
))
}
/// Check whether all addresses in the given interval point to a writeable segment in the runtime memory image.
///
/// Returns an error if the address interval intersects more than one memory segment
/// or if it does not point to global memory at all.
pub
fn
is_interval_writeable
(
&
self
,
start_address
:
u64
,
end_address
:
u64
,
)
->
Result
<
bool
,
Error
>
{
for
segment
in
self
.memory_segments
.iter
()
{
if
start_address
>=
segment
.base_address
&&
start_address
<
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
if
end_address
<=
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
return
Ok
(
segment
.write_flag
);
}
else
{
return
Err
(
anyhow!
(
"Interval spans more than one segment"
));
}
}
}
Err
(
anyhow!
(
"Address not contained in runtime memory image"
))
}
}
impl
RuntimeMemoryImage
{
/// Creates a mock runtime memory image with: byte series, strings and format strings.
pub
fn
mock
()
->
RuntimeMemoryImage
{
RuntimeMemoryImage
{
memory_segments
:
vec!
[
MemorySegment
{
bytes
:
[
0xb0u8
,
0xb1
,
0xb2
,
0xb3
,
0xb4
]
.to_vec
(),
base_address
:
0x1000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
},
MemorySegment
{
bytes
:
[
0u8
;
8
]
.to_vec
(),
base_address
:
0x2000
,
read_flag
:
true
,
write_flag
:
true
,
execute_flag
:
false
,
},
// Contains the Hello World string at byte 3002.
MemorySegment
{
bytes
:
[
0x01
,
0x02
,
0x48
,
0x65
,
0x6c
,
0x6c
,
0x6f
,
0x20
,
0x57
,
0x6f
,
0x72
,
0x6c
,
0x64
,
0x00
,
]
.to_vec
(),
base_address
:
0x3000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
},
MemorySegment
{
bytes
:
[
0x02
,
0x30
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
]
.to_vec
(),
base_address
:
0x4000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
},
// Contains strings: '/dev/sd%c%d' and 'cat %s'
MemorySegment
{
bytes
:
[
0x2f
,
0x64
,
0x65
,
0x76
,
0x2f
,
0x73
,
0x64
,
0x25
,
0x63
,
0x25
,
0x64
,
0x00
,
0x63
,
0x61
,
0x74
,
0x20
,
0x25
,
0x73
,
0x00
,
]
.to_vec
(),
base_address
:
0x5000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
},
// Contains string: 'cat %s %s %s %s' starting at the first byte.
MemorySegment
{
bytes
:
[
0x63
,
0x61
,
0x74
,
0x20
,
0x25
,
0x73
,
0x20
,
0x25
,
0x73
,
0x20
,
0x25
,
0x73
,
0x20
,
0x25
,
0x73
,
0x00
,
]
.to_vec
(),
base_address
:
0x6000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
},
// Contains string: 'str1 str2 str3 str4'
MemorySegment
{
bytes
:
[
0x73
,
0x74
,
0x72
,
0x31
,
0x20
,
0x73
,
0x74
,
0x72
,
0x32
,
0x20
,
0x73
,
0x74
,
0x72
,
0x33
,
0x20
,
0x73
,
0x74
,
0x72
,
0x34
,
0x00
,
]
.to_vec
(),
base_address
:
0x7000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
},
],
is_little_endian
:
true
,
}
}
}
#[cfg(test)]
mod
tests
{
use
crate
::
intermediate_representation
::{
Bitvector
,
ByteSize
,
RuntimeMemoryImage
};
#[test]
fn
read_endianness
()
{
let
mut
mem_image
=
RuntimeMemoryImage
::
mock
();
let
address
=
Bitvector
::
from_u32
(
0x1001
);
assert_eq!
(
mem_image
.read
(
&
address
,
ByteSize
::
new
(
4
))
.unwrap
(),
Bitvector
::
from_u32
(
0xb4b3b2b1
)
.into
()
);
mem_image
.is_little_endian
=
false
;
assert_eq!
(
mem_image
.read
(
&
address
,
ByteSize
::
new
(
4
))
.unwrap
(),
Bitvector
::
from_u32
(
0xb1b2b3b4
)
.into
()
);
}
#[test]
fn
ro_data_pointer
()
{
let
mem_image
=
RuntimeMemoryImage
::
mock
();
let
address
=
Bitvector
::
from_u32
(
0x1002
);
let
(
slice
,
index
)
=
mem_image
.get_ro_data_pointer_at_address
(
&
address
)
.unwrap
();
assert_eq!
(
index
,
2
);
assert_eq!
(
&
slice
[
index
..
],
&
[
0xb2u8
,
0xb3
,
0xb4
]);
}
#[test]
fn
test_read_string_until_null_terminator
()
{
let
mem_image
=
RuntimeMemoryImage
::
mock
();
// the byte array contains "Hello World".
let
expected_string
:
&
str
=
std
::
str
::
from_utf8
(
b
"
\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64
"
)
.unwrap
();
let
address
=
Bitvector
::
from_u32
(
0x3002
);
assert_eq!
(
expected_string
,
mem_image
.read_string_until_null_terminator
(
&
address
)
.unwrap
(),
);
}
}
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/lib.rs
View file @
170f44b1
...
...
@@ -74,8 +74,8 @@ use analysis::function_signature::FunctionSignature;
use
analysis
::
graph
::
Graph
;
use
analysis
::
pointer_inference
::
PointerInference
;
use
analysis
::
string_abstraction
::
StringAbstraction
;
use
intermediate_representation
::
Project
;
use
utils
::
binary
::
RuntimeMemoryImage
;
use
utils
::
log
::{
CweWarning
,
LogMessage
};
mod
prelude
{
...
...
@@ -140,8 +140,6 @@ pub fn get_modules() -> Vec<&'static CweModule> {
pub
struct
AnalysisResults
<
'a
>
{
/// The content of the binary file
pub
binary
:
&
'a
[
u8
],
/// A representation of the runtime memory image of the binary.
pub
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
/// The computed control flow graph of the program.
pub
control_flow_graph
:
&
'a
Graph
<
'a
>
,
/// A pointer to the project struct
...
...
@@ -158,13 +156,11 @@ impl<'a> AnalysisResults<'a> {
/// Create a new `AnalysisResults` struct with only the project itself known.
pub
fn
new
(
binary
:
&
'a
[
u8
],
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
control_flow_graph
:
&
'a
Graph
<
'a
>
,
project
:
&
'a
Project
,
)
->
AnalysisResults
<
'a
>
{
AnalysisResults
{
binary
,
runtime_memory_image
,
control_flow_graph
,
project
,
function_signatures
:
None
,
...
...
@@ -231,7 +227,6 @@ impl<'a> AnalysisResults<'a> {
)
->
StringAbstraction
<
BricksDomain
>
{
crate
::
analysis
::
string_abstraction
::
run
(
self
.project
,
self
.runtime_memory_image
,
self
.control_flow_graph
,
pi_results
.unwrap
(),
serde_json
::
from_value
(
config
.clone
())
.unwrap
(),
...
...
@@ -264,11 +259,8 @@ mod tests {
HashSet
::
from_iter
(
project
.program.term.extern_symbols
.keys
()
.cloned
());
let
graph
=
Box
::
new
(
get_program_cfg
(
&
project
.program
,
extern_subs
));
let
graph
:
&
'a
Graph
=
Box
::
leak
(
graph
);
let
runtime_mem_image
=
Box
::
new
(
RuntimeMemoryImage
::
mock
());
let
runtime_mem_image
:
&
'a
RuntimeMemoryImage
=
Box
::
leak
(
runtime_mem_image
);
let
binary
:
&
'a
Vec
<
u8
>
=
Box
::
leak
(
Box
::
new
(
Vec
::
new
()));
let
analysis_results
=
AnalysisResults
::
new
(
binary
,
runtime_mem_image
,
graph
,
project
);
let
analysis_results
=
AnalysisResults
::
new
(
binary
,
graph
,
project
);
let
(
fn_sigs
,
_
)
=
analysis_results
.compute_function_signatures
();
let
fn_sigs
:
&
'a
BTreeMap
<
_
,
_
>
=
Box
::
leak
(
Box
::
new
(
fn_sigs
));
let
analysis_results
=
analysis_results
.with_function_signatures
(
Some
(
fn_sigs
));
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/pcode/term.rs
View file @
170f44b1
...
...
@@ -13,6 +13,7 @@ use crate::intermediate_representation::ExternSymbol as IrExternSymbol;
use
crate
::
intermediate_representation
::
Jmp
as
IrJmp
;
use
crate
::
intermediate_representation
::
Program
as
IrProgram
;
use
crate
::
intermediate_representation
::
Project
as
IrProject
;
use
crate
::
intermediate_representation
::
RuntimeMemoryImage
;
use
crate
::
intermediate_representation
::
Sub
as
IrSub
;
use
crate
::
intermediate_representation
::
Variable
as
IrVariable
;
use
crate
::
prelude
::
*
;
...
...
@@ -870,6 +871,7 @@ impl Project {
calling_conventions
,
register_set
,
datatype_properties
:
self
.datatype_properties
.clone
(),
runtime_memory_image
:
RuntimeMemoryImage
::
empty
(
true
),
}
}
}
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/utils/arguments.rs
View file @
170f44b1
//! Handles argument detection by parsing format string arguments during a function call. (e.g. sprintf)
use
super
::
binary
::
RuntimeMemoryImage
;
use
crate
::
prelude
::
*
;
use
crate
::{
abstract_domain
::{
IntervalDomain
,
TryToBitvec
},
...
...
@@ -105,7 +104,6 @@ pub fn get_variable_parameters(
pi_state
:
&
PointerInferenceState
,
extern_symbol
:
&
ExternSymbol
,
format_string_index_map
:
&
HashMap
<
String
,
usize
>
,
runtime_memory_image
:
&
RuntimeMemoryImage
,
)
->
Result
<
Vec
<
Arg
>
,
Error
>
{
let
format_string_index
=
match
format_string_index_map
.get
(
&
extern_symbol
.name
)
{
Some
(
index
)
=>
*
index
,
...
...
@@ -116,7 +114,7 @@ pub fn get_variable_parameters(
pi_state
,
extern_symbol
,
format_string_index
,
runtime_memory_image
,
&
project
.
runtime_memory_image
,
);
if
let
Ok
(
format_string
)
=
format_string_results
.as_ref
()
{
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/utils/arguments/tests.rs
View file @
170f44b1
...
...
@@ -9,7 +9,6 @@ fn mock_pi_state() -> PointerInferenceState {
#[test]
/// Tests extraction of format string parameters '/dev/sd%c%d' and 'cat %s'.
fn
test_get_variable_parameters
()
{
let
mem_image
=
RuntimeMemoryImage
::
mock
();
let
mut
pi_state
=
mock_pi_state
();
let
sprintf_symbol
=
ExternSymbol
::
mock_string
();
let
mut
format_string_index_map
:
HashMap
<
String
,
usize
>
=
HashMap
::
new
();
...
...
@@ -38,7 +37,6 @@ fn test_get_variable_parameters() {
&
pi_state
,
&
sprintf_symbol
,
&
format_string_index_map
,
&
mem_image
,
)
.unwrap
()
);
...
...
@@ -61,7 +59,6 @@ fn test_get_variable_parameters() {
&
pi_state
,
&
sprintf_symbol
,
&
format_string_index_map
,
&
mem_image
,
)
.unwrap
()
);
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/utils/binary.rs
View file @
170f44b1
//! Utility structs and functions which directly parse the binary file.
use
crate
::
intermediate_representation
::
BinOpType
;
use
crate
::
intermediate_representation
::
BitvectorExtended
;
use
crate
::
prelude
::
*
;
use
goblin
::
elf
;
use
goblin
::
pe
;
use
goblin
::
Object
;
/// Contains all information parsed out of the bare metal configuration JSON file.
///
...
...
@@ -50,23 +47,16 @@ impl BareMetalConfig {
}
/// A helper function to parse a hex string to an integer.
fn
parse_hex_string_to_u64
(
mut
string
:
&
str
)
->
Result
<
u64
,
Error
>
{
pub
fn
parse_hex_string_to_u64
(
mut
string
:
&
str
)
->
Result
<
u64
,
Error
>
{
if
string
.starts_with
(
"0x"
)
{
string
=
&
string
[
2
..
]
}
Ok
(
u64
::
from_str_radix
(
string
,
16
)
?
)
}
/// A representation of the runtime image of a binary after being loaded into memory by the loader.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone)]
pub
struct
RuntimeMemoryImage
{
memory_segments
:
Vec
<
MemorySegment
>
,
is_little_endian
:
bool
,
}
/// A continuous segment in the memory image.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone)]
struct
MemorySegment
{
pub
struct
MemorySegment
{
/// The contents of the segment
pub
bytes
:
Vec
<
u8
>
,
/// The base address, i.e. the address of the first byte of the segment
...
...
@@ -143,394 +133,3 @@ impl MemorySegment {
}
}
}
impl
RuntimeMemoryImage
{
/// Generate a runtime memory image containing no memory segments.
/// Primarily useful in situations where any access to global memory would be an error.
pub
fn
empty
(
is_little_endian
:
bool
)
->
RuntimeMemoryImage
{
RuntimeMemoryImage
{
memory_segments
:
Vec
::
new
(),
is_little_endian
,
}
}
/// Generate a runtime memory image for a given binary.
///
/// The function can parse ELF and PE files as input.
pub
fn
new
(
binary
:
&
[
u8
])
->
Result
<
Self
,
Error
>
{
let
parsed_object
=
Object
::
parse
(
binary
)
?
;
match
parsed_object
{
Object
::
Elf
(
elf_file
)
=>
{
let
mut
memory_segments
=
Vec
::
new
();
for
header
in
elf_file
.program_headers
.iter
()
{
if
header
.p_type
==
elf
::
program_header
::
PT_LOAD
{
memory_segments
.push
(
MemorySegment
::
from_elf_segment
(
binary
,
header
));
}
}
if
memory_segments
.is_empty
()
{
return
Err
(
anyhow!
(
"No loadable segments found"
));
}
Ok
(
RuntimeMemoryImage
{
memory_segments
,
is_little_endian
:
elf_file
.header
.endianness
()
.unwrap
()
.is_little
(),
})
}
Object
::
PE
(
pe_file
)
=>
{
let
mut
memory_segments
=
Vec
::
new
();
for
header
in
pe_file
.sections
.iter
()
{
if
(
header
.characteristics
&
0x02000000
)
==
0
{
// Only load segments which are not discardable
memory_segments
.push
(
MemorySegment
::
from_pe_section
(
binary
,
header
));
}
}
if
memory_segments
.is_empty
()
{
return
Err
(
anyhow!
(
"No loadable segments found"
));
}
let
mut
memory_image
=
RuntimeMemoryImage
{
memory_segments
,
is_little_endian
:
true
,
};
memory_image
.add_global_memory_offset
(
pe_file
.image_base
as
u64
);
Ok
(
memory_image
)
}
_
=>
Err
(
anyhow!
(
"Object type not supported."
)),
}
}
/// Generate a runtime memory image for a bare metal binary.
///
/// The generated runtime memory image contains:
/// * one memory region corresponding to non-volatile memory
/// * one memory region corresponding to volatile memory (RAM)
///
/// See [`BareMetalConfig`] for more information about the assumed memory layout for bare metal binaries.
pub
fn
new_from_bare_metal
(
binary
:
&
[
u8
],
bare_metal_config
:
&
BareMetalConfig
,
)
->
Result
<
Self
,
Error
>
{
let
processor_id_parts
:
Vec
<&
str
>
=
bare_metal_config
.processor_id
.split
(
':'
)
.collect
();
if
processor_id_parts
.len
()
<
3
{
return
Err
(
anyhow!
(
"Could not parse processor ID."
));
}
let
is_little_endian
=
match
processor_id_parts
[
1
]
{
"LE"
=>
true
,
"BE"
=>
false
,
_
=>
return
Err
(
anyhow!
(
"Could not parse endianness of the processor ID."
)),
};
let
flash_base_address
=
parse_hex_string_to_u64
(
&
bare_metal_config
.flash_base_address
)
?
;
let
ram_base_address
=
parse_hex_string_to_u64
(
&
bare_metal_config
.ram_base_address
)
?
;
let
ram_size
=
parse_hex_string_to_u64
(
&
bare_metal_config
.ram_size
)
?
;
// Check that the whole binary is contained in addressable space.
let
address_bit_length
=
processor_id_parts
[
2
]
.parse
::
<
u64
>
()
?
;
match
flash_base_address
.checked_add
(
binary
.len
()
as
u64
)
{
Some
(
max_address
)
=>
{
if
(
max_address
>>
address_bit_length
)
!=
0
{
return
Err
(
anyhow!
(
"Binary too large for given base address"
));
}
}
None
=>
return
Err
(
anyhow!
(
"Binary too large for given base address"
)),
}
Ok
(
RuntimeMemoryImage
{
memory_segments
:
vec!
[
MemorySegment
::
from_bare_metal_file
(
binary
,
flash_base_address
),
MemorySegment
::
new_bare_metal_ram_segment
(
ram_base_address
,
ram_size
),
],
is_little_endian
,
})
}
/// Return whether values in the memory image should be interpreted in little-endian
/// or big-endian byte order.
pub
fn
is_little_endian_byte_order
(
&
self
)
->
bool
{
self
.is_little_endian
}
/// Add a global offset to the base addresses of all memory segments.
/// Useful to align the addresses with those reported by Ghidra
/// if the Ghidra backend added such an offset to all addresses.
pub
fn
add_global_memory_offset
(
&
mut
self
,
offset
:
u64
)
{
for
segment
in
self
.memory_segments
.iter_mut
()
{
segment
.base_address
+=
offset
;
}
}
/// Read the contents of the memory image at the given address
/// to emulate a read instruction to global data at runtime.
///
/// The read method is endian-aware,
/// i.e. values are interpreted with the endianness of the CPU architecture.
/// If the address points to a writeable segment, the returned value is a `Ok(None)` value,
/// since the data may change during program execution.
///
/// Returns an error if the address is not contained in the global data address range.
pub
fn
read
(
&
self
,
address
:
&
Bitvector
,
size
:
ByteSize
)
->
Result
<
Option
<
Bitvector
>
,
Error
>
{
let
address
=
address
.try_to_u64
()
.unwrap
();
for
segment
in
self
.memory_segments
.iter
()
{
if
address
>=
segment
.base_address
&&
u64
::
from
(
size
)
<=
segment
.base_address
+
segment
.bytes
.len
()
as
u64
&&
address
<=
segment
.base_address
+
segment
.bytes
.len
()
as
u64
-
u64
::
from
(
size
)
{
if
segment
.write_flag
{
// The segment is writeable, thus we do not know the content at runtime.
return
Ok
(
None
);
}
let
index
=
(
address
-
segment
.base_address
)
as
usize
;
let
mut
bytes
=
segment
.bytes
[
index
..
index
+
u64
::
from
(
size
)
as
usize
]
.to_vec
();
if
self
.is_little_endian
{
bytes
=
bytes
.into_iter
()
.rev
()
.collect
();
}
let
mut
bytes
=
bytes
.into_iter
();
let
mut
bitvector
=
Bitvector
::
from_u8
(
bytes
.next
()
.unwrap
());
for
byte
in
bytes
{
let
new_byte
=
Bitvector
::
from_u8
(
byte
);
bitvector
=
bitvector
.bin_op
(
BinOpType
::
Piece
,
&
new_byte
)
?
;
}
return
Ok
(
Some
(
bitvector
));
}
}
// No segment fully contains the read.
Err
(
anyhow!
(
"Address is not a valid global memory address."
))
}
/// Read the contents of memory from a given address onwards until a null byte is reached and checks whether the
/// content is a valid UTF8 string.
pub
fn
read_string_until_null_terminator
(
&
self
,
address
:
&
Bitvector
)
->
Result
<&
str
,
Error
>
{
let
address
=
address
.try_to_u64
()
.unwrap
();
for
segment
in
self
.memory_segments
.iter
()
{
if
address
>=
segment
.base_address
&&
address
<=
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
let
start_index
=
(
address
-
segment
.base_address
)
as
usize
;
if
let
Some
(
end_index
)
=
segment
.bytes
[
start_index
..
]
.iter
()
.position
(|
&
b
|
b
==
0
)
{
let
c_str
=
std
::
ffi
::
CStr
::
from_bytes_with_nul
(
&
segment
.bytes
[
start_index
..
start_index
+
end_index
+
1
],
)
?
;
return
Ok
(
c_str
.to_str
()
?
);
}
else
{
return
Err
(
anyhow!
(
"Not a valid string in memory."
));
}
}
}
Err
(
anyhow!
(
"Address is not a valid global memory address."
))
}
/// Checks whether the constant is a global memory address.
pub
fn
is_global_memory_address
(
&
self
,
constant
:
&
Bitvector
)
->
bool
{
if
self
.read
(
constant
,
constant
.bytesize
())
.is_ok
()
{
return
true
;
}
false
}
/// Check whether all addresses in the given interval point to a readable segment in the runtime memory image.
///
/// Returns an error if the address interval intersects more than one memory segment
/// or if it does not point to global memory at all.
pub
fn
is_interval_readable
(
&
self
,
start_address
:
u64
,
end_address
:
u64
,
)
->
Result
<
bool
,
Error
>
{
for
segment
in
self
.memory_segments
.iter
()
{
if
start_address
>=
segment
.base_address
&&
start_address
<
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
if
end_address
<=
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
return
Ok
(
segment
.read_flag
);
}
else
{
return
Err
(
anyhow!
(
"Interval spans more than one segment"
));
}
}
}
Err
(
anyhow!
(
"Address not contained in runtime memory image"
))
}
/// For an address to global read-only memory, return the memory segment it points to
/// and the index inside the segment, where the address points to.
///
/// Returns an error if the target memory segment is marked as writeable
/// or if the pointer does not point to global memory.
pub
fn
get_ro_data_pointer_at_address
(
&
self
,
address
:
&
Bitvector
,
)
->
Result
<
(
&
[
u8
],
usize
),
Error
>
{
let
address
=
address
.try_to_u64
()
.unwrap
();
for
segment
in
self
.memory_segments
.iter
()
{
if
address
>=
segment
.base_address
&&
address
<
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
if
segment
.write_flag
{
return
Err
(
anyhow!
(
"Target segment is writeable"
));
}
else
{
return
Ok
((
&
segment
.bytes
,
(
address
-
segment
.base_address
)
as
usize
));
}
}
}
Err
(
anyhow!
(
"Pointer target not in global memory."
))
}
/// Check whether the given address points to a writeable segment in the runtime memory image.
///
/// Returns an error if the address does not point to global memory.
pub
fn
is_address_writeable
(
&
self
,
address
:
&
Bitvector
)
->
Result
<
bool
,
Error
>
{
let
address
=
address
.try_to_u64
()
.unwrap
();
for
segment
in
self
.memory_segments
.iter
()
{
if
address
>=
segment
.base_address
&&
address
<
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
return
Ok
(
segment
.write_flag
);
}
}
Err
(
anyhow!
(
"Address not contained in runtime memory image"
))
}
/// Check whether all addresses in the given interval point to a writeable segment in the runtime memory image.
///
/// Returns an error if the address interval intersects more than one memory segment
/// or if it does not point to global memory at all.
pub
fn
is_interval_writeable
(
&
self
,
start_address
:
u64
,
end_address
:
u64
,
)
->
Result
<
bool
,
Error
>
{
for
segment
in
self
.memory_segments
.iter
()
{
if
start_address
>=
segment
.base_address
&&
start_address
<
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
if
end_address
<=
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
return
Ok
(
segment
.write_flag
);
}
else
{
return
Err
(
anyhow!
(
"Interval spans more than one segment"
));
}
}
}
Err
(
anyhow!
(
"Address not contained in runtime memory image"
))
}
}
#[cfg(test)]
pub
mod
tests
{
use
super
::
*
;
impl
RuntimeMemoryImage
{
/// Creates a mock runtime memory image with: byte series, strings and format strings.
pub
fn
mock
()
->
RuntimeMemoryImage
{
RuntimeMemoryImage
{
memory_segments
:
vec!
[
MemorySegment
{
bytes
:
[
0xb0u8
,
0xb1
,
0xb2
,
0xb3
,
0xb4
]
.to_vec
(),
base_address
:
0x1000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
},
MemorySegment
{
bytes
:
[
0u8
;
8
]
.to_vec
(),
base_address
:
0x2000
,
read_flag
:
true
,
write_flag
:
true
,
execute_flag
:
false
,
},
// Contains the Hello World string at byte 3002.
MemorySegment
{
bytes
:
[
0x01
,
0x02
,
0x48
,
0x65
,
0x6c
,
0x6c
,
0x6f
,
0x20
,
0x57
,
0x6f
,
0x72
,
0x6c
,
0x64
,
0x00
,
]
.to_vec
(),
base_address
:
0x3000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
},
MemorySegment
{
bytes
:
[
0x02
,
0x30
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
,
0x00
]
.to_vec
(),
base_address
:
0x4000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
},
// Contains strings: '/dev/sd%c%d' and 'cat %s'
MemorySegment
{
bytes
:
[
0x2f
,
0x64
,
0x65
,
0x76
,
0x2f
,
0x73
,
0x64
,
0x25
,
0x63
,
0x25
,
0x64
,
0x00
,
0x63
,
0x61
,
0x74
,
0x20
,
0x25
,
0x73
,
0x00
,
]
.to_vec
(),
base_address
:
0x5000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
},
// Contains string: 'cat %s %s %s %s' starting at the first byte.
MemorySegment
{
bytes
:
[
0x63
,
0x61
,
0x74
,
0x20
,
0x25
,
0x73
,
0x20
,
0x25
,
0x73
,
0x20
,
0x25
,
0x73
,
0x20
,
0x25
,
0x73
,
0x00
,
]
.to_vec
(),
base_address
:
0x6000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
},
// Contains string: 'str1 str2 str3 str4'
MemorySegment
{
bytes
:
[
0x73
,
0x74
,
0x72
,
0x31
,
0x20
,
0x73
,
0x74
,
0x72
,
0x32
,
0x20
,
0x73
,
0x74
,
0x72
,
0x33
,
0x20
,
0x73
,
0x74
,
0x72
,
0x34
,
0x00
,
]
.to_vec
(),
base_address
:
0x7000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
},
],
is_little_endian
:
true
,
}
}
}
#[test]
fn
read_endianness
()
{
let
mut
mem_image
=
RuntimeMemoryImage
::
mock
();
let
address
=
Bitvector
::
from_u32
(
0x1001
);
assert_eq!
(
mem_image
.read
(
&
address
,
ByteSize
::
new
(
4
))
.unwrap
(),
Bitvector
::
from_u32
(
0xb4b3b2b1
)
.into
()
);
mem_image
.is_little_endian
=
false
;
assert_eq!
(
mem_image
.read
(
&
address
,
ByteSize
::
new
(
4
))
.unwrap
(),
Bitvector
::
from_u32
(
0xb1b2b3b4
)
.into
()
);
}
#[test]
fn
ro_data_pointer
()
{
let
mem_image
=
RuntimeMemoryImage
::
mock
();
let
address
=
Bitvector
::
from_u32
(
0x1002
);
let
(
slice
,
index
)
=
mem_image
.get_ro_data_pointer_at_address
(
&
address
)
.unwrap
();
assert_eq!
(
index
,
2
);
assert_eq!
(
&
slice
[
index
..
],
&
[
0xb2u8
,
0xb3
,
0xb4
]);
}
#[test]
fn
test_read_string_until_null_terminator
()
{
let
mem_image
=
RuntimeMemoryImage
::
mock
();
// the byte array contains "Hello World".
let
expected_string
:
&
str
=
std
::
str
::
from_utf8
(
b
"
\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64
"
)
.unwrap
();
let
address
=
Bitvector
::
from_u32
(
0x3002
);
assert_eq!
(
expected_string
,
mem_image
.read_string_until_null_terminator
(
&
address
)
.unwrap
(),
);
}
}
This diff is collapsed.
Click to expand it.
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