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
cbe2f035
Unverified
Commit
cbe2f035
authored
Jun 09, 2022
by
Enkelmann
Committed by
GitHub
Jun 09, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
More detailed reports for CWE-119 check (#333)
parent
e23a4fcd
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
213 additions
and
88 deletions
+213
-88
bounds_computation.rs
...er_lib/src/checkers/cwe_119/context/bounds_computation.rs
+213
-0
mod.rs
src/cwe_checker_lib/src/checkers/cwe_119/context/mod.rs
+0
-0
param_replacement.rs
...ker_lib/src/checkers/cwe_119/context/param_replacement.rs
+0
-0
tests.rs
src/cwe_checker_lib/src/checkers/cwe_119/context/tests.rs
+0
-88
state.rs
src/cwe_checker_lib/src/checkers/cwe_119/state.rs
+0
-0
No files found.
src/cwe_checker_lib/src/checkers/cwe_119/context/bounds_computation.rs
0 → 100644
View file @
cbe2f035
use
super
::
Context
;
use
crate
::
abstract_domain
::{
AbstractIdentifier
,
DataDomain
,
IntervalDomain
,
TryToBitvec
};
use
crate
::
prelude
::
*
;
/// This struct contains the computed bound for an object.
/// If the object is a parameter object,
/// it also contains metadata about the source object used to determine the bound for the parameter object.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone)]
pub
struct
BoundsMetadata
{
/// The source object (and the offset into the source object that the object points to)
/// if the bound of the memory object is derived from another object (e.g. for parameter objects).
pub
source
:
Option
<
DataDomain
<
IntervalDomain
>>
,
/// The resulting bound for the memory object.
pub
resulting_bound
:
i64
,
}
impl
BoundsMetadata
{
/// Create a new instance without source metadata.
pub
fn
new
(
resulting_bound
:
i64
)
->
BoundsMetadata
{
BoundsMetadata
{
source
:
None
,
resulting_bound
,
}
}
/// Create an instance where the source of the bound is given by `id + offset`.
pub
fn
from_source
(
id
:
&
AbstractIdentifier
,
offset
:
&
IntervalDomain
,
resulting_bound
:
i64
,
)
->
BoundsMetadata
{
BoundsMetadata
{
source
:
Some
(
DataDomain
::
from_target
(
id
.clone
(),
offset
.clone
())),
resulting_bound
,
}
}
}
/// If `bound` is `None`, replace it with `new_bound`.
/// Else only replace it if the bound in `new_bound` is smaller than the existing bound.
fn
replace_if_smaller_bound
(
bound
:
&
mut
Option
<
BoundsMetadata
>
,
new_bound
:
BoundsMetadata
)
{
if
let
Some
(
old_bound
)
=
bound
{
if
old_bound
.resulting_bound
>
new_bound
.resulting_bound
{
*
bound
=
Some
(
new_bound
);
}
}
else
{
*
bound
=
Some
(
new_bound
);
}
}
/// If `bound` is `None`, replace it with `new_bound`.
/// Else only replace it if the bound in `new_bound` is larger than the existing bound.
fn
replace_if_larger_bound
(
bound
:
&
mut
Option
<
BoundsMetadata
>
,
new_bound
:
BoundsMetadata
)
{
if
let
Some
(
old_bound
)
=
bound
{
if
old_bound
.resulting_bound
<
new_bound
.resulting_bound
{
*
bound
=
Some
(
new_bound
);
}
}
else
{
*
bound
=
Some
(
new_bound
);
}
}
impl
<
'a
>
Context
<
'a
>
{
/// Compute the bounds of the memory object associated with the given parameter ID.
///
/// Since the memory object associated to a parameter may not be unique
/// the bounds are only approximated from those objects where exact bounds could be determined.
/// If different objects were found the bounds are approximated by the strictest bounds that were found.
fn
compute_bounds_of_param_id
(
&
self
,
param_object_id
:
&
AbstractIdentifier
,
)
->
(
Option
<
BoundsMetadata
>
,
Option
<
BoundsMetadata
>
)
{
let
object_data
=
self
.recursively_substitute_param_values
(
&
DataDomain
::
from_target
(
param_object_id
.clone
(),
Bitvector
::
zero
(
param_object_id
.bytesize
()
.into
())
.into
(),
));
let
mut
lower_bound
:
Option
<
BoundsMetadata
>
=
None
;
let
mut
upper_bound
:
Option
<
BoundsMetadata
>
=
None
;
for
(
id
,
offset
)
in
object_data
.get_relative_values
()
{
// Right now we ignore cases where we do not know the exact offset into the object.
let
concrete_offset
=
match
offset
.try_to_offset
()
{
Ok
(
offset
)
=>
offset
,
Err
(
_
)
=>
continue
,
};
if
self
.malloc_tid_to_object_size_map
.contains_key
(
id
.get_tid
())
{
replace_if_larger_bound
(
&
mut
lower_bound
,
BoundsMetadata
::
from_source
(
id
,
offset
,
-
concrete_offset
),
);
let
object_size
=
self
.compute_size_of_heap_object
(
id
);
if
let
Ok
(
concrete_object_size
)
=
object_size
.try_to_offset
()
{
replace_if_smaller_bound
(
&
mut
upper_bound
,
BoundsMetadata
::
from_source
(
id
,
offset
,
concrete_object_size
-
concrete_offset
,
),
);
}
}
else
if
self
.is_stack_frame_id
(
id
)
{
let
stack_frame_upper_bound
=
self
.function_signatures
.get
(
id
.get_tid
())
.unwrap
()
.get_stack_params_total_size
();
replace_if_smaller_bound
(
&
mut
upper_bound
,
BoundsMetadata
::
from_source
(
id
,
offset
,
stack_frame_upper_bound
-
concrete_offset
,
),
);
// We do not set a lower bound since we do not know the concrete call site for stack pointers,
// which we would need to determine a correct lower bound.
}
// FIXME: Cases not handled here include unresolved parameter IDs, unknown IDs and global pointers.
// For the first two we do not have any size information.
// For global pointers we need some kind of pre-analysis so that we do not have to assume
// that the pointer may address the complete range of global data addresses.
}
(
lower_bound
,
upper_bound
)
}
/// Compute the bounds of a memory object given by the provided `object_id`.
///
/// Returns `(lower_bound, upper_bound)`, where the bounds may be `None` if they could not be determined.
pub
fn
compute_bounds_of_id
(
&
self
,
object_id
:
&
AbstractIdentifier
,
current_stack_frame_id
:
&
AbstractIdentifier
,
)
->
(
Option
<
BoundsMetadata
>
,
Option
<
BoundsMetadata
>
)
{
if
self
.malloc_tid_to_object_size_map
.contains_key
(
object_id
.get_tid
())
{
let
object_size
=
self
.compute_size_of_heap_object
(
object_id
);
if
let
Ok
(
object_size
)
=
object_size
.try_to_offset
()
{
(
Some
(
BoundsMetadata
::
new
(
0
)),
Some
(
BoundsMetadata
::
new
(
object_size
)),
)
}
else
{
(
Some
(
BoundsMetadata
::
new
(
0
)),
None
)
}
}
else
if
object_id
==
current_stack_frame_id
{
let
stack_frame_upper_bound
=
self
.function_signatures
.get
(
object_id
.get_tid
())
.unwrap
()
.get_stack_params_total_size
();
(
None
,
Some
(
BoundsMetadata
::
new
(
stack_frame_upper_bound
)))
}
else
if
object_id
.get_tid
()
==
current_stack_frame_id
.get_tid
()
&&
object_id
.get_path_hints
()
.is_empty
()
{
// Handle parameter IDs
self
.compute_bounds_of_param_id
(
object_id
)
}
else
{
// The type of object is unknown, thus the size restrictions are also unknown.
(
None
,
None
)
}
}
}
#[cfg(test)]
pub
mod
tests
{
use
super
::
*
;
use
crate
::
analysis
::
pointer_inference
::
Data
;
use
std
::
collections
::{
HashMap
,
HashSet
};
#[test]
fn
test_compute_bounds_of_param_id
()
{
let
mut
context
=
Context
::
mock_x64
();
let
param_id
=
AbstractIdentifier
::
mock
(
"func"
,
"RDI"
,
8
);
let
param_id_2
=
AbstractIdentifier
::
mock
(
"func"
,
"RSI"
,
8
);
let
callsite_id
=
AbstractIdentifier
::
mock
(
"callsite_id"
,
"RDI"
,
8
);
let
callsite_id_2
=
AbstractIdentifier
::
mock
(
"callsite_id"
,
"RSI"
,
8
);
let
malloc_call_id
=
AbstractIdentifier
::
mock
(
"malloc_call"
,
"RAX"
,
8
);
let
main_stack_id
=
AbstractIdentifier
::
mock
(
"main"
,
"RSP"
,
8
);
let
param_value
=
Data
::
from_target
(
malloc_call_id
.clone
(),
Bitvector
::
from_i64
(
2
)
.into
());
let
param_value_2
=
Data
::
from_target
(
main_stack_id
.clone
(),
Bitvector
::
from_i64
(
-
10
)
.into
());
let
param_replacement_map
=
HashMap
::
from
([
(
callsite_id
,
param_value
.clone
()),
(
callsite_id_2
,
param_value_2
.clone
()),
]);
let
callee_to_callsites_map
=
HashMap
::
from
([(
Tid
::
new
(
"func"
),
HashSet
::
from
([
Tid
::
new
(
"callsite_id"
)]))]);
context
.param_replacement_map
=
param_replacement_map
;
context
.callee_to_callsites_map
=
callee_to_callsites_map
;
context
.malloc_tid_to_object_size_map
.insert
(
Tid
::
new
(
"malloc_call"
),
Data
::
from
(
Bitvector
::
from_i64
(
42
)));
context
.call_to_caller_fn_map
=
HashMap
::
from
([
(
Tid
::
new
(
"malloc_call"
),
Tid
::
new
(
"main"
)),
(
Tid
::
new
(
"callsite_id"
),
Tid
::
new
(
"main"
)),
]);
// Test bound computation if the param gets resolved to a heap object
let
(
lower_bound
,
upper_bound
)
=
context
.compute_bounds_of_param_id
(
&
param_id
);
assert_eq!
(
lower_bound
.unwrap
()
.resulting_bound
,
-
2
);
assert_eq!
(
upper_bound
.unwrap
()
.resulting_bound
,
40
);
// Test bound computation if the param gets resolved to a caller stack frame
let
(
lower_bound
,
upper_bound
)
=
context
.compute_bounds_of_param_id
(
&
param_id_2
);
assert_eq!
(
lower_bound
,
None
);
assert_eq!
(
upper_bound
.unwrap
()
.resulting_bound
,
10
);
}
}
src/cwe_checker_lib/src/checkers/cwe_119/context/mod.rs
View file @
cbe2f035
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_119/context/param_replacement.rs
0 → 100644
View file @
cbe2f035
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_119/context/tests.rs
View file @
cbe2f035
...
...
@@ -48,91 +48,3 @@ fn test_compute_size_value_of_malloc_like_call() {
)
.is_none
());
}
#[test]
fn
test_substitute_param_values_context_sensitive
()
{
let
mut
context
=
Context
::
mock_x64
();
let
param_id
=
AbstractIdentifier
::
mock
(
"func"
,
"RDI"
,
8
);
let
callsite_id
=
AbstractIdentifier
::
mock
(
"callsite_id"
,
"RDI"
,
8
);
let
recursive_param_id
=
AbstractIdentifier
::
mock
(
"main"
,
"RSI"
,
8
);
let
recursive_callsite_id
=
AbstractIdentifier
::
mock
(
"recursive_callsite_id"
,
"RSI"
,
8
);
let
param_value
=
Data
::
from_target
(
recursive_param_id
.clone
(),
Bitvector
::
from_i64
(
1
)
.into
());
let
recursive_param_value
=
Data
::
from
(
Bitvector
::
from_i64
(
41
));
let
param_replacement_map
=
HashMap
::
from
([
(
callsite_id
,
param_value
.clone
()),
(
recursive_callsite_id
.clone
(),
recursive_param_value
),
]);
let
callee_to_callsites_map
=
HashMap
::
from
([
(
Tid
::
new
(
"func"
),
HashSet
::
from
([
Tid
::
new
(
"callsite_id"
)])),
(
Tid
::
new
(
"main"
),
HashSet
::
from
([
Tid
::
new
(
"recursive_callsite_id"
)]),
),
]);
let
call_to_caller_map
=
HashMap
::
from
([
(
Tid
::
new
(
"callsite_id"
),
Tid
::
new
(
"main"
)),
(
Tid
::
new
(
"recursive_callsite_id"
),
Tid
::
new
(
"somer_other_fn_id"
),
),
]);
context
.param_replacement_map
=
param_replacement_map
;
context
.callee_to_callsites_map
=
callee_to_callsites_map
;
context
.call_to_caller_fn_map
=
call_to_caller_map
;
// non-recursive substitution
let
result
=
context
.substitute_param_values_context_sensitive
(
&
Data
::
from_target
(
param_id
.clone
(),
Bitvector
::
from_i64
(
5
)
.into
()),
&
Tid
::
new
(
"callsite_id"
),
&
Tid
::
new
(
"func"
),
);
assert_eq!
(
result
,
Data
::
from_target
(
recursive_param_id
.clone
(),
Bitvector
::
from_i64
(
6
)
.into
())
);
// recursive substitution
let
result
=
context
.recursively_substitute_param_values_context_sensitive
(
&
Data
::
from_target
(
param_id
,
Bitvector
::
from_i64
(
5
)
.into
()),
&
Tid
::
new
(
"func"
),
&
[
Tid
::
new
(
"callsite_id"
),
Tid
::
new
(
"recursive_callsite_id"
)],
);
println!
(
"{:#}"
,
result
.to_json_compact
());
assert_eq!
(
result
,
Bitvector
::
from_i64
(
47
)
.into
());
}
#[test]
fn
test_substitute_param_values
()
{
let
mut
context
=
Context
::
mock_x64
();
let
param_id
=
AbstractIdentifier
::
mock
(
"func"
,
"RDI"
,
8
);
let
callsite_id
=
AbstractIdentifier
::
mock
(
"callsite_id"
,
"RDI"
,
8
);
let
recursive_param_id
=
AbstractIdentifier
::
mock
(
"main"
,
"RSI"
,
8
);
let
recursive_callsite_id
=
AbstractIdentifier
::
mock
(
"recursive_callsite_id"
,
"RSI"
,
8
);
let
param_value
=
Data
::
from_target
(
recursive_param_id
.clone
(),
Bitvector
::
from_i64
(
1
)
.into
());
let
recursive_param_value
=
Data
::
from
(
Bitvector
::
from_i64
(
39
));
let
param_replacement_map
=
HashMap
::
from
([
(
callsite_id
,
param_value
.clone
()),
(
recursive_callsite_id
.clone
(),
recursive_param_value
),
]);
let
callee_to_callsites_map
=
HashMap
::
from
([
(
Tid
::
new
(
"func"
),
HashSet
::
from
([
Tid
::
new
(
"callsite_id"
)])),
(
Tid
::
new
(
"main"
),
HashSet
::
from
([
Tid
::
new
(
"recursive_callsite_id"
)]),
),
]);
context
.param_replacement_map
=
param_replacement_map
;
context
.callee_to_callsites_map
=
callee_to_callsites_map
;
// non-recursive substitution
let
(
result_absolute
,
result
)
=
context
.substitute_param_values
(
&
param_id
);
assert
!
(
result_absolute
.is_none
());
assert_eq!
(
result
,
param_value
);
// recursive substitution
let
result
=
context
.recursively_substitute_param_values
(
&
Data
::
from_target
(
param_id
,
Bitvector
::
from_i64
(
5
)
.into
(),
));
assert_eq!
(
result
,
Bitvector
::
from_i64
(
45
)
.into
());
}
src/cwe_checker_lib/src/checkers/cwe_119/state.rs
View file @
cbe2f035
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