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
242c5325
Unverified
Commit
242c5325
authored
2 years ago
by
Enkelmann
Committed by
GitHub
2 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement new check for CWE-415 and CWE-416 (#318)
parent
26f9844d
master
…
v0.7
v0.6
No related merge requests found
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
783 additions
and
37 deletions
+783
-37
main.rs
src/caller/src/main.rs
+1
-1
mod.rs
src/cwe_checker_lib/src/analysis/pointer_inference/mod.rs
+51
-7
checkers.rs
src/cwe_checker_lib/src/checkers.rs
+1
-0
mod.rs
src/cwe_checker_lib/src/checkers/cwe_119/context/mod.rs
+10
-10
tests.rs
src/cwe_checker_lib/src/checkers/cwe_119/context/tests.rs
+1
-8
mod.rs
src/cwe_checker_lib/src/checkers/cwe_119/mod.rs
+4
-9
context.rs
src/cwe_checker_lib/src/checkers/cwe_416/context.rs
+350
-0
mod.rs
src/cwe_checker_lib/src/checkers/cwe_416/mod.rs
+84
-0
state.rs
src/cwe_checker_lib/src/checkers/cwe_416/state.rs
+278
-0
lib.rs
src/cwe_checker_lib/src/lib.rs
+1
-0
lib.rs
test/src/lib.rs
+2
-2
No files found.
src/caller/src/main.rs
View file @
242c5325
...
@@ -178,7 +178,7 @@ fn run_with_ghidra(args: &CmdlineArgs) {
...
@@ -178,7 +178,7 @@ fn run_with_ghidra(args: &CmdlineArgs) {
let
modules_depending_on_string_abstraction
=
BTreeSet
::
from_iter
([
"CWE78"
]);
let
modules_depending_on_string_abstraction
=
BTreeSet
::
from_iter
([
"CWE78"
]);
let
modules_depending_on_pointer_inference
=
let
modules_depending_on_pointer_inference
=
BTreeSet
::
from_iter
([
"CWE119"
,
"CWE134"
,
"CWE476"
,
"Memory"
]);
BTreeSet
::
from_iter
([
"CWE119"
,
"CWE134"
,
"CWE4
16"
,
"CWE4
76"
,
"Memory"
]);
let
string_abstraction_needed
=
modules
let
string_abstraction_needed
=
modules
.iter
()
.iter
()
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/analysis/pointer_inference/mod.rs
View file @
242c5325
...
@@ -29,7 +29,7 @@
...
@@ -29,7 +29,7 @@
use
super
::
fixpoint
::
Computation
;
use
super
::
fixpoint
::
Computation
;
use
super
::
forward_interprocedural_fixpoint
::
GeneralizedContext
;
use
super
::
forward_interprocedural_fixpoint
::
GeneralizedContext
;
use
super
::
interprocedural_fixpoint_generic
::
NodeValue
;
use
super
::
interprocedural_fixpoint_generic
::
NodeValue
;
use
crate
::
abstract_domain
::{
DataDomain
,
IntervalDomain
,
SizedDomain
};
use
crate
::
abstract_domain
::{
AbstractIdentifier
,
DataDomain
,
IntervalDomain
,
SizedDomain
};
use
crate
::
analysis
::
forward_interprocedural_fixpoint
::
Context
as
_
;
use
crate
::
analysis
::
forward_interprocedural_fixpoint
::
Context
as
_
;
use
crate
::
analysis
::
graph
::{
Graph
,
Node
};
use
crate
::
analysis
::
graph
::{
Graph
,
Node
};
use
crate
::
intermediate_representation
::
*
;
use
crate
::
intermediate_representation
::
*
;
...
@@ -37,7 +37,7 @@ use crate::prelude::*;
...
@@ -37,7 +37,7 @@ use crate::prelude::*;
use
crate
::
utils
::
log
::
*
;
use
crate
::
utils
::
log
::
*
;
use
petgraph
::
graph
::
NodeIndex
;
use
petgraph
::
graph
::
NodeIndex
;
use
petgraph
::
visit
::
IntoNodeReferences
;
use
petgraph
::
visit
::
IntoNodeReferences
;
use
std
::
collections
::
HashMap
;
use
std
::
collections
::
{
BTreeMap
,
HashMap
}
;
mod
context
;
mod
context
;
pub
mod
object
;
pub
mod
object
;
...
@@ -96,6 +96,9 @@ pub struct PointerInference<'a> {
...
@@ -96,6 +96,9 @@ pub struct PointerInference<'a> {
/// Maps certain TIDs like the TIDs of [`Jmp`] instructions to the pointer inference state at that TID.
/// Maps certain TIDs like the TIDs of [`Jmp`] instructions to the pointer inference state at that TID.
/// The map will be filled after the fixpoint computation finished.
/// The map will be filled after the fixpoint computation finished.
states_at_tids
:
HashMap
<
Tid
,
State
>
,
states_at_tids
:
HashMap
<
Tid
,
State
>
,
/// Maps the TIDs of call instructions to a map mapping callee IDs to the corresponding value in the caller.
/// The map will be filled after the fixpoint computation finished.
id_renaming_maps_at_calls
:
HashMap
<
Tid
,
BTreeMap
<
AbstractIdentifier
,
Data
>>
,
}
}
impl
<
'a
>
PointerInference
<
'a
>
{
impl
<
'a
>
PointerInference
<
'a
>
{
...
@@ -145,6 +148,7 @@ impl<'a> PointerInference<'a> {
...
@@ -145,6 +148,7 @@ impl<'a> PointerInference<'a> {
values_at_defs
:
HashMap
::
new
(),
values_at_defs
:
HashMap
::
new
(),
addresses_at_defs
:
HashMap
::
new
(),
addresses_at_defs
:
HashMap
::
new
(),
states_at_tids
:
HashMap
::
new
(),
states_at_tids
:
HashMap
::
new
(),
id_renaming_maps_at_calls
:
HashMap
::
new
(),
}
}
}
}
...
@@ -256,12 +260,12 @@ impl<'a> PointerInference<'a> {
...
@@ -256,12 +260,12 @@ impl<'a> PointerInference<'a> {
let
context
=
self
.computation
.get_context
()
.get_context
();
let
context
=
self
.computation
.get_context
()
.get_context
();
let
graph
=
self
.computation
.get_graph
();
let
graph
=
self
.computation
.get_graph
();
for
node
in
graph
.node_indices
()
{
for
node
in
graph
.node_indices
()
{
let
node_state
=
match
self
.computation
.get_node_value
(
node
)
{
Some
(
NodeValue
::
Value
(
value
))
=>
value
,
_
=>
continue
,
};
match
graph
[
node
]
{
match
graph
[
node
]
{
Node
::
BlkStart
(
blk
,
_sub
)
=>
{
Node
::
BlkStart
(
blk
,
_sub
)
=>
{
let
node_state
=
match
self
.computation
.get_node_value
(
node
)
{
Some
(
NodeValue
::
Value
(
value
))
=>
value
,
_
=>
continue
,
};
let
mut
state
=
node_state
.clone
();
let
mut
state
=
node_state
.clone
();
for
def
in
&
blk
.term.defs
{
for
def
in
&
blk
.term.defs
{
match
&
def
.term
{
match
&
def
.term
{
...
@@ -291,12 +295,40 @@ impl<'a> PointerInference<'a> {
...
@@ -291,12 +295,40 @@ impl<'a> PointerInference<'a> {
}
}
}
}
Node
::
BlkEnd
(
blk
,
_sub
)
=>
{
Node
::
BlkEnd
(
blk
,
_sub
)
=>
{
let
node_state
=
match
self
.computation
.get_node_value
(
node
)
{
Some
(
NodeValue
::
Value
(
value
))
=>
value
,
_
=>
continue
,
};
for
jmp
in
&
blk
.term.jmps
{
for
jmp
in
&
blk
.term.jmps
{
self
.states_at_tids
self
.states_at_tids
.insert
(
jmp
.tid
.clone
(),
node_state
.clone
());
.insert
(
jmp
.tid
.clone
(),
node_state
.clone
());
}
}
}
}
Node
::
CallSource
{
..
}
|
Node
::
CallReturn
{
..
}
=>
(),
Node
::
CallSource
{
..
}
=>
(),
Node
::
CallReturn
{
call
:
(
caller_blk
,
_caller_sub
),
return_
:
_
,
}
=>
{
let
call_tid
=
match
caller_blk
.term.jmps
.get
(
0
)
{
Some
(
call
)
=>
&
call
.tid
,
_
=>
continue
,
};
let
(
state_before_call
,
state_before_return
)
=
match
self
.computation
.get_node_value
(
node
)
{
Some
(
NodeValue
::
CallFlowCombinator
{
call_stub
:
Some
(
state_before_call
),
interprocedural_flow
:
Some
(
state_before_return
),
})
=>
(
state_before_call
,
state_before_return
),
_
=>
continue
,
};
let
id_to_data_map
=
context
.create_callee_id_to_caller_data_map
(
state_before_call
,
state_before_return
,
call_tid
,
);
self
.id_renaming_maps_at_calls
.insert
(
call_tid
.clone
(),
id_to_data_map
);
}
}
}
}
}
}
}
...
@@ -307,6 +339,18 @@ impl<'a> PointerInference<'a> {
...
@@ -307,6 +339,18 @@ impl<'a> PointerInference<'a> {
self
.states_at_tids
.get
(
jmp_tid
)
self
.states_at_tids
.get
(
jmp_tid
)
}
}
/// Get the mapping from callee IDs to caller values for the given call.
/// This function only yields results after the fixpoint has been computed.
///
/// Note that the maps may contain mappings from callee IDs to temporary caller IDs that get instantly removed from the caller
/// since they are not referenced in any caller object.
pub
fn
get_id_renaming_map_at_call_tid
(
&
self
,
call_tid
:
&
Tid
,
)
->
Option
<&
BTreeMap
<
AbstractIdentifier
,
Data
>>
{
self
.id_renaming_maps_at_calls
.get
(
call_tid
)
}
/// Print information on dead ends in the control flow graph for debugging purposes.
/// Print information on dead ends in the control flow graph for debugging purposes.
/// Ignore returns where there is no known caller stack id.
/// Ignore returns where there is no known caller stack id.
#[allow(dead_code)]
#[allow(dead_code)]
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers.rs
View file @
242c5325
...
@@ -12,6 +12,7 @@ pub mod cwe_215;
...
@@ -12,6 +12,7 @@ pub mod cwe_215;
pub
mod
cwe_243
;
pub
mod
cwe_243
;
pub
mod
cwe_332
;
pub
mod
cwe_332
;
pub
mod
cwe_367
;
pub
mod
cwe_367
;
pub
mod
cwe_416
;
pub
mod
cwe_426
;
pub
mod
cwe_426
;
pub
mod
cwe_467
;
pub
mod
cwe_467
;
pub
mod
cwe_476
;
pub
mod
cwe_476
;
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_119/context/mod.rs
View file @
242c5325
...
@@ -39,19 +39,19 @@ pub struct Context<'a> {
...
@@ -39,19 +39,19 @@ pub struct Context<'a> {
impl
<
'a
>
Context
<
'a
>
{
impl
<
'a
>
Context
<
'a
>
{
/// Create a new context object.
/// Create a new context object.
pub
fn
new
(
pub
fn
new
<
'b
>
(
project
:
&
'a
Project
,
analysis_results
:
&
'b
AnalysisResults
<
'a
>
,
graph
:
&
'a
Graph
<
'a
>
,
pointer_inference
:
&
'a
PointerInference
<
'a
>
,
function_signatures
:
&
'a
BTreeMap
<
Tid
,
FunctionSignature
>
,
analysis_results
:
&
AnalysisResults
,
log_collector
:
crossbeam_channel
::
Sender
<
LogThreadMsg
>
,
log_collector
:
crossbeam_channel
::
Sender
<
LogThreadMsg
>
,
)
->
Self
{
)
->
Context
<
'a
>
where
'a
:
'b
,
{
let
project
=
analysis_results
.project
;
Context
{
Context
{
project
,
project
,
graph
,
graph
:
analysis_results
.control_flow_graph
,
pointer_inference
,
pointer_inference
:
analysis_results
.pointer_inference
.unwrap
()
,
function_signatures
,
function_signatures
:
analysis_results
.function_signatures
.unwrap
()
,
callee_to_callsites_map
:
compute_callee_to_call_sites_map
(
project
),
callee_to_callsites_map
:
compute_callee_to_call_sites_map
(
project
),
param_replacement_map
:
compute_param_replacement_map
(
analysis_results
),
param_replacement_map
:
compute_param_replacement_map
(
analysis_results
),
malloc_tid_to_object_size_map
:
compute_size_values_of_malloc_calls
(
analysis_results
),
malloc_tid_to_object_size_map
:
compute_size_values_of_malloc_calls
(
analysis_results
),
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_119/context/tests.rs
View file @
242c5325
...
@@ -18,14 +18,7 @@ impl<'a> Context<'a> {
...
@@ -18,14 +18,7 @@ impl<'a> Context<'a> {
let
analysis_results
=
Box
::
leak
(
analysis_results
);
let
analysis_results
=
Box
::
leak
(
analysis_results
);
let
(
log_collector
,
_
)
=
crossbeam_channel
::
unbounded
();
let
(
log_collector
,
_
)
=
crossbeam_channel
::
unbounded
();
Context
::
new
(
Context
::
new
(
analysis_results
,
log_collector
)
analysis_results
.project
,
analysis_results
.control_flow_graph
,
analysis_results
.pointer_inference
.unwrap
(),
analysis_results
.function_signatures
.unwrap
(),
analysis_results
,
log_collector
,
)
}
}
}
}
...
...
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_119/mod.rs
View file @
242c5325
...
@@ -65,14 +65,7 @@ pub fn check_cwe(
...
@@ -65,14 +65,7 @@ pub fn check_cwe(
)
->
(
Vec
<
LogMessage
>
,
Vec
<
CweWarning
>
)
{
)
->
(
Vec
<
LogMessage
>
,
Vec
<
CweWarning
>
)
{
let
log_thread
=
LogThread
::
spawn
(
LogThread
::
collect_and_deduplicate
);
let
log_thread
=
LogThread
::
spawn
(
LogThread
::
collect_and_deduplicate
);
let
context
=
Context
::
new
(
let
context
=
Context
::
new
(
analysis_results
,
log_thread
.get_msg_sender
());
analysis_results
.project
,
analysis_results
.control_flow_graph
,
analysis_results
.pointer_inference
.unwrap
(),
analysis_results
.function_signatures
.unwrap
(),
analysis_results
,
log_thread
.get_msg_sender
(),
);
let
mut
fixpoint_computation
=
let
mut
fixpoint_computation
=
crate
::
analysis
::
forward_interprocedural_fixpoint
::
create_computation
(
context
,
None
);
crate
::
analysis
::
forward_interprocedural_fixpoint
::
create_computation
(
context
,
None
);
...
@@ -91,5 +84,7 @@ pub fn check_cwe(
...
@@ -91,5 +84,7 @@ pub fn check_cwe(
fixpoint_computation
.compute_with_max_steps
(
100
);
fixpoint_computation
.compute_with_max_steps
(
100
);
log_thread
.collect
()
let
(
logs
,
mut
cwe_warnings
)
=
log_thread
.collect
();
cwe_warnings
.sort
();
(
logs
,
cwe_warnings
)
}
}
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_416/context.rs
0 → 100644
View file @
242c5325
use
super
::
State
;
use
super
::
CWE_MODULE
;
use
crate
::
abstract_domain
::
AbstractDomain
;
use
crate
::
analysis
::
function_signature
::
FunctionSignature
;
use
crate
::
analysis
::
graph
::
Graph
;
use
crate
::
analysis
::
pointer_inference
::
PointerInference
;
use
crate
::
analysis
::
vsa_results
::
VsaResult
;
use
crate
::
intermediate_representation
::
*
;
use
crate
::
prelude
::
*
;
use
crate
::
utils
::
log
::
CweWarning
;
use
crate
::
utils
::
log
::
LogMessage
;
use
crate
::
utils
::
log
::
LogThreadMsg
;
use
std
::
collections
::
BTreeMap
;
/// The context struct for the fixpoint algorithm that contains references to the analysis results
/// of other analyses used in this analysis.
pub
struct
Context
<
'a
>
{
/// A pointer to the project struct.
pub
project
:
&
'a
Project
,
/// A pointer to the control flow graph.
pub
graph
:
&
'a
Graph
<
'a
>
,
/// A pointer to the results of the pointer inference analysis.
pub
pointer_inference
:
&
'a
PointerInference
<
'a
>
,
/// A pointer to the computed function signatures for all internal functions.
pub
function_signatures
:
&
'a
BTreeMap
<
Tid
,
FunctionSignature
>
,
/// A sender channel that can be used to collect logs in the corresponding logging thread.
pub
log_collector
:
crossbeam_channel
::
Sender
<
LogThreadMsg
>
,
/// Generic function arguments assumed for calls to functions where the real number of parameters are unknown.
generic_function_parameter
:
Vec
<
Arg
>
,
}
impl
<
'a
>
Context
<
'a
>
{
/// Generate a new context struct from the given analysis results and a channel for gathering log messages and CWE warnings.
pub
fn
new
<
'b
>
(
analysis_results
:
&
'b
AnalysisResults
<
'a
>
,
log_collector
:
crossbeam_channel
::
Sender
<
LogThreadMsg
>
,
)
->
Context
<
'a
>
where
'a
:
'b
,
{
let
generic_function_parameter
:
Vec
<
_
>
=
if
let
Some
(
cconv
)
=
analysis_results
.project
.get_standard_calling_convention
()
{
cconv
.integer_parameter_register
.iter
()
.map
(|
reg
|
Arg
::
from_var
(
reg
.clone
(),
None
))
.collect
()
}
else
{
Vec
::
new
()
};
Context
{
project
:
analysis_results
.project
,
graph
:
analysis_results
.control_flow_graph
,
pointer_inference
:
analysis_results
.pointer_inference
.unwrap
(),
function_signatures
:
analysis_results
.function_signatures
.unwrap
(),
log_collector
,
generic_function_parameter
,
}
}
/// For the given call parameters of the given call check for possible Use-After-Free bugs
/// and return the possible causes for such bugs.
fn
collect_cwe_warnings_of_call_params
<
'b
>
(
&
self
,
state
:
&
mut
State
,
call_tid
:
&
Tid
,
call_params
:
impl
IntoIterator
<
Item
=
&
'b
Arg
>
,
)
->
Option
<
Vec
<
String
>>
{
let
mut
warnings
=
Vec
::
new
();
for
arg
in
call_params
{
if
let
Some
(
arg_value
)
=
self
.pointer_inference
.eval_parameter_arg_at_call
(
call_tid
,
arg
)
{
if
let
Some
(
mut
warning_causes
)
=
state
.check_address_for_use_after_free
(
&
arg_value
)
{
warnings
.append
(
&
mut
warning_causes
);
}
}
}
if
!
warnings
.is_empty
()
{
Some
(
warnings
)
}
else
{
None
}
}
/// Check the parameters of an internal function call for dangling pointers and report CWE warnings accordingly.
fn
check_internal_call_params_for_use_after_free
(
&
self
,
state
:
&
mut
State
,
callee_sub_tid
:
&
Tid
,
call_tid
:
&
Tid
,
)
{
let
function_signature
=
match
self
.function_signatures
.get
(
callee_sub_tid
)
{
Some
(
fn_sig
)
=>
fn_sig
,
None
=>
return
,
};
let
mut
warnings
=
Vec
::
new
();
for
(
arg
,
access_pattern
)
in
&
function_signature
.parameters
{
if
access_pattern
.is_dereferenced
()
{
if
let
Some
(
arg_value
)
=
self
.pointer_inference
.eval_parameter_arg_at_call
(
call_tid
,
arg
)
{
if
let
Some
(
mut
warning_causes
)
=
state
.check_address_for_use_after_free
(
&
arg_value
)
{
warnings
.append
(
&
mut
warning_causes
);
}
}
}
}
let
callee_sub_name
=
&
self
.project.program.term.subs
[
callee_sub_tid
]
.term.name
;
if
!
warnings
.is_empty
()
{
let
cwe_warning
=
CweWarning
{
name
:
"CWE416"
.to_string
(),
version
:
CWE_MODULE
.version
.to_string
(),
addresses
:
vec!
[
call_tid
.address
.clone
()],
tids
:
vec!
[
format!
(
"{}"
,
call_tid
)],
symbols
:
Vec
::
new
(),
other
:
vec!
[
warnings
],
description
:
format!
(
"(Use After Free) Call to {} at {} may access dangling pointers through its parameters"
,
callee_sub_name
,
call_tid
.address
),
};
self
.log_collector
.send
(
cwe_warning
.into
())
.unwrap
();
}
}
/// Handle a call to `free` by marking the corresponding memory object IDs as dangling and detecting possible double frees.
fn
handle_call_to_free
(
&
self
,
state
:
&
mut
State
,
call_tid
:
&
Tid
,
free_symbol
:
&
ExternSymbol
)
{
if
free_symbol
.parameters
.is_empty
()
{
let
error_msg
=
LogMessage
::
new_error
(
"free symbol without parameter encountered."
)
.location
(
call_tid
.clone
())
.source
(
CWE_MODULE
.name
);
self
.log_collector
.send
(
error_msg
.into
())
.unwrap
();
return
;
}
if
let
Some
(
param
)
=
self
.pointer_inference
.eval_parameter_arg_at_call
(
call_tid
,
&
free_symbol
.parameters
[
0
])
{
if
let
Some
(
pi_state
)
=
self
.pointer_inference
.get_state_at_jmp_tid
(
call_tid
)
{
if
let
Some
(
warning_causes
)
=
state
.handle_param_of_free_call
(
call_tid
,
&
param
,
pi_state
)
{
let
cwe_warning
=
CweWarning
{
name
:
"CWE415"
.to_string
(),
version
:
CWE_MODULE
.version
.to_string
(),
addresses
:
vec!
[
call_tid
.address
.clone
()],
tids
:
vec!
[
format!
(
"{}"
,
call_tid
)],
symbols
:
Vec
::
new
(),
other
:
vec!
[
warning_causes
],
description
:
format!
(
"(Double Free) Object may have been freed before at {}"
,
call_tid
.address
),
};
self
.log_collector
.send
(
cwe_warning
.into
())
.unwrap
();
}
}
}
}
}
impl
<
'a
>
crate
::
analysis
::
forward_interprocedural_fixpoint
::
Context
<
'a
>
for
Context
<
'a
>
{
type
Value
=
State
;
/// Get a reference to the control flow graph.
fn
get_graph
(
&
self
)
->
&
Graph
<
'a
>
{
self
.graph
}
/// Merge two node states.
fn
merge
(
&
self
,
state1
:
&
State
,
state2
:
&
State
)
->
State
{
state1
.merge
(
state2
)
}
/// Check whether the `def` may access already freed memory.
/// If yes, generate a CWE warning and mark the corresponding object IDs as already flagged.
fn
update_def
(
&
self
,
state
:
&
State
,
def
:
&
Term
<
Def
>
)
->
Option
<
State
>
{
let
mut
state
=
state
.clone
();
if
let
Some
(
address
)
=
self
.pointer_inference
.eval_address_at_def
(
&
def
.tid
)
{
if
let
Some
(
warning_causes
)
=
state
.check_address_for_use_after_free
(
&
address
)
{
let
cwe_warning
=
CweWarning
{
name
:
"CWE416"
.to_string
(),
version
:
CWE_MODULE
.version
.to_string
(),
addresses
:
vec!
[
def
.tid.address
.clone
()],
tids
:
vec!
[
format!
(
"{}"
,
def
.tid
)],
symbols
:
Vec
::
new
(),
other
:
vec!
[
warning_causes
],
description
:
format!
(
"(Use After Free) Access through a dangling pointer at {}"
,
def
.tid.address
),
};
self
.log_collector
.send
(
cwe_warning
.into
())
.unwrap
();
}
}
Some
(
state
)
}
/// Just returns the unmodified state.
fn
update_jump
(
&
self
,
state
:
&
State
,
_jump
:
&
Term
<
Jmp
>
,
_untaken_conditional
:
Option
<&
Term
<
Jmp
>>
,
_target
:
&
Term
<
Blk
>
,
)
->
Option
<
State
>
{
Some
(
state
.clone
())
}
/// Check whether any call parameters are dangling pointers and generate CWE warnings accordingly.
/// Always returns `None` since the analysis is a bottom-up analysis (i.e. no information flows from caller to callee).
fn
update_call
(
&
self
,
state
:
&
State
,
call
:
&
Term
<
Jmp
>
,
target
:
&
crate
::
analysis
::
graph
::
Node
,
_calling_convention
:
&
Option
<
String
>
,
)
->
Option
<
State
>
{
use
crate
::
analysis
::
graph
::
Node
;
let
sub
=
match
*
target
{
Node
::
BlkStart
(
_
,
sub
)
=>
sub
,
_
=>
return
None
,
};
let
mut
state
=
state
.clone
();
self
.check_internal_call_params_for_use_after_free
(
&
mut
state
,
&
sub
.tid
,
&
call
.tid
);
// No information flows from caller to callee, so we return `None` regardless.
None
}
/// Collect the IDs of objects freed in the callee and mark the corresponding objects in the caller as freed.
/// Also check the call parameters for Use-After-Frees.
fn
update_return
(
&
self
,
state
:
Option
<&
State
>
,
state_before_call
:
Option
<&
State
>
,
call
:
&
Term
<
Jmp
>
,
_return_term
:
&
Term
<
Jmp
>
,
_calling_convention
:
&
Option
<
String
>
,
)
->
Option
<
State
>
{
let
(
state_before_return
,
state_before_call
)
=
match
(
state
,
state_before_call
)
{
(
Some
(
state_before_return
),
Some
(
state_before_call
))
=>
{
(
state_before_return
,
state_before_call
)
}
_
=>
return
None
,
};
let
id_replacement_map
=
match
self
.pointer_inference
.get_id_renaming_map_at_call_tid
(
&
call
.tid
)
{
Some
(
map
)
=>
map
,
None
=>
return
None
,
};
let
pi_state_before_call
=
match
self
.pointer_inference
.get_state_at_jmp_tid
(
&
call
.tid
)
{
Some
(
pi_state
)
=>
pi_state
,
None
=>
return
None
,
};
let
mut
state_after_return
=
state_before_call
.clone
();
// Check for Use-After-Frees through function parameters.
// FIXME: This is actually done twice, since the `update_call` method uses the same check.
// But to remove the check there we would have to know the callee function TID here
// even in the case when the call does not actually return at all.
self
.check_internal_call_params_for_use_after_free
(
&
mut
state_after_return
,
&
state_before_return
.current_fn_tid
,
&
call
.tid
,
);
// Add object IDs of objects that may have been freed in the callee.
state_after_return
.collect_freed_objects_from_called_function
(
state_before_return
,
id_replacement_map
,
&
call
.tid
,
pi_state_before_call
,
);
Some
(
state_after_return
)
}
/// Handle extern symbols by checking for Use-After-Frees in the call parameters.
/// Also handle calls to `free` by marking the corresponding object ID as dangling.
fn
update_call_stub
(
&
self
,
state
:
&
State
,
call
:
&
Term
<
Jmp
>
)
->
Option
<
State
>
{
let
mut
state
=
state
.clone
();
if
let
Some
(
extern_symbol
)
=
match
&
call
.term
{
Jmp
::
Call
{
target
,
..
}
=>
self
.project.program.term.extern_symbols
.get
(
target
),
_
=>
None
,
}
{
match
extern_symbol
.name
.as_str
()
{
"free"
=>
self
.handle_call_to_free
(
&
mut
state
,
&
call
.tid
,
extern_symbol
),
extern_symbol_name
=>
{
if
let
Some
(
warnings
)
=
self
.collect_cwe_warnings_of_call_params
(
&
mut
state
,
&
call
.tid
,
&
extern_symbol
.parameters
,
)
{
let
cwe_warning
=
CweWarning
{
name
:
"CWE416"
.to_string
(),
version
:
CWE_MODULE
.version
.to_string
(),
addresses
:
vec!
[
call
.tid.address
.clone
()],
tids
:
vec!
[
format!
(
"{}"
,
call
.tid
)],
symbols
:
Vec
::
new
(),
other
:
vec!
[
warnings
],
description
:
format!
(
"(Use After Free) Call to {} at {} may access dangling pointers through its parameters"
,
extern_symbol_name
,
call
.tid.address
),
};
self
.log_collector
.send
(
cwe_warning
.into
())
.unwrap
();
}
}
}
}
else
if
let
Some
(
warnings
)
=
self
.collect_cwe_warnings_of_call_params
(
&
mut
state
,
&
call
.tid
,
&
self
.generic_function_parameter
,
)
{
let
cwe_warning
=
CweWarning
{
name
:
"CWE416"
.to_string
(),
version
:
CWE_MODULE
.version
.to_string
(),
addresses
:
vec!
[
call
.tid.address
.clone
()],
tids
:
vec!
[
format!
(
"{}"
,
call
.tid
)],
symbols
:
Vec
::
new
(),
other
:
vec!
[
warnings
],
description
:
format!
(
"(Use After Free) Call at {} may access dangling pointers through its parameters"
,
call
.tid.address
),
};
self
.log_collector
.send
(
cwe_warning
.into
())
.unwrap
();
}
Some
(
state
)
}
/// Just returns the unmodified state
fn
specialize_conditional
(
&
self
,
state
:
&
State
,
_condition
:
&
Expression
,
_block_before_condition
:
&
Term
<
Blk
>
,
_is_true
:
bool
,
)
->
Option
<
State
>
{
Some
(
state
.clone
())
}
}
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_416/mod.rs
0 → 100644
View file @
242c5325
//! This module implements a check for CWE-415: Double Free and CWE-416: Use After Free.
//!
//! If a program tries to reference memory objects or other resources after they have been freed
//! it can lead to crashes, unexpected behaviour or even arbitrary code execution.
//! The same is true if the program tries to free the same resource more than once
//! as this can lead to another unrelated resource being freed instead.
//!
//! See <https://cwe.mitre.org/data/definitions/415.html> and <https://cwe.mitre.org/data/definitions/416.html> for detailed descriptions.
//!
//! ## How the check works
//!
//! Using an interprocedural, bottom-up dataflow analysis
//! based on the results of the [Pointer Inference analysis](`crate::analysis::pointer_inference`)
//! the check keeps track of memory objects that have already been freed.
//! If a pointer to an already freed object is used to access memory or provided as a parameter to another function
//! then a CWE warning is generated.
//! To prevent duplicate CWE warnings with the same root cause
//! the check also keeps track of objects for which a CWE warning was already generated.
//!
//! ## False Positives
//!
//! - Since the analysis is not path-sensitive, infeasible paths may lead to false positives.
//! - Any analysis imprecision of the pointer inference analysis
//! that leads to assuming that a pointer can target more memory objects that it actually can target
//! may lead to false positive CWE warnings in this check.
//!
//! ## False Negatives
//!
//! - Arrays of memory objects are not tracked by this analysis as we currently cannot distinguish different array elements in the analysis.
//! Subsequently, CWEs corresponding to arrays of memory objects are not detected.
//! - Memory objects not tracked by the Pointer Inference analysis or pointer targets missed by the Pointer Inference
//! may lead to missed CWEs in this check.
//! - The analysis currently only tracks pointers to objects that were freed by a call to `free`.
//! If a memory object is freed by another external function then this may lead to false negatives in this check.
use
crate
::
prelude
::
*
;
use
crate
::
utils
::
log
::
CweWarning
;
use
crate
::
utils
::
log
::
LogMessage
;
use
crate
::
utils
::
log
::
LogThread
;
use
crate
::
CweModule
;
/// The module name and version
pub
static
CWE_MODULE
:
CweModule
=
CweModule
{
name
:
"CWE416"
,
version
:
"0.3"
,
run
:
check_cwe
,
};
mod
context
;
use
context
::
Context
;
mod
state
;
use
state
::
State
;
/// Run the check for CWE-416: Use After Free.
///
/// This function prepares the bottom-up fixpoint computation
/// by initializing the state at the start of each function with the empty state (i.e. no dangling objects known)
/// and then executing the fixpoint algorithm.
/// Returns collected log messages and CWE warnings.
pub
fn
check_cwe
(
analysis_results
:
&
AnalysisResults
,
_config
:
&
serde_json
::
Value
,
)
->
(
Vec
<
LogMessage
>
,
Vec
<
CweWarning
>
)
{
let
log_thread
=
LogThread
::
spawn
(
LogThread
::
collect_and_deduplicate
);
let
context
=
Context
::
new
(
analysis_results
,
log_thread
.get_msg_sender
());
let
mut
fixpoint_computation
=
crate
::
analysis
::
forward_interprocedural_fixpoint
::
create_computation
(
context
,
None
);
for
(
sub_tid
,
entry_node_of_sub
)
in
crate
::
analysis
::
graph
::
get_entry_nodes_of_subs
(
analysis_results
.control_flow_graph
)
{
let
fn_start_state
=
State
::
new
(
sub_tid
);
fixpoint_computation
.set_node_value
(
entry_node_of_sub
,
crate
::
analysis
::
interprocedural_fixpoint_generic
::
NodeValue
::
Value
(
fn_start_state
),
);
}
fixpoint_computation
.compute_with_max_steps
(
100
);
let
(
logs
,
mut
cwe_warnings
)
=
log_thread
.collect
();
cwe_warnings
.sort
();
(
logs
,
cwe_warnings
)
}
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/checkers/cwe_416/state.rs
0 → 100644
View file @
242c5325
use
crate
::
analysis
::
pointer_inference
::
State
as
PiState
;
use
crate
::{
abstract_domain
::{
AbstractDomain
,
AbstractIdentifier
,
DomainMap
,
UnionMergeStrategy
},
analysis
::
pointer_inference
::
Data
,
prelude
::
*
,
};
use
std
::
collections
::
BTreeMap
;
/// The state of a memory object for which at least one possible call to a `free`-like function was detected.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Clone)]
enum
ObjectState
{
/// The object is already freed, i.e. pointers to it are dangling.
/// The associated TID denotes the point in time when the object was freed.
Dangling
(
Tid
),
/// The object is already freed and a use-after-free CWE message for it was already generated.
/// This object state is used to prevent duplicate CWE warnings with the same root cause.
AlreadyFlagged
,
}
impl
AbstractDomain
for
ObjectState
{
/// Merge two object states.
/// If both object states are dangling then use the source TID of `self` in the result.
fn
merge
(
&
self
,
other
:
&
Self
)
->
Self
{
match
(
self
,
other
)
{
(
ObjectState
::
AlreadyFlagged
,
_
)
|
(
_
,
ObjectState
::
AlreadyFlagged
)
=>
{
ObjectState
::
AlreadyFlagged
}
(
ObjectState
::
Dangling
(
tid
),
ObjectState
::
Dangling
(
_
))
=>
{
ObjectState
::
Dangling
(
tid
.clone
())
}
}
}
/// The `Top` element for object states is a dangling pointer.
fn
is_top
(
&
self
)
->
bool
{
matches!
(
self
,
ObjectState
::
Dangling
(
_
))
}
}
/// The `State` currently only keeps track of the list of TIDs of memory object that may have been freed already
/// together with the corresponding object states.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Clone)]
pub
struct
State
{
pub
current_fn_tid
:
Tid
,
dangling_objects
:
DomainMap
<
AbstractIdentifier
,
ObjectState
,
UnionMergeStrategy
>
,
}
impl
State
{
/// Create a new, empty state, i.e. a state without any object marked as already freed.
pub
fn
new
(
current_fn_tid
:
Tid
)
->
State
{
State
{
current_fn_tid
,
dangling_objects
:
BTreeMap
::
new
()
.into
(),
}
}
/// Check the given address on whether it may point to already freed memory.
/// For each possible dangling pointer target a string describing the root cause is returnen.
/// The object states of corresponding memory objects are set to [`ObjectState::AlreadyFlagged`]
/// to prevent reporting duplicate CWE messages with the same root cause.
pub
fn
check_address_for_use_after_free
(
&
mut
self
,
address
:
&
Data
)
->
Option
<
Vec
<
String
>>
{
let
mut
free_ids_of_dangling_pointers
=
Vec
::
new
();
for
id
in
address
.get_relative_values
()
.keys
()
{
if
let
Some
(
ObjectState
::
Dangling
(
free_id
))
=
self
.dangling_objects
.get
(
id
)
{
free_ids_of_dangling_pointers
.push
(
format!
(
"Accessed ID {} may have been already freed at {}"
,
id
,
free_id
));
self
.dangling_objects
.insert
(
id
.clone
(),
ObjectState
::
AlreadyFlagged
);
}
}
if
free_ids_of_dangling_pointers
.is_empty
()
{
None
}
else
{
Some
(
free_ids_of_dangling_pointers
)
}
}
/// All TIDs that the given `param` may point to are marked as freed, i.e. pointers to them are dangling.
/// For each ID that was already marked as dangling return a string describing the root cause of a possible double free bug.
pub
fn
handle_param_of_free_call
(
&
mut
self
,
call_tid
:
&
Tid
,
param
:
&
Data
,
pi_state
:
&
PiState
,
)
->
Option
<
Vec
<
String
>>
{
// FIXME: This function could also generate debug log messages whenever nonsensical information is detected.
// E.g. stack frame IDs or non-zero ID offsets can be indicators of other bugs.
let
mut
warnings
=
Vec
::
new
();
for
id
in
param
.get_relative_values
()
.keys
()
{
if
pi_state
.memory
.is_unique_object
(
id
)
.ok
()
==
Some
(
false
)
{
// FIXME: We cannot distinguish different objects represented by the same ID.
// So to avoid producing lots of false positive warnings
// we ignore these cases by not marking these IDs as freed.
continue
;
}
if
let
Some
(
ObjectState
::
Dangling
(
old_free_id
))
=
self
.dangling_objects
.insert
(
id
.clone
(),
ObjectState
::
Dangling
(
call_tid
.clone
()))
{
warnings
.push
(
format!
(
"Object {} may have been freed before at {}."
,
id
,
old_free_id
));
}
}
if
!
warnings
.is_empty
()
{
Some
(
warnings
)
}
else
{
None
}
}
/// Add objects that were freed in the callee of a function call to the list of dangling pointers of `self`.
/// May return a list of warnings if cases of possible double frees are detected,
/// i.e. if an already freed object may also have been freed in the callee.
pub
fn
collect_freed_objects_from_called_function
(
&
mut
self
,
state_before_return
:
&
State
,
id_replacement_map
:
&
BTreeMap
<
AbstractIdentifier
,
Data
>
,
call_tid
:
&
Tid
,
pi_state
:
&
PiState
,
)
{
for
(
callee_id
,
callee_object_state
)
in
state_before_return
.dangling_objects
.iter
()
{
if
let
Some
(
caller_value
)
=
id_replacement_map
.get
(
callee_id
)
{
for
caller_id
in
caller_value
.get_relative_values
()
.keys
()
{
if
pi_state
.memory
.is_unique_object
(
caller_id
)
.ok
()
!=
Some
(
false
)
{
// FIXME: We cannot distinguish different objects represented by the same ID.
// So to avoid producing lots of false positive warnings we ignore these cases.
match
(
callee_object_state
,
self
.dangling_objects
.get
(
caller_id
))
{
// Case 1: The dangling object is unknown to the caller, so we add it.
(
ObjectState
::
Dangling
(
_
),
None
)
|
(
ObjectState
::
AlreadyFlagged
,
None
)
=>
{
self
.dangling_objects
.insert
(
caller_id
.clone
(),
ObjectState
::
Dangling
(
call_tid
.clone
()),
);
}
// Case 2: The dangling object is already known to the caller.
// If this were a case of Use-After-Free, then this should have been flagged when checking the call parameters.
// Thus we can simply leave the object state as it is.
(
_
,
Some
(
ObjectState
::
Dangling
(
_
)))
|
(
_
,
Some
(
&
ObjectState
::
AlreadyFlagged
))
=>
(),
}
}
}
}
}
}
}
impl
AbstractDomain
for
State
{
/// Merge two states.
fn
merge
(
&
self
,
other
:
&
Self
)
->
Self
{
State
{
current_fn_tid
:
self
.current_fn_tid
.clone
(),
dangling_objects
:
self
.dangling_objects
.merge
(
&
other
.dangling_objects
),
}
}
/// Always returns false. The state has no logical `Top` element.
fn
is_top
(
&
self
)
->
bool
{
false
}
}
#[cfg(test)]
pub
mod
tests
{
use
crate
::
intermediate_representation
::
Variable
;
use
super
::
*
;
#[test]
fn
test_check_address_for_use_after_free
()
{
let
mut
state
=
State
::
new
(
Tid
::
new
(
"current_fn"
));
state
.dangling_objects
.insert
(
AbstractIdentifier
::
mock
(
"obj_id"
,
"RAX"
,
8
),
ObjectState
::
Dangling
(
Tid
::
new
(
"free_call"
)),
);
state
.dangling_objects
.insert
(
AbstractIdentifier
::
mock
(
"flagged_obj_id"
,
"RAX"
,
8
),
ObjectState
::
AlreadyFlagged
,
);
let
address
=
Data
::
mock_from_target_map
(
BTreeMap
::
from
([
(
AbstractIdentifier
::
mock
(
"obj_id"
,
"RAX"
,
8
),
Bitvector
::
from_i64
(
0
)
.into
(),
),
(
AbstractIdentifier
::
mock
(
"flagged_obj_id"
,
"RAX"
,
8
),
Bitvector
::
from_i64
(
0
)
.into
(),
),
]));
// Check that one warning is generated for the dangling pointer
// and that afterwards all corresponding IDs are marked as already flagged.
assert_eq!
(
state
.check_address_for_use_after_free
(
&
address
)
.unwrap
()
.len
(),
1
);
assert_eq!
(
*
state
.dangling_objects
.get
(
&
AbstractIdentifier
::
mock
(
"obj_id"
,
"RAX"
,
8
))
.unwrap
(),
ObjectState
::
AlreadyFlagged
);
assert_eq!
(
*
state
.dangling_objects
.get
(
&
AbstractIdentifier
::
mock
(
"flagged_obj_id"
,
"RAX"
,
8
))
.unwrap
(),
ObjectState
::
AlreadyFlagged
);
}
#[test]
fn
test_handle_param_of_free_call
()
{
let
mut
state
=
State
::
new
(
Tid
::
new
(
"current_fn"
));
let
param
=
Data
::
from_target
(
AbstractIdentifier
::
mock
(
"obj_id"
,
"RAX"
,
8
),
Bitvector
::
from_i64
(
0
)
.into
(),
);
let
pi_state
=
PiState
::
new
(
&
Variable
::
mock
(
"RSP"
,
8
),
Tid
::
new
(
"call"
));
// Check that the parameter is correctly marked as freed in the state.
assert
!
(
state
.handle_param_of_free_call
(
&
Tid
::
new
(
"free_call"
),
&
param
,
&
pi_state
)
.is_none
());
assert_eq!
(
*
state
.dangling_objects
.get
(
&
AbstractIdentifier
::
mock
(
"obj_id"
,
"RAX"
,
8
))
.unwrap
(),
ObjectState
::
Dangling
(
Tid
::
new
(
"free_call"
))
);
// Check that a second free operation yields a double free warning.
assert
!
(
state
.handle_param_of_free_call
(
&
Tid
::
new
(
"free_call"
),
&
param
,
&
pi_state
)
.is_some
());
}
#[test]
fn
test_collect_freed_objects_from_called_function
()
{
let
mut
state
=
State
::
new
(
Tid
::
new
(
"current_fn"
));
let
mut
state_before_return
=
State
::
new
(
Tid
::
new
(
"callee_fn_tid"
));
state_before_return
.dangling_objects
.insert
(
AbstractIdentifier
::
mock
(
"callee_obj_tid"
,
"RAX"
,
8
),
ObjectState
::
Dangling
(
Tid
::
new
(
"free_tid"
)),
);
let
pi_state
=
PiState
::
new
(
&
Variable
::
mock
(
"RSP"
,
8
),
Tid
::
new
(
"call"
));
let
id_replacement_map
=
BTreeMap
::
from
([(
AbstractIdentifier
::
mock
(
"callee_obj_tid"
,
"RAX"
,
8
),
Data
::
from_target
(
AbstractIdentifier
::
mock
(
"caller_tid"
,
"RBX"
,
8
),
Bitvector
::
from_i64
(
42
)
.into
(),
),
)]);
// Check that the callee object ID is correctly translated to a caller object ID
state
.collect_freed_objects_from_called_function
(
&
state_before_return
,
&
id_replacement_map
,
&
Tid
::
new
(
"call_tid"
),
&
pi_state
,
);
assert_eq!
(
state
.dangling_objects
.len
(),
1
);
assert_eq!
(
state
.dangling_objects
.get
(
&
AbstractIdentifier
::
mock
(
"caller_tid"
,
"RBX"
,
8
))
.unwrap
(),
&
ObjectState
::
Dangling
(
Tid
::
new
(
"call_tid"
))
);
}
}
This diff is collapsed.
Click to expand it.
src/cwe_checker_lib/src/lib.rs
View file @
242c5325
...
@@ -123,6 +123,7 @@ pub fn get_modules() -> Vec<&'static CweModule> {
...
@@ -123,6 +123,7 @@ pub fn get_modules() -> Vec<&'static CweModule> {
&
crate
::
checkers
::
cwe_243
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_243
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_332
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_332
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_367
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_367
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_416
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_426
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_426
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_467
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_467
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_476
::
CWE_MODULE
,
&
crate
::
checkers
::
cwe_476
::
CWE_MODULE
,
...
...
This diff is collapsed.
Click to expand it.
test/src/lib.rs
View file @
242c5325
...
@@ -423,7 +423,7 @@ mod tests {
...
@@ -423,7 +423,7 @@ mod tests {
#[ignore]
#[ignore]
fn
cwe_415
()
{
fn
cwe_415
()
{
let
mut
error_log
=
Vec
::
new
();
let
mut
error_log
=
Vec
::
new
();
let
mut
tests
=
all_test_cases
(
"cwe_415"
,
"
Memory
"
);
let
mut
tests
=
all_test_cases
(
"cwe_415"
,
"
CWE416
"
);
mark_architecture_skipped
(
&
mut
tests
,
"ppc64"
);
// Ghidra generates mangled function names here for some reason.
mark_architecture_skipped
(
&
mut
tests
,
"ppc64"
);
// Ghidra generates mangled function names here for some reason.
mark_architecture_skipped
(
&
mut
tests
,
"ppc64le"
);
// Ghidra generates mangled function names here for some reason.
mark_architecture_skipped
(
&
mut
tests
,
"ppc64le"
);
// Ghidra generates mangled function names here for some reason.
...
@@ -451,7 +451,7 @@ mod tests {
...
@@ -451,7 +451,7 @@ mod tests {
#[ignore]
#[ignore]
fn
cwe_416
()
{
fn
cwe_416
()
{
let
mut
error_log
=
Vec
::
new
();
let
mut
error_log
=
Vec
::
new
();
let
mut
tests
=
all_test_cases
(
"cwe_416"
,
"
Memory
"
);
let
mut
tests
=
all_test_cases
(
"cwe_416"
,
"
CWE416
"
);
mark_architecture_skipped
(
&
mut
tests
,
"ppc64"
);
// Ghidra generates mangled function names here for some reason.
mark_architecture_skipped
(
&
mut
tests
,
"ppc64"
);
// Ghidra generates mangled function names here for some reason.
mark_architecture_skipped
(
&
mut
tests
,
"ppc64le"
);
// Ghidra generates mangled function names here for some reason.
mark_architecture_skipped
(
&
mut
tests
,
"ppc64le"
);
// Ghidra generates mangled function names here for some reason.
...
...
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