Unverified Commit 02864348 by Enkelmann Committed by GitHub

provide control flow graph independently of pointer inference analysis (#146)

parent 5b09cb15
use cwe_checker_rs::analysis::graph;
use cwe_checker_rs::utils::binary::RuntimeMemoryImage; use cwe_checker_rs::utils::binary::RuntimeMemoryImage;
use cwe_checker_rs::utils::log::print_all_messages; use cwe_checker_rs::utils::log::print_all_messages;
use cwe_checker_rs::utils::{get_ghidra_plugin_path, read_config_file}; use cwe_checker_rs::utils::{get_ghidra_plugin_path, read_config_file};
...@@ -155,11 +156,24 @@ fn run_with_ghidra(args: CmdlineArgs) { ...@@ -155,11 +156,24 @@ fn run_with_ghidra(args: CmdlineArgs) {
// so that other analyses do not have to adjust their addresses. // so that other analyses do not have to adjust their addresses.
runtime_memory_image.add_global_memory_offset(project.program.term.address_base_offset); runtime_memory_image.add_global_memory_offset(project.program.term.address_base_offset);
} }
// Generate the control flow graph of the program
let extern_sub_tids = project
.program
.term
.extern_symbols
.iter()
.map(|symbol| symbol.tid.clone())
.collect();
let control_flow_graph = graph::get_program_cfg(&project.program, extern_sub_tids);
let mut analysis_results = AnalysisResults::new(&binary, &runtime_memory_image, &project); let analysis_results = AnalysisResults::new(
&binary,
&runtime_memory_image,
&control_flow_graph,
&project,
);
let modules_depending_on_pointer_inference = let modules_depending_on_pointer_inference = vec!["CWE78", "CWE476", "Memory"];
vec!["CWE78", "CWE243", "CWE367", "CWE476", "Memory"];
let pointer_inference_results = if modules let pointer_inference_results = if modules
.iter() .iter()
.any(|module| modules_depending_on_pointer_inference.contains(&module.name)) .any(|module| modules_depending_on_pointer_inference.contains(&module.name))
...@@ -168,7 +182,8 @@ fn run_with_ghidra(args: CmdlineArgs) { ...@@ -168,7 +182,8 @@ fn run_with_ghidra(args: CmdlineArgs) {
} else { } else {
None None
}; };
analysis_results = analysis_results.set_pointer_inference(pointer_inference_results.as_ref()); let analysis_results =
analysis_results.set_pointer_inference(pointer_inference_results.as_ref());
// Print debug and then return. // Print debug and then return.
// Right now there is only one debug printing function. // Right now there is only one debug printing function.
...@@ -177,6 +192,7 @@ fn run_with_ghidra(args: CmdlineArgs) { ...@@ -177,6 +192,7 @@ fn run_with_ghidra(args: CmdlineArgs) {
cwe_checker_rs::analysis::pointer_inference::run( cwe_checker_rs::analysis::pointer_inference::run(
&project, &project,
&runtime_memory_image, &runtime_memory_image,
&control_flow_graph,
serde_json::from_value(config["Memory"].clone()).unwrap(), serde_json::from_value(config["Memory"].clone()).unwrap(),
true, true,
); );
......
...@@ -4,7 +4,7 @@ use crate::intermediate_representation::*; ...@@ -4,7 +4,7 @@ use crate::intermediate_representation::*;
use crate::prelude::*; use crate::prelude::*;
use crate::utils::log::*; use crate::utils::log::*;
use crate::{abstract_domain::*, utils::binary::RuntimeMemoryImage}; use crate::{abstract_domain::*, utils::binary::RuntimeMemoryImage};
use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::collections::{BTreeMap, BTreeSet};
use super::state::State; use super::state::State;
use super::{Config, Data, VERSION}; use super::{Config, Data, VERSION};
...@@ -18,7 +18,7 @@ mod trait_impls; ...@@ -18,7 +18,7 @@ mod trait_impls;
/// The struct also implements the `interprocedural_fixpoint::Context` trait to enable the fixpoint computation. /// The struct also implements the `interprocedural_fixpoint::Context` trait to enable the fixpoint computation.
pub struct Context<'a> { pub struct Context<'a> {
/// The program control flow graph on which the fixpoint will be computed /// The program control flow graph on which the fixpoint will be computed
pub graph: Graph<'a>, pub graph: &'a Graph<'a>,
/// A reference to the `Project` object representing the binary /// A reference to the `Project` object representing the binary
pub project: &'a Project, pub project: &'a Project,
/// The runtime memory image for reading global read-only variables. /// The runtime memory image for reading global read-only variables.
...@@ -44,6 +44,7 @@ impl<'a> Context<'a> { ...@@ -44,6 +44,7 @@ impl<'a> Context<'a> {
pub fn new( pub fn new(
project: &'a Project, project: &'a Project,
runtime_memory_image: &'a RuntimeMemoryImage, runtime_memory_image: &'a RuntimeMemoryImage,
control_flow_graph: &'a Graph<'a>,
config: Config, config: Config,
log_collector: crossbeam_channel::Sender<LogThreadMsg>, log_collector: crossbeam_channel::Sender<LogThreadMsg>,
) -> Context<'a> { ) -> Context<'a> {
...@@ -51,17 +52,8 @@ impl<'a> Context<'a> { ...@@ -51,17 +52,8 @@ impl<'a> Context<'a> {
for symbol in project.program.term.extern_symbols.iter() { for symbol in project.program.term.extern_symbols.iter() {
extern_symbol_map.insert(symbol.tid.clone(), symbol); extern_symbol_map.insert(symbol.tid.clone(), symbol);
} }
let extern_symbol_tid_set: HashSet<Tid> = project
.program
.term
.extern_symbols
.iter()
.map(|symb| symb.tid.clone())
.collect();
let graph =
crate::analysis::graph::get_program_cfg(&project.program, extern_symbol_tid_set);
Context { Context {
graph, graph: control_flow_graph,
project, project,
runtime_memory_image, runtime_memory_image,
extern_symbol_map, extern_symbol_map,
......
use super::*; use super::*;
use std::collections::HashSet;
fn bv(value: i64) -> BitvectorDomain { fn bv(value: i64) -> BitvectorDomain {
BitvectorDomain::Value(Bitvector::from_i64(value)) BitvectorDomain::Value(Bitvector::from_i64(value))
...@@ -106,8 +107,9 @@ fn context_problem_implementation() { ...@@ -106,8 +107,9 @@ fn context_problem_implementation() {
let (project, config) = mock_project(); let (project, config) = mock_project();
let runtime_memory_image = RuntimeMemoryImage::mock(); let runtime_memory_image = RuntimeMemoryImage::mock();
let graph = crate::analysis::graph::get_program_cfg(&project.program, HashSet::new());
let (log_sender, _log_receiver) = crossbeam_channel::unbounded(); let (log_sender, _log_receiver) = crossbeam_channel::unbounded();
let context = Context::new(&project, &runtime_memory_image, config, log_sender); let context = Context::new(&project, &runtime_memory_image, &graph, config, log_sender);
let mut state = State::new(&register("RSP"), Tid::new("main")); let mut state = State::new(&register("RSP"), Tid::new("main"));
let def = Term { let def = Term {
...@@ -283,9 +285,10 @@ fn update_return() { ...@@ -283,9 +285,10 @@ fn update_return() {
use crate::analysis::pointer_inference::object::ObjectType; use crate::analysis::pointer_inference::object::ObjectType;
use crate::analysis::pointer_inference::Data; use crate::analysis::pointer_inference::Data;
let (project, config) = mock_project(); let (project, config) = mock_project();
let graph = crate::analysis::graph::get_program_cfg(&project.program, HashSet::new());
let runtime_memory_image = RuntimeMemoryImage::mock(); let runtime_memory_image = RuntimeMemoryImage::mock();
let (log_sender, _log_receiver) = crossbeam_channel::unbounded(); let (log_sender, _log_receiver) = crossbeam_channel::unbounded();
let context = Context::new(&project, &runtime_memory_image, config, log_sender); let context = Context::new(&project, &runtime_memory_image, &graph, config, log_sender);
let state_before_return = State::new(&register("RSP"), Tid::new("callee")); let state_before_return = State::new(&register("RSP"), Tid::new("callee"));
let mut state_before_return = context let mut state_before_return = context
.update_def( .update_def(
......
...@@ -74,10 +74,17 @@ impl<'a> PointerInference<'a> { ...@@ -74,10 +74,17 @@ impl<'a> PointerInference<'a> {
pub fn new( pub fn new(
project: &'a Project, project: &'a Project,
runtime_memory_image: &'a RuntimeMemoryImage, runtime_memory_image: &'a RuntimeMemoryImage,
control_flow_graph: &'a Graph<'a>,
config: Config, config: Config,
log_sender: crossbeam_channel::Sender<LogThreadMsg>, log_sender: crossbeam_channel::Sender<LogThreadMsg>,
) -> PointerInference<'a> { ) -> PointerInference<'a> {
let context = Context::new(project, runtime_memory_image, config, log_sender.clone()); let context = Context::new(
project,
runtime_memory_image,
control_flow_graph,
config,
log_sender.clone(),
);
let mut entry_sub_to_entry_blocks_map = HashMap::new(); let mut entry_sub_to_entry_blocks_map = HashMap::new();
let subs: HashMap<Tid, &Term<Sub>> = project let subs: HashMap<Tid, &Term<Sub>> = project
...@@ -405,6 +412,7 @@ pub fn extract_pi_analysis_results( ...@@ -405,6 +412,7 @@ pub fn extract_pi_analysis_results(
pub fn run<'a>( pub fn run<'a>(
project: &'a Project, project: &'a Project,
runtime_memory_image: &'a RuntimeMemoryImage, runtime_memory_image: &'a RuntimeMemoryImage,
control_flow_graph: &'a Graph<'a>,
config: Config, config: Config,
print_debug: bool, print_debug: bool,
) -> PointerInference<'a> { ) -> PointerInference<'a> {
...@@ -413,6 +421,7 @@ pub fn run<'a>( ...@@ -413,6 +421,7 @@ pub fn run<'a>(
let mut computation = PointerInference::new( let mut computation = PointerInference::new(
project, project,
runtime_memory_image, runtime_memory_image,
control_flow_graph,
config, config,
logging_thread.get_msg_sender(), logging_thread.get_msg_sender(),
); );
...@@ -474,13 +483,14 @@ mod tests { ...@@ -474,13 +483,14 @@ mod tests {
pub fn mock( pub fn mock(
project: &'a Project, project: &'a Project,
mem_image: &'a RuntimeMemoryImage, mem_image: &'a RuntimeMemoryImage,
graph: &'a Graph,
) -> PointerInference<'a> { ) -> PointerInference<'a> {
let config = Config { let config = Config {
allocation_symbols: vec!["malloc".to_string()], allocation_symbols: vec!["malloc".to_string()],
deallocation_symbols: vec!["free".to_string()], deallocation_symbols: vec!["free".to_string()],
}; };
let (log_sender, _) = crossbeam_channel::unbounded(); let (log_sender, _) = crossbeam_channel::unbounded();
PointerInference::new(project, mem_image, config, log_sender) PointerInference::new(project, mem_image, graph, config, log_sender)
} }
} }
} }
...@@ -116,7 +116,7 @@ pub fn check_cwe( ...@@ -116,7 +116,7 @@ pub fn check_cwe(
cwe_params: &serde_json::Value, cwe_params: &serde_json::Value,
) -> (Vec<LogMessage>, Vec<CweWarning>) { ) -> (Vec<LogMessage>, Vec<CweWarning>) {
let project = analysis_results.project; let project = analysis_results.project;
let graph = analysis_results.pointer_inference.unwrap().get_graph(); let graph = analysis_results.control_flow_graph;
let config: Config = serde_json::from_value(cwe_params.clone()).unwrap(); let config: Config = serde_json::from_value(cwe_params.clone()).unwrap();
let priviledge_dropping_tids: Vec<Tid> = config let priviledge_dropping_tids: Vec<Tid> = config
......
...@@ -72,7 +72,7 @@ pub fn check_cwe( ...@@ -72,7 +72,7 @@ pub fn check_cwe(
) -> (Vec<LogMessage>, Vec<CweWarning>) { ) -> (Vec<LogMessage>, Vec<CweWarning>) {
let config: Config = serde_json::from_value(cwe_params.clone()).unwrap(); let config: Config = serde_json::from_value(cwe_params.clone()).unwrap();
let project = analysis_results.project; let project = analysis_results.project;
let graph = analysis_results.pointer_inference.unwrap().get_graph(); let graph = analysis_results.control_flow_graph;
let mut cwe_warnings = Vec::new(); let mut cwe_warnings = Vec::new();
let symbol_map: HashMap<&str, Tid> = project let symbol_map: HashMap<&str, Tid> = project
......
...@@ -426,6 +426,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont ...@@ -426,6 +426,7 @@ impl<'a> crate::analysis::forward_interprocedural_fixpoint::Context<'a> for Cont
mod tests { mod tests {
use super::*; use super::*;
use crate::utils::binary::RuntimeMemoryImage; use crate::utils::binary::RuntimeMemoryImage;
use std::collections::HashSet;
impl<'a> Context<'a> { impl<'a> Context<'a> {
pub fn mock( pub fn mock(
...@@ -454,7 +455,8 @@ mod tests { ...@@ -454,7 +455,8 @@ mod tests {
fn check_parameter_arg_for_taint() { fn check_parameter_arg_for_taint() {
let project = Project::mock_empty(); let project = Project::mock_empty();
let runtime_memory_image = RuntimeMemoryImage::mock(); let runtime_memory_image = RuntimeMemoryImage::mock();
let pi_results = PointerInferenceComputation::mock(&project, &runtime_memory_image); let graph = crate::analysis::graph::get_program_cfg(&project.program, HashSet::new());
let pi_results = PointerInferenceComputation::mock(&project, &runtime_memory_image, &graph);
let context = Context::mock(&project, &runtime_memory_image, &pi_results); let context = Context::mock(&project, &runtime_memory_image, &pi_results);
let (mut state, _pi_state) = State::mock_with_pi_state(); let (mut state, _pi_state) = State::mock_with_pi_state();
...@@ -477,7 +479,8 @@ mod tests { ...@@ -477,7 +479,8 @@ mod tests {
fn handle_generic_call() { fn handle_generic_call() {
let project = Project::mock_empty(); let project = Project::mock_empty();
let runtime_memory_image = RuntimeMemoryImage::mock(); let runtime_memory_image = RuntimeMemoryImage::mock();
let pi_results = PointerInferenceComputation::mock(&project, &runtime_memory_image); let graph = crate::analysis::graph::get_program_cfg(&project.program, HashSet::new());
let pi_results = PointerInferenceComputation::mock(&project, &runtime_memory_image, &graph);
let context = Context::mock(&project, &runtime_memory_image, &pi_results); let context = Context::mock(&project, &runtime_memory_image, &pi_results);
let mut state = State::mock(); let mut state = State::mock();
...@@ -498,7 +501,8 @@ mod tests { ...@@ -498,7 +501,8 @@ mod tests {
fn update_def() { fn update_def() {
let project = Project::mock_empty(); let project = Project::mock_empty();
let runtime_memory_image = RuntimeMemoryImage::mock(); let runtime_memory_image = RuntimeMemoryImage::mock();
let pi_results = PointerInferenceComputation::mock(&project, &runtime_memory_image); let graph = crate::analysis::graph::get_program_cfg(&project.program, HashSet::new());
let pi_results = PointerInferenceComputation::mock(&project, &runtime_memory_image, &graph);
let context = Context::mock(&project, &runtime_memory_image, &pi_results); let context = Context::mock(&project, &runtime_memory_image, &pi_results);
let (mut state, pi_state) = State::mock_with_pi_state(); let (mut state, pi_state) = State::mock_with_pi_state();
state.set_pointer_inference_state(Some(pi_state)); state.set_pointer_inference_state(Some(pi_state));
...@@ -551,7 +555,8 @@ mod tests { ...@@ -551,7 +555,8 @@ mod tests {
fn update_jump() { fn update_jump() {
let project = Project::mock_empty(); let project = Project::mock_empty();
let runtime_memory_image = RuntimeMemoryImage::mock(); let runtime_memory_image = RuntimeMemoryImage::mock();
let pi_results = PointerInferenceComputation::mock(&project, &runtime_memory_image); let graph = crate::analysis::graph::get_program_cfg(&project.program, HashSet::new());
let pi_results = PointerInferenceComputation::mock(&project, &runtime_memory_image, &graph);
let context = Context::mock(&project, &runtime_memory_image, &pi_results); let context = Context::mock(&project, &runtime_memory_image, &pi_results);
let (state, _pi_state) = State::mock_with_pi_state(); let (state, _pi_state) = State::mock_with_pi_state();
......
...@@ -7,6 +7,7 @@ Parts of the cwe_checker that are written in Rust. ...@@ -7,6 +7,7 @@ Parts of the cwe_checker that are written in Rust.
#[macro_use] #[macro_use]
extern crate ocaml; extern crate ocaml;
use crate::analysis::graph::Graph;
use crate::analysis::pointer_inference::PointerInference; use crate::analysis::pointer_inference::PointerInference;
use crate::intermediate_representation::Project; use crate::intermediate_representation::Project;
use crate::utils::binary::RuntimeMemoryImage; use crate::utils::binary::RuntimeMemoryImage;
...@@ -79,6 +80,8 @@ pub struct AnalysisResults<'a> { ...@@ -79,6 +80,8 @@ pub struct AnalysisResults<'a> {
pub binary: &'a [u8], pub binary: &'a [u8],
/// A representation of the runtime memory image of the binary. /// A representation of the runtime memory image of the binary.
pub runtime_memory_image: &'a RuntimeMemoryImage, pub runtime_memory_image: &'a RuntimeMemoryImage,
/// The computed control flow graph of the program.
pub control_flow_graph: &'a Graph<'a>,
/// A pointer to the project struct /// A pointer to the project struct
pub project: &'a Project, pub project: &'a Project,
/// The result of the pointer inference analysis if already computed. /// The result of the pointer inference analysis if already computed.
...@@ -90,11 +93,13 @@ impl<'a> AnalysisResults<'a> { ...@@ -90,11 +93,13 @@ impl<'a> AnalysisResults<'a> {
pub fn new( pub fn new(
binary: &'a [u8], binary: &'a [u8],
runtime_memory_image: &'a RuntimeMemoryImage, runtime_memory_image: &'a RuntimeMemoryImage,
control_flow_graph: &'a Graph<'a>,
project: &'a Project, project: &'a Project,
) -> AnalysisResults<'a> { ) -> AnalysisResults<'a> {
AnalysisResults { AnalysisResults {
binary, binary,
runtime_memory_image, runtime_memory_image,
control_flow_graph,
project, project,
pointer_inference: None, pointer_inference: None,
} }
...@@ -102,10 +107,11 @@ impl<'a> AnalysisResults<'a> { ...@@ -102,10 +107,11 @@ impl<'a> AnalysisResults<'a> {
/// Compute the pointer inference analysis. /// Compute the pointer inference analysis.
/// The result gets returned, but not saved to the `AnalysisResults` struct itself. /// The result gets returned, but not saved to the `AnalysisResults` struct itself.
pub fn compute_pointer_inference(&self, config: &serde_json::Value) -> PointerInference<'a> { pub fn compute_pointer_inference(&'a self, config: &serde_json::Value) -> PointerInference<'a> {
crate::analysis::pointer_inference::run( crate::analysis::pointer_inference::run(
self.project, self.project,
self.runtime_memory_image, self.runtime_memory_image,
self.control_flow_graph,
serde_json::from_value(config.clone()).unwrap(), serde_json::from_value(config.clone()).unwrap(),
false, false,
) )
......
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