1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
use super::super::State;
use super::*;
use crate::analysis::graph::Graph;
use crate::checkers::cwe_119::stubs::ExternCallHandler;
impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Context<'a> {
type Value = State;
/// Get the control flow graph.
fn get_graph(&self) -> &Graph<'a> {
self.graph
}
/// Merge two states.
fn merge(&self, state1: &State, state2: &State) -> State {
state1.merge(state2)
}
/// If the given [`Def`] is a load or store instruction, check whether it may access addresses
/// that are out of bounds of the corresponding memory object.
/// Generate CWE warnings accordingly.
fn update_def(&self, state: &State, def: &Term<Def>) -> Option<State> {
let mut state = state.clone();
match &def.term {
Def::Load { address: _, var } => {
let address = match self.pointer_inference.eval_address_at_def(&def.tid) {
Some(address) => address,
None => return None, // There seems to be no pointer inference state here.
};
let warnings = state.check_address_access(&address, var.size, self);
if !warnings.is_empty() {
let mut cwe_warning = CweWarning::new(
"CWE125",
super::super::CWE_MODULE.version,
format!(
"(Out-of-bounds Read) Memory read at {} may be out of bounds",
&def.tid.address
),
);
cwe_warning.tids = vec![format!("{}", def.tid)];
cwe_warning.addresses = vec![def.tid.address.to_string()];
cwe_warning.other = vec![warnings];
self.log_collector.send(cwe_warning.into()).unwrap();
}
}
Def::Store { address: _, value } => {
let address = match self.pointer_inference.eval_address_at_def(&def.tid) {
Some(address) => address,
None => return None, // There seems to be no pointer inference state here.
};
let warnings = state.check_address_access(&address, value.bytesize(), self);
if !warnings.is_empty() {
let mut cwe_warning = CweWarning::new(
"CWE787",
super::super::CWE_MODULE.version,
format!(
"(Out-of-bounds Write) Memory write at {} may be out of bounds.",
&def.tid.address
),
);
cwe_warning.tids = vec![format!("{}", def.tid)];
cwe_warning.addresses = vec![def.tid.address.to_string()];
cwe_warning.other = vec![warnings];
self.log_collector.send(cwe_warning.into()).unwrap();
}
}
Def::Assign { .. } => (),
}
Some(state)
}
/// The state does not change for intraprocedural jumps.
fn update_jump(
&self,
state: &State,
_jump: &Term<Jmp>,
_untaken_conditional: Option<&Term<Jmp>>,
_target: &Term<Blk>,
) -> Option<State> {
Some(state.clone())
}
/// Always returns `None`, since the fixpoint computation is intraprocedural
/// and the access to parameter values is checked in the callee separately.
fn update_call(
&self,
_state: &State,
_call: &Term<Jmp>,
_target: &crate::analysis::graph::Node,
_calling_convention: &Option<String>,
) -> Option<State> {
// The analysis is intraprocedural and parameters are checked not here but in the callee.
None
}
/// Just return the `state_before_call` since the fixpoint comutation is intraprocedural.
fn update_return(
&self,
_state_before_return: Option<&State>,
state_before_call: Option<&State>,
_call_term: &Term<Jmp>,
_return_term: &Term<Jmp>,
_calling_convention: &Option<String>,
) -> Option<State> {
// The analysis is intraprocedural
state_before_call.cloned()
}
/// For calls to extern symbols check whether any parameter may point out of bounds of the corresponding memory object.
/// Note that we do not know whether the called function accesses memory areas of certain sizes.
/// Thus we only check that parameter pointers themselves point into the memory object
/// but not whether certain address ranges around a pointer are still inside the corresponding memory object.
fn update_call_stub(&self, state: &State, call: &Term<Jmp>) -> Option<State> {
let mut state = state.clone();
match &call.term {
Jmp::Call { target, .. } => {
if let Some(extern_symbol) = self.project.program.term.extern_symbols.get(target) {
let mut extern_call_handler =
ExternCallHandler::new(self, &mut state, extern_symbol, call);
extern_call_handler.handle_call();
} else {
self.log_debug(
&call.tid,
"Call stub edge without associated extern symbol encountered.",
);
}
}
Jmp::CallInd { .. } => {
if let Some(cconv) = self.project.get_standard_calling_convention() {
for param in &cconv.integer_parameter_register {
let param_arg = Arg::from_var(param.clone(), None);
self.check_param_at_call(&mut state, ¶m_arg, &call.tid, None);
}
}
}
_ => (),
}
Some(state)
}
/// Just return the given state without modification.
fn specialize_conditional(
&self,
state: &State,
_condition: &Expression,
_block_before_condition: &Term<Blk>,
_is_true: bool,
) -> Option<State> {
Some(state.clone())
}
}