Skip to content

Commit

Permalink
Add config options to pointer inference analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
Enkelmann committed Nov 3, 2020
1 parent 1e9e4ab commit 4beae23
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 20 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ all:
cd plugins/cwe_checker_pointer_inference_debug && make all
mkdir -p ${HOME}/.config/cwe_checker
cp src/utils/registers.json ${HOME}/.config/cwe_checker/registers.json
cp src/config.json ${HOME}/.config/cwe_checker/config.json

test:
cargo test
Expand Down
9 changes: 8 additions & 1 deletion cwe_checker_rs/src/analysis/pointer_inference/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::utils::log::*;
use std::collections::{BTreeMap, BTreeSet, HashSet};

use super::state::State;
use super::{Data, VERSION};
use super::{Config, Data, VERSION};

// contains trait implementations for the `Context` struct,
// especially the implementation of the `interprocedural_fixpoint::Context` trait.
Expand All @@ -31,13 +31,18 @@ pub struct Context<'a> {
pub cwe_collector: crossbeam_channel::Sender<CweWarning>,
/// A channel where log messages should be sent to.
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> {
/// 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,
config: Config,
cwe_collector: crossbeam_channel::Sender<CweWarning>,
log_collector: crossbeam_channel::Sender<LogMessage>,
) -> Context {
Expand All @@ -60,6 +65,8 @@ impl<'a> Context<'a> {
extern_symbol_map,
cwe_collector,
log_collector,
allocation_symbols: config.allocation_symbols,
deallocation_symbols: config.deallocation_symbols,
}
}

Expand Down
30 changes: 18 additions & 12 deletions cwe_checker_rs/src/analysis/pointer_inference/context/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ fn return_term(target_name: &str) -> Term<Jmp> {
}
}

fn mock_project() -> Project {
fn mock_project() -> (Project, Config) {
let program = Program {
subs: Vec::new(),
extern_symbols: vec![
Expand All @@ -80,13 +80,19 @@ fn mock_project() -> Project {
tid: Tid::new("program"),
term: program,
};
Project {
program: program_term,
cpu_architecture: "x86_64".to_string(),
stack_pointer_register: register("RSP"),
callee_saved_registers: vec!["callee_saved_reg".to_string()],
parameter_registers: vec!["RAX".to_string()],
}
(
Project {
program: program_term,
cpu_architecture: "x86_64".to_string(),
stack_pointer_register: register("RSP"),
callee_saved_registers: vec!["callee_saved_reg".to_string()],
parameter_registers: vec!["RAX".to_string()],
},
Config {
allocation_symbols: vec!["malloc".into()],
deallocation_symbols: vec!["free".into()],
},
)
}

#[test]
Expand All @@ -95,10 +101,10 @@ fn context_problem_implementation() {
use crate::analysis::pointer_inference::Data;
use Expression::*;

let project = mock_project();
let (project, config) = mock_project();
let (cwe_sender, _cwe_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 def = Term {
Expand Down Expand Up @@ -251,10 +257,10 @@ fn update_return() {
use crate::analysis::interprocedural_fixpoint::Context as IpFpContext;
use crate::analysis::pointer_inference::object::ObjectType;
use crate::analysis::pointer_inference::Data;
let project = mock_project();
let (project, config) = mock_project();
let (cwe_sender, _cwe_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 mut state_before_return = context
.update_def(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

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)
}
"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.handle_generic_extern_call(state, new_state, call, extern_symbol),
Expand Down
23 changes: 20 additions & 3 deletions cwe_checker_rs/src/analysis/pointer_inference/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@
//! 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,
//! 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 crate::abstract_domain::{BitvectorDomain, DataDomain};
use crate::analysis::graph::{Graph, Node};
use crate::intermediate_representation::*;
use crate::prelude::*;
use crate::utils::log::*;
use petgraph::graph::NodeIndex;
use petgraph::visit::IntoNodeReferences;
Expand All @@ -35,6 +38,19 @@ const VERSION: &str = "0.1";
/// The abstract domain type for representing register values.
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.
pub struct PointerInference<'a> {
computation: Computation<'a, Context<'a>>,
Expand All @@ -45,10 +61,11 @@ impl<'a> PointerInference<'a> {
/// Generate a new pointer inference compuation for a project.
pub fn new(
project: &'a Project,
config: Config,
cwe_sender: crossbeam_channel::Sender<CweWarning>,
log_sender: crossbeam_channel::Sender<LogMessage>,
) -> 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 subs: HashMap<Tid, &Term<Sub>> = project
Expand Down Expand Up @@ -247,7 +264,7 @@ impl<'a> PointerInference<'a> {

/// 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.
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 (log_sender, log_receiver) = crossbeam_channel::unbounded();

Expand All @@ -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.
// 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.count_blocks_with_state();
Expand Down
10 changes: 8 additions & 2 deletions cwe_checker_rs/src/ffi/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");

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) {
Expand All @@ -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");

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) {
Expand Down
11 changes: 11 additions & 0 deletions cwe_checker_rs/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,14 @@ pub fn get_generic_parameter_and_callee_saved_register(
params.append(&mut params_float);
(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()
}
11 changes: 11 additions & 0 deletions src/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -190,5 +190,16 @@
"fgets",
"scanf"
]
},
"pointer_inference": {
"allocation_symbols": [
"malloc",
"calloc",
"realloc",
"xmalloc"
],
"deallocation_symbols": [
"free"
]
}
}

0 comments on commit 4beae23

Please sign in to comment.