Commit 4beae232 by Enkelmann Committed by Enkelmann

Add config options to pointer inference analysis

parent 1e9e4ab9
...@@ -12,6 +12,7 @@ all: ...@@ -12,6 +12,7 @@ all:
cd plugins/cwe_checker_pointer_inference_debug && make all cd plugins/cwe_checker_pointer_inference_debug && make all
mkdir -p ${HOME}/.config/cwe_checker mkdir -p ${HOME}/.config/cwe_checker
cp src/utils/registers.json ${HOME}/.config/cwe_checker/registers.json cp src/utils/registers.json ${HOME}/.config/cwe_checker/registers.json
cp src/config.json ${HOME}/.config/cwe_checker/config.json
test: test:
cargo test cargo test
......
...@@ -7,7 +7,7 @@ use crate::utils::log::*; ...@@ -7,7 +7,7 @@ use crate::utils::log::*;
use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::collections::{BTreeMap, BTreeSet, HashSet};
use super::state::State; use super::state::State;
use super::{Data, VERSION}; use super::{Config, Data, VERSION};
// contains trait implementations for the `Context` struct, // contains trait implementations for the `Context` struct,
// especially the implementation of the `interprocedural_fixpoint::Context` trait. // especially the implementation of the `interprocedural_fixpoint::Context` trait.
...@@ -31,6 +31,10 @@ pub struct Context<'a> { ...@@ -31,6 +31,10 @@ pub struct Context<'a> {
pub cwe_collector: crossbeam_channel::Sender<CweWarning>, pub cwe_collector: crossbeam_channel::Sender<CweWarning>,
/// A channel where log messages should be sent to. /// A channel where log messages should be sent to.
pub log_collector: crossbeam_channel::Sender<LogMessage>, pub log_collector: crossbeam_channel::Sender<LogMessage>,
/// Names of `malloc`-like extern functions.
pub allocation_symbols: Vec<String>,
/// Names of `free`-like extern functions.
pub deallocation_symbols: Vec<String>,
} }
impl<'a> Context<'a> { impl<'a> Context<'a> {
...@@ -38,6 +42,7 @@ impl<'a> Context<'a> { ...@@ -38,6 +42,7 @@ impl<'a> Context<'a> {
/// Also needs two channels as input to know where CWE warnings and log messages should be sent to. /// Also needs two channels as input to know where CWE warnings and log messages should be sent to.
pub fn new( pub fn new(
project: &Project, project: &Project,
config: Config,
cwe_collector: crossbeam_channel::Sender<CweWarning>, cwe_collector: crossbeam_channel::Sender<CweWarning>,
log_collector: crossbeam_channel::Sender<LogMessage>, log_collector: crossbeam_channel::Sender<LogMessage>,
) -> Context { ) -> Context {
...@@ -60,6 +65,8 @@ impl<'a> Context<'a> { ...@@ -60,6 +65,8 @@ impl<'a> Context<'a> {
extern_symbol_map, extern_symbol_map,
cwe_collector, cwe_collector,
log_collector, log_collector,
allocation_symbols: config.allocation_symbols,
deallocation_symbols: config.deallocation_symbols,
} }
} }
......
...@@ -66,7 +66,7 @@ fn return_term(target_name: &str) -> Term<Jmp> { ...@@ -66,7 +66,7 @@ fn return_term(target_name: &str) -> Term<Jmp> {
} }
} }
fn mock_project() -> Project { fn mock_project() -> (Project, Config) {
let program = Program { let program = Program {
subs: Vec::new(), subs: Vec::new(),
extern_symbols: vec![ extern_symbols: vec![
...@@ -80,13 +80,19 @@ fn mock_project() -> Project { ...@@ -80,13 +80,19 @@ fn mock_project() -> Project {
tid: Tid::new("program"), tid: Tid::new("program"),
term: program, term: program,
}; };
(
Project { Project {
program: program_term, program: program_term,
cpu_architecture: "x86_64".to_string(), cpu_architecture: "x86_64".to_string(),
stack_pointer_register: register("RSP"), stack_pointer_register: register("RSP"),
callee_saved_registers: vec!["callee_saved_reg".to_string()], callee_saved_registers: vec!["callee_saved_reg".to_string()],
parameter_registers: vec!["RAX".to_string()], parameter_registers: vec!["RAX".to_string()],
} },
Config {
allocation_symbols: vec!["malloc".into()],
deallocation_symbols: vec!["free".into()],
},
)
} }
#[test] #[test]
...@@ -95,10 +101,10 @@ fn context_problem_implementation() { ...@@ -95,10 +101,10 @@ fn context_problem_implementation() {
use crate::analysis::pointer_inference::Data; use crate::analysis::pointer_inference::Data;
use Expression::*; use Expression::*;
let project = mock_project(); let (project, config) = mock_project();
let (cwe_sender, _cwe_receiver) = crossbeam_channel::unbounded(); let (cwe_sender, _cwe_receiver) = crossbeam_channel::unbounded();
let (log_sender, _log_receiver) = crossbeam_channel::unbounded(); let (log_sender, _log_receiver) = crossbeam_channel::unbounded();
let context = Context::new(&project, cwe_sender, log_sender); let context = Context::new(&project, config, cwe_sender, 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 {
...@@ -251,10 +257,10 @@ fn update_return() { ...@@ -251,10 +257,10 @@ fn update_return() {
use crate::analysis::interprocedural_fixpoint::Context as IpFpContext; use crate::analysis::interprocedural_fixpoint::Context as IpFpContext;
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 = mock_project(); let (project, config) = mock_project();
let (cwe_sender, _cwe_receiver) = crossbeam_channel::unbounded(); let (cwe_sender, _cwe_receiver) = crossbeam_channel::unbounded();
let (log_sender, _log_receiver) = crossbeam_channel::unbounded(); let (log_sender, _log_receiver) = crossbeam_channel::unbounded();
let context = Context::new(&project, cwe_sender, log_sender); let context = Context::new(&project, config, cwe_sender, 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(
......
...@@ -233,10 +233,10 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a> ...@@ -233,10 +233,10 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a>
self.check_parameter_register_for_dangling_pointer(state, call, extern_symbol); self.check_parameter_register_for_dangling_pointer(state, call, extern_symbol);
match extern_symbol.name.as_str() { match extern_symbol.name.as_str() {
"malloc" | "calloc" | "realloc" | "xmalloc" => { malloc_like_fn if self.allocation_symbols.iter().any(|x| x == malloc_like_fn) => {
self.add_new_object_in_call_return_register(new_state, call, extern_symbol) self.add_new_object_in_call_return_register(new_state, call, extern_symbol)
} }
"free" => { free_like_fn if self.deallocation_symbols.iter().any(|x| x == free_like_fn) => {
self.mark_parameter_object_as_freed(state, new_state, call, extern_symbol) self.mark_parameter_object_as_freed(state, new_state, call, extern_symbol)
} }
_ => self.handle_generic_extern_call(state, new_state, call, extern_symbol), _ => self.handle_generic_extern_call(state, new_state, call, extern_symbol),
......
...@@ -10,11 +10,14 @@ ...@@ -10,11 +10,14 @@
//! whether an error is due to an error in the memory management of the program under analysis //! whether an error is due to an error in the memory management of the program under analysis
//! or due to inexactness of the pointer inference analysis itself, //! or due to inexactness of the pointer inference analysis itself,
//! we try to treat is as the more likely (but not necessarily true) case of the two. //! we try to treat is as the more likely (but not necessarily true) case of the two.
//!
//! See the `Config` struct for configurable analysis parameters.
use super::interprocedural_fixpoint::{Computation, NodeValue}; use super::interprocedural_fixpoint::{Computation, NodeValue};
use crate::abstract_domain::{BitvectorDomain, DataDomain}; use crate::abstract_domain::{BitvectorDomain, DataDomain};
use crate::analysis::graph::{Graph, Node}; use crate::analysis::graph::{Graph, Node};
use crate::intermediate_representation::*; use crate::intermediate_representation::*;
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;
...@@ -35,6 +38,19 @@ const VERSION: &str = "0.1"; ...@@ -35,6 +38,19 @@ const VERSION: &str = "0.1";
/// The abstract domain type for representing register values. /// The abstract domain type for representing register values.
type Data = DataDomain<BitvectorDomain>; type Data = DataDomain<BitvectorDomain>;
/// Configurable parameters for the analysis.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Config {
/// Names of extern functions that are `malloc`-like,
/// i.e. the unique return value is a pointer to a newly allocated chunk of memory or a NULL pointer.
allocation_symbols: Vec<String>,
/// Names of extern functions that are `free`-like,
/// i.e. the memory chunk that the unique parameter of the function points to gets deallocated.
/// Note that the analysis currently does not detect mismatching allocation-deallocation pairs,
/// i.e. it cannot distinguish between memory allocated by `malloc` and memory allocated by `new`.
deallocation_symbols: Vec<String>,
}
/// A wrapper struct for the pointer inference computation object. /// A wrapper struct for the pointer inference computation object.
pub struct PointerInference<'a> { pub struct PointerInference<'a> {
computation: Computation<'a, Context<'a>>, computation: Computation<'a, Context<'a>>,
...@@ -45,10 +61,11 @@ impl<'a> PointerInference<'a> { ...@@ -45,10 +61,11 @@ impl<'a> PointerInference<'a> {
/// Generate a new pointer inference compuation for a project. /// Generate a new pointer inference compuation for a project.
pub fn new( pub fn new(
project: &'a Project, project: &'a Project,
config: Config,
cwe_sender: crossbeam_channel::Sender<CweWarning>, cwe_sender: crossbeam_channel::Sender<CweWarning>,
log_sender: crossbeam_channel::Sender<LogMessage>, log_sender: crossbeam_channel::Sender<LogMessage>,
) -> PointerInference<'a> { ) -> PointerInference<'a> {
let context = Context::new(project, cwe_sender, log_sender.clone()); let context = Context::new(project, config, cwe_sender, 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
...@@ -247,7 +264,7 @@ impl<'a> PointerInference<'a> { ...@@ -247,7 +264,7 @@ impl<'a> PointerInference<'a> {
/// Generate and execute the pointer inference analysis. /// Generate and execute the pointer inference analysis.
/// Returns a vector of all found CWE warnings and a vector of all log messages generated during analysis. /// Returns a vector of all found CWE warnings and a vector of all log messages generated during analysis.
pub fn run(project: &Project, print_debug: bool) -> (Vec<CweWarning>, Vec<String>) { pub fn run(project: &Project, config: Config, print_debug: bool) -> (Vec<CweWarning>, Vec<String>) {
let (cwe_sender, cwe_receiver) = crossbeam_channel::unbounded(); let (cwe_sender, cwe_receiver) = crossbeam_channel::unbounded();
let (log_sender, log_receiver) = crossbeam_channel::unbounded(); let (log_sender, log_receiver) = crossbeam_channel::unbounded();
...@@ -257,7 +274,7 @@ pub fn run(project: &Project, print_debug: bool) -> (Vec<CweWarning>, Vec<String ...@@ -257,7 +274,7 @@ pub fn run(project: &Project, print_debug: bool) -> (Vec<CweWarning>, Vec<String
{ {
// Scope the computation object so that it is dropped before the warning collector thread is joined. // Scope the computation object so that it is dropped before the warning collector thread is joined.
// Else the warning collector thread will not terminate (the cwe_sender needs to be dropped for it to terminate). // Else the warning collector thread will not terminate (the cwe_sender needs to be dropped for it to terminate).
let mut computation = PointerInference::new(project, cwe_sender, log_sender); let mut computation = PointerInference::new(project, config, cwe_sender, log_sender);
computation.compute(); computation.compute();
computation.count_blocks_with_state(); computation.count_blocks_with_state();
......
...@@ -12,7 +12,10 @@ fn run_pointer_inference(program_jsonbuilder_val: ocaml::Value) -> (Vec<CweWarni ...@@ -12,7 +12,10 @@ fn run_pointer_inference(program_jsonbuilder_val: ocaml::Value) -> (Vec<CweWarni
serde_json::from_value(program_json).expect("Project deserialization failed"); serde_json::from_value(program_json).expect("Project deserialization failed");
project.replace_let_bindings(); project.replace_let_bindings();
crate::analysis::pointer_inference::run(&project.into(), false) let config: crate::analysis::pointer_inference::Config =
serde_json::from_value(crate::utils::read_config_file()["pointer_inference"].clone())
.unwrap();
crate::analysis::pointer_inference::run(&project.into(), config, false)
} }
caml!(rs_run_pointer_inference(program_jsonbuilder_val) { caml!(rs_run_pointer_inference(program_jsonbuilder_val) {
...@@ -31,7 +34,10 @@ fn run_pointer_inference_and_print_debug(program_jsonbuilder_val: ocaml::Value) ...@@ -31,7 +34,10 @@ fn run_pointer_inference_and_print_debug(program_jsonbuilder_val: ocaml::Value)
serde_json::from_value(program_json).expect("Project deserialization failed"); serde_json::from_value(program_json).expect("Project deserialization failed");
project.replace_let_bindings(); project.replace_let_bindings();
crate::analysis::pointer_inference::run(&project.into(), true); // Note: This discard all CweWarnings and log messages. let config: crate::analysis::pointer_inference::Config =
serde_json::from_value(crate::utils::read_config_file()["pointer_inference"].clone())
.unwrap();
crate::analysis::pointer_inference::run(&project.into(), config, true); // Note: This discard all CweWarnings and log messages.
} }
caml!(rs_run_pointer_inference_and_print_debug(program_jsonbuilder_val) { caml!(rs_run_pointer_inference_and_print_debug(program_jsonbuilder_val) {
......
...@@ -30,3 +30,14 @@ pub fn get_generic_parameter_and_callee_saved_register( ...@@ -30,3 +30,14 @@ pub fn get_generic_parameter_and_callee_saved_register(
params.append(&mut params_float); params.append(&mut params_float);
(params, callee_saved) (params, callee_saved)
} }
/// Get the contents of the main configuration file.
pub fn read_config_file() -> serde_json::Value {
let project_dirs = directories::ProjectDirs::from("", "", "cwe_checker")
.expect("Could not discern location of configuration files.");
let config_dir = project_dirs.config_dir();
let config_path = config_dir.join("config.json");
let config_file =
std::fs::read_to_string(config_path).expect("Could not read register configuration file");
serde_json::from_str(&config_file).unwrap()
}
...@@ -190,5 +190,16 @@ ...@@ -190,5 +190,16 @@
"fgets", "fgets",
"scanf" "scanf"
] ]
},
"pointer_inference": {
"allocation_symbols": [
"malloc",
"calloc",
"realloc",
"xmalloc"
],
"deallocation_symbols": [
"free"
]
} }
} }
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