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
fac0ec9d
Unverified
Commit
fac0ec9d
authored
Sep 09, 2021
by
Enkelmann
Committed by
GitHub
Sep 09, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Correctly classify some buffer overflows as NULL dereferences (#226)
parent
7194b7f0
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
110 additions
and
4 deletions
+110
-4
data.rs
src/cwe_checker_lib/src/abstract_domain/data.rs
+2
-2
mod.rs
...checker_lib/src/analysis/pointer_inference/context/mod.rs
+17
-0
trait_impls.rs
...lib/src/analysis/pointer_inference/context/trait_impls.rs
+10
-1
mod.rs
src/cwe_checker_lib/src/analysis/pointer_inference/mod.rs
+1
-0
access_handling.rs
...b/src/analysis/pointer_inference/state/access_handling.rs
+47
-0
tests.rs
...checker_lib/src/analysis/pointer_inference/state/tests.rs
+32
-0
lib.rs
test/src/lib.rs
+1
-1
No files found.
src/cwe_checker_lib/src/abstract_domain/data.rs
View file @
fac0ec9d
...
...
@@ -206,8 +206,8 @@ impl<T: RegisterDomain + Display> DataDomain<T> {
}
if
self
.contains_top_values
{
values
.push
(
serde_json
::
Value
::
String
(
format!
(
"Top:{}"
,
self
.bytesize
()
"Top:
i
{}"
,
self
.bytesize
()
.as_bit_length
()
)));
}
match
values
.len
()
{
...
...
src/cwe_checker_lib/src/analysis/pointer_inference/context/mod.rs
View file @
fac0ec9d
...
...
@@ -516,6 +516,23 @@ impl<'a> Context<'a> {
}
ValueDomain
::
new_top
(
self
.project.stack_pointer_register.size
)
}
/// Report a NULL dereference CWE at the address of the given TID.
fn
report_null_deref
(
&
self
,
tid
:
&
Tid
)
{
let
warning
=
CweWarning
{
name
:
"CWE476"
.to_string
(),
version
:
VERSION
.to_string
(),
addresses
:
vec!
[
tid
.address
.clone
()],
tids
:
vec!
[
format!
(
"{}"
,
tid
)],
symbols
:
Vec
::
new
(),
other
:
Vec
::
new
(),
description
:
format!
(
"(NULL Pointer Dereference) Memory access at {} may result in a NULL dereference"
,
tid
.address
),
};
let
_
=
self
.log_collector
.send
(
LogThreadMsg
::
Cwe
(
warning
));
}
}
#[cfg(test)]
...
...
src/cwe_checker_lib/src/analysis/pointer_inference/context/trait_impls.rs
View file @
fac0ec9d
...
...
@@ -32,8 +32,17 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
};
let
_
=
self
.log_collector
.send
(
LogThreadMsg
::
Cwe
(
warning
));
}
// check for null dereferences
match
new_state
.check_def_for_null_dereferences
(
def
)
{
Err
(
_
)
=>
{
self
.report_null_deref
(
&
def
.tid
);
return
None
;
}
Ok
(
true
)
=>
self
.report_null_deref
(
&
def
.tid
),
Ok
(
false
)
=>
(),
// no null dereference detected
}
// check for out-of-bounds memory access
if
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
.runtime_memory_image
)
{
let
(
warning_name
,
warning_description
)
=
match
def
.term
{
Def
::
Load
{
..
}
=>
(
"CWE125"
,
...
...
src/cwe_checker_lib/src/analysis/pointer_inference/mod.rs
View file @
fac0ec9d
...
...
@@ -15,6 +15,7 @@
//! - [CWE-125](https://cwe.mitre.org/data/definitions/125.html) Buffer Overflow: Out-of-bounds Read
//! - [CWE-415](https://cwe.mitre.org/data/definitions/415.html): Double Free
//! - [CWE-416](https://cwe.mitre.org/data/definitions/416.html): Use After Free
//! - [CWE-476](https://cwe.mitre.org/data/definitions/476.html): NULL Pointer Dereference
//! - [CWE-787](https://cwe.mitre.org/data/definitions/787.html): Buffer Overflow: Out-of-bounds Write
//!
//! The analysis operates on a best-effort basis.
...
...
src/cwe_checker_lib/src/analysis/pointer_inference/state/access_handling.rs
View file @
fac0ec9d
...
...
@@ -337,4 +337,51 @@ impl State {
}
None
}
/// Check whether the given `def` could result in a memory access through a NULL pointer.
///
/// If no NULL pointer dereference is detected then `Ok(false)` is returned.
/// If a NULL pointer dereference is detected,
/// try to specialize the state so that `address_expr` cannot result in a NULL pointer anymore.
/// If that succeeds, `Ok(true)` is returned.
/// If that would result in an unsatisfiable state, an error is returned.
pub
fn
check_def_for_null_dereferences
(
&
mut
self
,
def
:
&
Term
<
Def
>
)
->
Result
<
bool
,
Error
>
{
let
address_expr
=
match
&
def
.term
{
Def
::
Load
{
address
,
..
}
|
Def
::
Store
{
address
,
..
}
=>
address
,
Def
::
Assign
{
..
}
=>
return
Ok
(
false
),
};
let
mut
address_val
=
self
.eval
(
address_expr
);
if
let
Some
((
start_index
,
end_index
))
=
address_val
.get_absolute_value
()
.map
(|
val
|
val
.try_to_offset_interval
()
.ok
())
.flatten
()
{
if
(
start_index
>
-
1024
&&
start_index
<
1024
)
||
(
end_index
>
-
1024
&&
end_index
<
1024
)
{
// Interval starts or ends with a null pointer
let
absolute_val
=
address_val
.get_absolute_value
()
.unwrap
()
.clone
();
let
new_absolute_val
=
if
start_index
>
-
1024
&&
start_index
<
1024
{
absolute_val
.add_signed_greater_equal_bound
(
&
Bitvector
::
from_i16
(
1024
)
.into_resize_signed
(
address_val
.bytesize
()),
)
.ok
()
}
else
{
absolute_val
.add_signed_less_equal_bound
(
&
Bitvector
::
from_i16
(
-
1024
)
.into_resize_signed
(
address_val
.bytesize
()),
)
.ok
()
};
address_val
.set_absolute_value
(
new_absolute_val
);
if
address_val
.is_empty
()
{
return
Err
(
anyhow!
(
"Unsatisfiable state"
));
}
self
.specialize_by_expression_result
(
address_expr
,
address_val
)
?
;
return
Ok
(
true
);
}
}
Ok
(
false
)
}
}
src/cwe_checker_lib/src/analysis/pointer_inference/state/tests.rs
View file @
fac0ec9d
...
...
@@ -1085,3 +1085,35 @@ fn specialize_pointer_comparison() {
.is_ok
());
assert_eq!
(
state
.get_register
(
&
register
(
"RAX"
)),
specialized_pointer
);
}
#[test]
fn
test_check_def_for_null_dereferences
()
{
let
mut
state
=
State
::
new
(
&
register
(
"RSP"
),
Tid
::
new
(
"func_tid"
));
let
var_rax
=
Variable
::
mock
(
"RAX"
,
8
);
let
def
=
Def
::
load
(
"load_def"
,
Variable
::
mock
(
"RBX"
,
8
),
Expression
::
Var
(
var_rax
.clone
()),
);
state
.set_register
(
&
var_rax
,
Bitvector
::
from_i64
(
0
)
.into
());
assert
!
(
state
.check_def_for_null_dereferences
(
&
def
)
.is_err
());
state
.set_register
(
&
var_rax
,
Bitvector
::
from_i64
(
12345
)
.into
());
assert_eq!
(
state
.check_def_for_null_dereferences
(
&
def
)
.ok
(),
Some
(
false
)
);
state
.set_register
(
&
var_rax
,
IntervalDomain
::
mock
(
-
2000
,
5
)
.into
());
assert_eq!
(
state
.check_def_for_null_dereferences
(
&
def
)
.ok
(),
Some
(
true
));
assert_eq!
(
state
.get_register
(
&
var_rax
),
IntervalDomain
::
mock
(
-
2000
,
-
1024
)
.into
()
);
let
mut
address
=
state
.get_register
(
&
register
(
"RSP"
));
address
.set_contains_top_flag
();
address
.set_absolute_value
(
Some
(
IntervalDomain
::
mock
(
0
,
0xffff
)));
state
.set_register
(
&
var_rax
,
address
);
assert_eq!
(
state
.check_def_for_null_dereferences
(
&
def
)
.ok
(),
Some
(
true
));
}
test/src/lib.rs
View file @
fac0ec9d
...
...
@@ -184,7 +184,7 @@ mod tests {
let
num_cwes
=
String
::
from_utf8
(
output
.stdout
)
.unwrap
()
.lines
()
.filter
(|
line
|
line
.starts_with
(
"[CWE
125
]"
))
.filter
(|
line
|
line
.starts_with
(
"[CWE
476
]"
))
.count
();
// We check the number of found CWEs only approximately
// so that this check does not fail on minor result changes.
...
...
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