Unverified Commit b14c336f by Enkelmann Committed by GitHub

Handle global memory in pointer inference analysis (#133)

parent 0cf8b8d7
......@@ -166,6 +166,7 @@ fn run_with_ghidra(args: CmdlineArgs) {
if args.debug {
cwe_checker_rs::analysis::pointer_inference::run(
&project,
&runtime_memory_image,
serde_json::from_value(config["Memory"].clone()).unwrap(),
true,
);
......
use super::object::ObjectType;
use crate::abstract_domain::*;
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, HashSet};
use super::state::State;
......@@ -21,6 +21,9 @@ pub struct Context<'a> {
pub graph: 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: BTreeMap<Tid, &'a ExternSymbol>,
/// A channel where found CWE warnings and log messages should be sent to.
......@@ -39,10 +42,11 @@ impl<'a> Context<'a> {
/// Create a new context object for a given project.
/// Also needs two channels as input to know where CWE warnings and log messages should be sent to.
pub fn new(
project: &Project,
project: &'a Project,
runtime_memory_image: &'a RuntimeMemoryImage,
config: Config,
log_collector: crossbeam_channel::Sender<LogThreadMsg>,
) -> Context {
) -> Context<'a> {
let mut extern_symbol_map = BTreeMap::new();
for symbol in project.program.term.extern_symbols.iter() {
extern_symbol_map.insert(symbol.tid.clone(), symbol);
......@@ -59,6 +63,7 @@ impl<'a> Context<'a> {
Context {
graph,
project,
runtime_memory_image,
extern_symbol_map,
log_collector,
allocation_symbols: config.allocation_symbols,
......@@ -169,8 +174,11 @@ impl<'a> Context<'a> {
) -> Option<State> {
match extern_symbol.get_unique_parameter() {
Ok(parameter) => {
let parameter_value =
state.eval_parameter_arg(parameter, &self.project.stack_pointer_register);
let parameter_value = state.eval_parameter_arg(
parameter,
&self.project.stack_pointer_register,
&self.runtime_memory_image,
);
match parameter_value {
Ok(memory_object_pointer) => {
if let Data::Pointer(pointer) = memory_object_pointer {
......@@ -225,7 +233,11 @@ impl<'a> Context<'a> {
extern_symbol: &ExternSymbol,
) {
for parameter in extern_symbol.parameters.iter() {
match state.eval_parameter_arg(parameter, &self.project.stack_pointer_register) {
match state.eval_parameter_arg(
parameter,
&self.project.stack_pointer_register,
&self.runtime_memory_image,
) {
Ok(value) => {
if state.memory.is_dangling_pointer(&value, true) {
let warning = CweWarning {
......@@ -304,7 +316,11 @@ impl<'a> Context<'a> {
extern_symbol: &ExternSymbol,
) -> Option<State> {
self.log_debug(
new_state.clear_stack_parameter(extern_symbol, &self.project.stack_pointer_register),
new_state.clear_stack_parameter(
extern_symbol,
&self.project.stack_pointer_register,
self.runtime_memory_image,
),
Some(&call.tid),
);
let calling_conv = extern_symbol.get_calling_convention(&self.project);
......@@ -320,9 +336,11 @@ impl<'a> Context<'a> {
}
} else {
for parameter in extern_symbol.parameters.iter() {
if let Ok(data) =
state.eval_parameter_arg(parameter, &self.project.stack_pointer_register)
{
if let Ok(data) = state.eval_parameter_arg(
parameter,
&self.project.stack_pointer_register,
&self.runtime_memory_image,
) {
possible_referenced_ids.append(&mut data.referenced_ids());
}
}
......
......@@ -109,8 +109,9 @@ fn context_problem_implementation() {
use Expression::*;
let (project, config) = mock_project();
let runtime_memory_image = RuntimeMemoryImage::mock();
let (log_sender, _log_receiver) = crossbeam_channel::unbounded();
let context = Context::new(&project, config, log_sender);
let context = Context::new(&project, &runtime_memory_image, config, log_sender);
let mut state = State::new(&register("RSP"), Tid::new("main"));
let def = Term {
......@@ -271,8 +272,9 @@ fn update_return() {
use crate::analysis::pointer_inference::object::ObjectType;
use crate::analysis::pointer_inference::Data;
let (project, config) = mock_project();
let runtime_memory_image = RuntimeMemoryImage::mock();
let (log_sender, _log_receiver) = crossbeam_channel::unbounded();
let context = Context::new(&project, config, log_sender);
let context = Context::new(&project, &runtime_memory_image, config, log_sender);
let state_before_return = State::new(&register("RSP"), Tid::new("callee"));
let mut state_before_return = context
.update_def(
......
......@@ -35,7 +35,10 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
match &def.term {
Def::Store { address, value } => {
let mut new_state = state.clone();
self.log_debug(new_state.handle_store(address, value), Some(&def.tid));
self.log_debug(
new_state.handle_store(address, value, self.runtime_memory_image),
Some(&def.tid),
);
Some(new_state)
}
Def::Assign { var, value } => {
......@@ -45,7 +48,10 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
}
Def::Load { var, address } => {
let mut new_state = state.clone();
self.log_debug(new_state.handle_load(var, address), Some(&def.tid));
self.log_debug(
new_state.handle_load(var, address, &self.runtime_memory_image),
Some(&def.tid),
);
Some(new_state)
}
}
......
......@@ -16,11 +16,14 @@
use super::fixpoint::Computation;
use super::forward_interprocedural_fixpoint::GeneralizedContext;
use super::interprocedural_fixpoint_generic::NodeValue;
use crate::abstract_domain::{BitvectorDomain, DataDomain};
use crate::analysis::graph::{Graph, Node};
use crate::intermediate_representation::*;
use crate::prelude::*;
use crate::utils::log::*;
use crate::{
abstract_domain::{BitvectorDomain, DataDomain},
utils::binary::RuntimeMemoryImage,
};
use petgraph::graph::NodeIndex;
use petgraph::visit::IntoNodeReferences;
use petgraph::Direction;
......@@ -70,10 +73,11 @@ impl<'a> PointerInference<'a> {
/// Generate a new pointer inference compuation for a project.
pub fn new(
project: &'a Project,
runtime_memory_image: &'a RuntimeMemoryImage,
config: Config,
log_sender: crossbeam_channel::Sender<LogThreadMsg>,
) -> PointerInference<'a> {
let context = Context::new(project, config, log_sender.clone());
let context = Context::new(project, runtime_memory_image, config, log_sender.clone());
let mut entry_sub_to_entry_blocks_map = HashMap::new();
let subs: HashMap<Tid, &Term<Sub>> = project
......@@ -398,10 +402,20 @@ pub fn extract_pi_analysis_results(
///
/// If `print_debug` is set to `true` print debug information to *stdout*.
/// Note that the format of the debug information is currently unstable and subject to change.
pub fn run(project: &Project, config: Config, print_debug: bool) -> PointerInference {
pub fn run<'a>(
project: &'a Project,
runtime_memory_image: &'a RuntimeMemoryImage,
config: Config,
print_debug: bool,
) -> PointerInference<'a> {
let logging_thread = LogThread::spawn(collect_all_logs);
let mut computation = PointerInference::new(project, config, logging_thread.get_msg_sender());
let mut computation = PointerInference::new(
project,
runtime_memory_image,
config,
logging_thread.get_msg_sender(),
);
computation.compute_with_speculative_entry_points(project);
......@@ -457,13 +471,16 @@ mod tests {
use super::*;
impl<'a> PointerInference<'a> {
pub fn mock(project: &'a Project) -> PointerInference<'a> {
pub fn mock(
project: &'a Project,
mem_image: &'a RuntimeMemoryImage,
) -> PointerInference<'a> {
let config = Config {
allocation_symbols: vec!["malloc".to_string()],
deallocation_symbols: vec!["free".to_string()],
};
let (log_sender, _) = crossbeam_channel::unbounded();
PointerInference::new(project, config, log_sender)
PointerInference::new(project, mem_image, config, log_sender)
}
}
}
......@@ -70,7 +70,7 @@ impl AbstractObjectList {
/// Returns an error if the address is a `Data::Value`, i.e. not a pointer.
pub fn get_value(&self, address: &Data, size: ByteSize) -> Result<Data, Error> {
match address {
Data::Value(value) => Err(anyhow!("Load from non-pointer value:\n{:?}", value)),
Data::Value(_) => Err(anyhow!("Load from non-pointer value")),
Data::Top(_) => Ok(Data::new_top(size)),
Data::Pointer(pointer) => {
let mut merged_value: Option<Data> = None;
......
use crate::utils::binary::RuntimeMemoryImage;
use super::*;
impl State {
......@@ -55,7 +57,12 @@ impl State {
}
/// Store `value` at the given `address`.
pub fn store_value(&mut self, address: &Data, value: &Data) -> Result<(), Error> {
pub fn store_value(
&mut self,
address: &Data,
value: &Data,
global_memory: &RuntimeMemoryImage,
) -> Result<(), Error> {
// If the address is a unique caller stack address, write to *all* caller stacks.
if let Some(offset) = self.unwrap_offset_if_caller_stack_address(address) {
let caller_addresses: Vec<_> = self
......@@ -67,53 +74,95 @@ impl State {
.collect();
let mut result = Ok(());
for address in caller_addresses {
if let Err(err) = self.store_value(&address, &value.clone()) {
if let Err(err) = self.store_value(&address, &value.clone(), global_memory) {
result = Err(err);
}
}
// Note that this only returns the last error that was detected.
result
} else if let Data::Pointer(pointer) = self.adjust_pointer_for_read(address) {
self.memory.set_value(pointer, value.clone())?;
Ok(())
} else {
// TODO: Implement recognition of stores to global memory.
Err(anyhow!("Memory write to non-pointer data"))
match self.adjust_pointer_for_read(address) {
Data::Pointer(pointer) => {
self.memory.set_value(pointer, value.clone())?;
Ok(())
}
Data::Value(BitvectorDomain::Value(address_to_global_data)) => {
match global_memory.is_address_writeable(&address_to_global_data) {
Ok(true) => Ok(()),
Ok(false) => Err(anyhow!("Write to read-only global data")),
Err(err) => Err(err),
}
}
Data::Value(BitvectorDomain::Top(_)) | Data::Top(_) => Ok(()),
}
}
}
/// Write a value to the address one gets when evaluating the address expression.
pub fn write_to_address(&mut self, address: &Expression, value: &Data) -> Result<(), Error> {
pub fn write_to_address(
&mut self,
address: &Expression,
value: &Data,
global_memory: &RuntimeMemoryImage,
) -> Result<(), Error> {
match self.eval(address) {
Ok(address_data) => self.store_value(&address_data, value),
Ok(address_data) => self.store_value(&address_data, value, global_memory),
Err(err) => Err(err),
}
}
/// Evaluate the given store instruction on the given state and return the resulting state.
/// Evaluate the store instruction, given by its address and value expressions,
/// and modify the state accordingly.
///
/// The function panics if given anything else than a store expression.
pub fn handle_store(&mut self, address: &Expression, value: &Expression) -> Result<(), Error> {
/// If an error occurs, the state is still modified before the error is returned.
/// E.g. if the value expression cannot be evaluated,
/// the value at the target address is overwritten with a `Top` value.
pub fn handle_store(
&mut self,
address: &Expression,
value: &Expression,
global_memory: &RuntimeMemoryImage,
) -> Result<(), Error> {
match self.eval(value) {
Ok(data) => self.write_to_address(address, &data),
Ok(data) => self.write_to_address(address, &data, global_memory),
Err(err) => {
// we still need to write to the target location before reporting the error
self.write_to_address(address, &Data::new_top(value.bytesize()))?;
self.write_to_address(address, &Data::new_top(value.bytesize()), global_memory)?;
Err(err)
}
}
}
/// Evaluate the given load instruction and return the data read on success.
pub fn load_value(&self, address: &Expression, size: ByteSize) -> Result<Data, Error> {
Ok(self
.memory
.get_value(&self.adjust_pointer_for_read(&self.eval(address)?), size)?)
pub fn load_value(
&self,
address: &Expression,
size: ByteSize,
global_memory: &RuntimeMemoryImage,
) -> Result<Data, Error> {
let address = self.adjust_pointer_for_read(&self.eval(address)?);
match address {
Data::Value(BitvectorDomain::Value(address_bitvector)) => {
let loaded_value = global_memory.read(&address_bitvector, size)?;
if loaded_value.is_top() {
Ok(Data::Top(loaded_value.bytesize()))
} else {
Ok(Data::Value(loaded_value))
}
}
Data::Value(BitvectorDomain::Top(_)) | Data::Top(_) => Ok(Data::new_top(size)),
Data::Pointer(_) => Ok(self.memory.get_value(&address, size)?),
}
}
/// Handle a load instruction by assigning the value loaded from the address given by the `address` expression to `var`.
pub fn handle_load(&mut self, var: &Variable, address: &Expression) -> Result<(), Error> {
match self.load_value(address, var.size) {
pub fn handle_load(
&mut self,
var: &Variable,
address: &Expression,
global_memory: &RuntimeMemoryImage,
) -> Result<(), Error> {
match self.load_value(address, var.size, global_memory) {
Ok(data) => {
self.set_register(var, data);
Ok(())
......@@ -196,6 +245,7 @@ impl State {
&self,
parameter: &Arg,
stack_pointer: &Variable,
global_memory: &RuntimeMemoryImage,
) -> Result<Data, Error> {
match parameter {
Arg::Register(var) => self.eval(&Expression::Var(var.clone())),
......@@ -210,6 +260,7 @@ impl State {
)),
},
*size,
global_memory,
),
}
}
......
......@@ -3,6 +3,7 @@ use super::Data;
use crate::abstract_domain::*;
use crate::intermediate_representation::*;
use crate::prelude::*;
use crate::utils::binary::RuntimeMemoryImage;
use std::collections::{BTreeMap, BTreeSet};
mod access_handling;
......@@ -89,6 +90,7 @@ impl State {
&mut self,
extern_call: &ExternSymbol,
stack_pointer_register: &Variable,
global_memory: &RuntimeMemoryImage,
) -> Result<(), Error> {
let mut result_log = Ok(());
for arg in &extern_call.parameters {
......@@ -105,7 +107,9 @@ impl State {
.unwrap(),
)),
};
if let Err(err) = self.write_to_address(&location_expression, &data_top) {
if let Err(err) =
self.write_to_address(&location_expression, &data_top, global_memory)
{
result_log = Err(err);
}
}
......
......@@ -24,6 +24,7 @@ use crate::abstract_domain::{BitvectorDomain, DataDomain};
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;
......@@ -43,20 +44,24 @@ 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, block: &Term<Blk>) -> State {
fn compute_block_end_state(
project: &Project,
global_memory: &RuntimeMemoryImage,
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);
let _ = state.handle_store(address, value, global_memory);
}
Def::Assign { var, value } => {
let _ = state.handle_register_assign(var, value);
}
Def::Load { var, address } => {
let _ = state.handle_load(var, address);
let _ = state.handle_load(var, address, global_memory);
}
}
}
......@@ -66,14 +71,15 @@ fn compute_block_end_state(project: &Project, block: &Term<Blk>) -> 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, block);
let state = compute_block_end_state(project, global_memory, block);
for parameter in symbol.parameters.iter() {
if let Ok(DataDomain::Value(BitvectorDomain::Value(param_value))) =
state.eval_parameter_arg(parameter, &project.stack_pointer_register)
state.eval_parameter_arg(parameter, &project.stack_pointer_register, global_memory)
{
if Ok(u64::from(pointer_size)) == param_value.try_to_u64() {
return true;
......@@ -113,7 +119,12 @@ pub fn check_cwe(
let symbol_map = get_symbol_map(project, &config.symbols);
for sub in project.program.term.subs.iter() {
for (block, jmp, symbol) in get_callsites(sub, &symbol_map) {
if check_for_pointer_sized_arg(project, block, symbol) {
if check_for_pointer_sized_arg(
project,
analysis_results.runtime_memory_image,
block,
symbol,
) {
cwe_warnings.push(generate_cwe_warning(jmp, symbol))
}
}
......
......@@ -84,7 +84,12 @@ 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, &pointer_inference_results, cwe_sender);
let general_context = Context::new(
project,
analysis_results.runtime_memory_image,
&pointer_inference_results,
cwe_sender,
);
for edge in general_context.get_graph().edge_references() {
if let Edge::ExternCallStub(jmp) = edge.weight() {
......
......@@ -9,6 +9,7 @@ use crate::analysis::pointer_inference::PointerInference as PointerInferenceComp
use crate::analysis::pointer_inference::State as PointerInferenceState;
use crate::intermediate_representation::*;
use crate::prelude::*;
use crate::utils::binary::RuntimeMemoryImage;
use crate::utils::log::CweWarning;
use petgraph::graph::NodeIndex;
use petgraph::visit::IntoNodeReferences;
......@@ -26,6 +27,8 @@ 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.
......@@ -62,6 +65,7 @@ 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 {
......@@ -89,6 +93,7 @@ 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),
......@@ -220,9 +225,11 @@ impl<'a> Context<'a> {
return true;
}
}
if let Ok(stack_param) = pi_state
.eval_parameter_arg(parameter, &self.project.stack_pointer_register)
{
if let Ok(stack_param) = pi_state.eval_parameter_arg(
parameter,
&self.project.stack_pointer_register,
&self.runtime_memory_image,
) {
if state.check_if_address_points_to_taint(stack_param, pi_state) {
return true;
}
......@@ -428,14 +435,16 @@ 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, pi_results, cwe_sender);
let mut context = Context::new(project, runtime_memory_image, pi_results, cwe_sender);
let taint_source = Box::new(Term {
tid: Tid::new("taint_source"),
term: Jmp::Call {
......@@ -454,8 +463,9 @@ mod tests {
#[test]
fn check_parameter_arg_for_taint() {
let project = Project::mock_empty();
let pi_results = PointerInferenceComputation::mock(&project);
let context = Context::mock(&project, &pi_results);
let runtime_memory_image = RuntimeMemoryImage::mock();
let pi_results = PointerInferenceComputation::mock(&project, &runtime_memory_image);
let context = Context::mock(&project, &runtime_memory_image, &pi_results);
let (mut state, _pi_state) = State::mock_with_pi_state();
assert_eq!(
......@@ -476,8 +486,9 @@ mod tests {
#[test]
fn handle_generic_call() {
let project = Project::mock_empty();
let pi_results = PointerInferenceComputation::mock(&project);
let context = Context::mock(&project, &pi_results);
let runtime_memory_image = RuntimeMemoryImage::mock();
let pi_results = PointerInferenceComputation::mock(&project, &runtime_memory_image);
let context = Context::mock(&project, &runtime_memory_image, &pi_results);
let mut state = State::mock();
assert!(context
......@@ -496,8 +507,9 @@ mod tests {
#[test]
fn update_def() {
let project = Project::mock_empty();
let pi_results = PointerInferenceComputation::mock(&project);
let context = Context::mock(&project, &pi_results);
let runtime_memory_image = RuntimeMemoryImage::mock();
let pi_results = PointerInferenceComputation::mock(&project, &runtime_memory_image);
let context = Context::mock(&project, &runtime_memory_image, &pi_results);
let (mut state, pi_state) = State::mock_with_pi_state();
state.set_pointer_inference_state(Some(pi_state));
......@@ -548,8 +560,9 @@ mod tests {
#[test]
fn update_jump() {
let project = Project::mock_empty();
let pi_results = PointerInferenceComputation::mock(&project);
let context = Context::mock(&project, &pi_results);
let runtime_memory_image = RuntimeMemoryImage::mock();
let pi_results = PointerInferenceComputation::mock(&project, &runtime_memory_image);
let context = Context::mock(&project, &runtime_memory_image, &pi_results);
let (state, _pi_state) = State::mock_with_pi_state();
let jump = Term {
......
......@@ -26,6 +26,7 @@ use crate::abstract_domain::{BitvectorDomain, DataDomain};
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;
......@@ -46,6 +47,7 @@ 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());
......@@ -53,19 +55,20 @@ 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);
let _ = state.handle_store(address, value, global_memory);
}
Def::Assign { var, value } => {
let _ = state.handle_register_assign(var, value);
}
Def::Load { var, address } => {
let _ = state.handle_load(var, address);
let _ = state.handle_load(var, address, global_memory);
}
}
}
let parameter = umask_symbol.get_unique_parameter()?;
let param_value = state.eval_parameter_arg(parameter, &project.stack_pointer_register)?;
let param_value =
state.eval_parameter_arg(parameter, &project.stack_pointer_register, global_memory)?;
if let DataDomain::Value(BitvectorDomain::Value(umask_arg)) = param_value {
Ok(umask_arg.try_to_u64()?)
} else {
......@@ -108,7 +111,12 @@ pub fn check_cwe(
if !umask_symbol_map.is_empty() {
for sub in project.program.term.subs.iter() {
for (block, jmp, umask_symbol) in get_callsites(sub, &umask_symbol_map) {
match get_umask_permission_arg(block, umask_symbol, project) {
match get_umask_permission_arg(
block,
umask_symbol,
project,
analysis_results.runtime_memory_image,
) {
Ok(permission_const) => {
if is_chmod_style_arg(permission_const) {
cwes.push(generate_cwe_warning(sub, jmp, permission_const));
......
use super::serde::JsonBuilder;
use super::OcamlSendable;
use crate::term::*;
use crate::utils::log::CweWarning;
use crate::{analysis::pointer_inference::PointerInference, term::*};
use super::failwith_on_panic;
#[allow(unreachable_code)]
#[allow(unused_variables)]
fn run_pointer_inference(program_jsonbuilder_val: ocaml::Value) -> (Vec<CweWarning>, Vec<String>) {
let json_builder = unsafe { JsonBuilder::from_ocaml(&program_jsonbuilder_val) };
let program_json = serde_json::Value::from(json_builder);
......@@ -13,11 +15,12 @@ fn run_pointer_inference(program_jsonbuilder_val: ocaml::Value) -> (Vec<CweWarni
project.replace_let_bindings();
let mut project: crate::intermediate_representation::Project = project.into();
let mut all_logs = project.normalize();
let all_logs = project.normalize();
let config: crate::analysis::pointer_inference::Config =
serde_json::from_value(crate::utils::read_config_file("config.json")["Memory"].clone())
.unwrap();
let pi_analysis = crate::analysis::pointer_inference::run(&project, config, false);
// let pi_analysis = crate::analysis::pointer_inference::run(&project, config, false);
let pi_analysis: PointerInference = panic!("Running the pointer inference analysis with the BAP backend is deprecated. Please use the Ghidra backend for this analysis instead.");
let (mut logs, cwes) = pi_analysis.collected_logs;
all_logs.append(&mut logs);
(
......@@ -38,6 +41,7 @@ caml!(rs_run_pointer_inference(program_jsonbuilder_val) {
})
});
#[allow(unused_variables)]
fn run_pointer_inference_and_print_debug(program_jsonbuilder_val: ocaml::Value) {
let json_builder = unsafe { JsonBuilder::from_ocaml(&program_jsonbuilder_val) };
let program_json = serde_json::Value::from(json_builder);
......@@ -50,7 +54,8 @@ fn run_pointer_inference_and_print_debug(program_jsonbuilder_val: ocaml::Value)
let config: crate::analysis::pointer_inference::Config =
serde_json::from_value(crate::utils::read_config_file("config.json")["Memory"].clone())
.unwrap();
crate::analysis::pointer_inference::run(&project, config, true); // Note: This discard all CweWarnings and log messages.
panic!("Running the pointer inference analysis with the BAP backend is deprecated. Please use the Ghidra backend for this analysis instead.");
// crate::analysis::pointer_inference::run(&project, config, true); // Note: This discard all CweWarnings and log messages.
}
caml!(rs_run_pointer_inference_and_print_debug(program_jsonbuilder_val) {
......
......@@ -104,6 +104,7 @@ impl<'a> AnalysisResults<'a> {
pub fn compute_pointer_inference(&self, config: &serde_json::Value) -> PointerInference<'a> {
crate::analysis::pointer_inference::run(
self.project,
self.runtime_memory_image,
serde_json::from_value(config.clone()).unwrap(),
false,
)
......
......@@ -192,6 +192,21 @@ impl RuntimeMemoryImage {
}
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"))
}
}
#[cfg(test)]
......@@ -202,13 +217,22 @@ pub mod tests {
/// Create a mock runtime memory image for unit tests.
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,
}],
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,
},
],
is_little_endian: true,
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment