Commit af32d275 by Enkelmann Committed by Enkelmann

Adapt the analysis modules to use the internal IR instead of the BAP IR.

parent d701731b
...@@ -5,11 +5,13 @@ all: ...@@ -5,11 +5,13 @@ all:
cp target/release/libcwe_checker_rs.so src/dllcwe_checker_rs.so cp target/release/libcwe_checker_rs.so src/dllcwe_checker_rs.so
dune build dune build
dune install dune install
cd plugins/cwe_checker; make all; cd ../.. cd plugins/cwe_checker && make all
cd plugins/cwe_checker_emulation; make all; cd ../.. cd plugins/cwe_checker_emulation && make all
cd plugins/cwe_checker_type_inference; make all; cd ../.. cd plugins/cwe_checker_type_inference && make all
cd plugins/cwe_checker_type_inference_print; make all; cd ../.. cd plugins/cwe_checker_type_inference_print && make all
cd plugins/cwe_checker_pointer_inference_debug; make all; cd ../.. cd plugins/cwe_checker_pointer_inference_debug && make all
mkdir ${HOME}/.config/cwe_checker
cp src/utils/registers.json ${HOME}/.config/cwe_checker/registers.json
test: test:
cargo test cargo test
...@@ -43,6 +45,7 @@ uninstall: ...@@ -43,6 +45,7 @@ uninstall:
cd plugins/cwe_checker_type_inference; make uninstall; cd ../.. cd plugins/cwe_checker_type_inference; make uninstall; cd ../..
cd plugins/cwe_checker_type_inference_print; make uninstall; cd ../.. cd plugins/cwe_checker_type_inference_print; make uninstall; cd ../..
cd plugins/cwe_checker_pointer_inference_debug; make uninstall; cd ../.. cd plugins/cwe_checker_pointer_inference_debug; make uninstall; cd ../..
rm -f -r ${HOME}/.config/cwe_checker
documentation: documentation:
dune build @doc dune build @doc
......
...@@ -15,6 +15,7 @@ fnv = "1.0" # a faster hash function for small keys like integers ...@@ -15,6 +15,7 @@ fnv = "1.0" # a faster hash function for small keys like integers
anyhow = "1.0" # for easy error types anyhow = "1.0" # for easy error types
crossbeam-channel = "0.4" crossbeam-channel = "0.4"
derive_more = "0.99" derive_more = "0.99"
directories = "3.0"
[lib] [lib]
name = "cwe_checker_rs" name = "cwe_checker_rs"
......
use crate::bil::variable::*; use crate::intermediate_representation::*;
use crate::prelude::*; use crate::prelude::*;
use derive_more::Deref; use derive_more::Deref;
use std::sync::Arc; use std::sync::Arc;
...@@ -57,7 +57,7 @@ impl std::fmt::Display for AbstractIdentifier { ...@@ -57,7 +57,7 @@ impl std::fmt::Display for AbstractIdentifier {
/// It is also impossible to accidently describe circular references. /// It is also impossible to accidently describe circular references.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
pub enum AbstractLocation { pub enum AbstractLocation {
Register(String, BitSize), Register(String, ByteSize),
Pointer(String, AbstractMemoryLocation), Pointer(String, AbstractMemoryLocation),
} }
...@@ -81,7 +81,7 @@ impl AbstractLocation { ...@@ -81,7 +81,7 @@ impl AbstractLocation {
} }
Ok(AbstractLocation::Register( Ok(AbstractLocation::Register(
variable.name.clone(), variable.name.clone(),
variable.bitsize()?, variable.size,
)) ))
} }
} }
......
//! This module defines traits describing general properties of abstract domains //! This module defines traits describing general properties of abstract domains
//! as well as several abstract domain types implementing these traits. //! as well as several abstract domain types implementing these traits.
use crate::bil::*; use crate::intermediate_representation::*;
mod bitvector; mod bitvector;
pub use bitvector::*; pub use bitvector::*;
...@@ -29,14 +29,14 @@ pub trait AbstractDomain: Sized + Eq + Clone { ...@@ -29,14 +29,14 @@ pub trait AbstractDomain: Sized + Eq + Clone {
fn is_top(&self) -> bool; fn is_top(&self) -> bool;
} }
/// A trait for types representing values with a fixed size (in bits). /// A trait for types representing values with a fixed size (in bytes).
/// ///
/// For abstract domains, the bitsize is a parameter of the domain itself, /// For abstract domains, the bytesize is a parameter of the domain itself,
/// i.e. you cannot merge values of different bitsizes, /// i.e. you cannot merge values of different bytesizes,
/// since they lie in different posets (one for each bitsize). /// since they lie in different posets (one for each bytesize).
pub trait HasBitSize { pub trait HasByteSize {
/// Return the size of the represented value in bits. /// Return the size of the represented value in bytes.
fn bitsize(&self) -> BitSize; fn bytesize(&self) -> ByteSize;
} }
/// An abstract domain implementing this trait has a global maximum, i.e. a *Top* element. /// An abstract domain implementing this trait has a global maximum, i.e. a *Top* element.
...@@ -52,11 +52,11 @@ pub trait HasTop { ...@@ -52,11 +52,11 @@ pub trait HasTop {
/// A trait for abstract domains that can represent values loaded into CPU register. /// A trait for abstract domains that can represent values loaded into CPU register.
/// ///
/// The domain implements all general operations used to manipulate register values. /// The domain implements all general operations used to manipulate register values.
/// The domain is parametrized by its bitsize (which represents the size of the register). /// The domain is parametrized by its bytesize (which represents the size of the register).
/// It has a *Top* element, which is only characterized by its bitsize. /// It has a *Top* element, which is only characterized by its bytesize.
pub trait RegisterDomain: AbstractDomain + HasBitSize + HasTop { pub trait RegisterDomain: AbstractDomain + HasByteSize + HasTop {
/// Return a new top element with the given bitsize /// Return a new top element with the given bytesize
fn new_top(bitsize: BitSize) -> Self; fn new_top(bytesize: ByteSize) -> Self;
/// Compute the (abstract) result of a binary operation /// Compute the (abstract) result of a binary operation
fn bin_op(&self, op: BinOpType, rhs: &Self) -> Self; fn bin_op(&self, op: BinOpType, rhs: &Self) -> Self;
...@@ -64,12 +64,9 @@ pub trait RegisterDomain: AbstractDomain + HasBitSize + HasTop { ...@@ -64,12 +64,9 @@ pub trait RegisterDomain: AbstractDomain + HasBitSize + HasTop {
/// Compute the (abstract) result of a unary operation /// Compute the (abstract) result of a unary operation
fn un_op(&self, op: UnOpType) -> Self; fn un_op(&self, op: UnOpType) -> Self;
/// extract a sub-bitvector /// Extract a sub-bitvector
fn extract(&self, low_bit: BitSize, high_bit: BitSize) -> Self; fn subpiece(&self, low_byte: ByteSize, size: ByteSize) -> Self;
/// Extend a bitvector using the given cast type /// Perform a typecast to extend a bitvector or to cast between integer and floating point types.
fn cast(&self, kind: CastType, width: BitSize) -> Self; fn cast(&self, kind: CastOpType, width: ByteSize) -> Self;
/// Concatenate two bitvectors
fn concat(&self, other: &Self) -> Self;
} }
use super::{AbstractDomain, AbstractIdentifier, HasBitSize, RegisterDomain}; use super::{AbstractDomain, AbstractIdentifier, HasByteSize, RegisterDomain};
use crate::bil::BinOpType; use crate::intermediate_representation::{BinOpType, ByteSize};
use crate::prelude::*; use crate::prelude::*;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::Display; use std::fmt::Display;
...@@ -38,15 +38,15 @@ impl<T: RegisterDomain> AbstractDomain for PointerDomain<T> { ...@@ -38,15 +38,15 @@ impl<T: RegisterDomain> AbstractDomain for PointerDomain<T> {
} }
} }
impl<T: RegisterDomain> HasBitSize for PointerDomain<T> { impl<T: RegisterDomain> HasByteSize for PointerDomain<T> {
/// Return the bitsize of the pointer. /// Return the bitsize of the pointer.
/// Should always equal the pointer size of the CPU architecture. /// Should always equal the pointer size of the CPU architecture.
fn bitsize(&self) -> BitSize { fn bytesize(&self) -> ByteSize {
self.0 self.0
.values() .values()
.next() .next()
.expect("Pointer without targets encountered") .expect("Pointer without targets encountered")
.bitsize() .bytesize()
} }
} }
...@@ -84,7 +84,7 @@ impl<T: RegisterDomain> PointerDomain<T> { ...@@ -84,7 +84,7 @@ impl<T: RegisterDomain> PointerDomain<T> {
offset_adjustment: &T, offset_adjustment: &T,
) { ) {
if let Some(old_offset) = self.0.get(&old_id) { if let Some(old_offset) = self.0.get(&old_id) {
let new_offset = old_offset.bin_op(BinOpType::PLUS, offset_adjustment); let new_offset = old_offset.bin_op(BinOpType::IntAdd, offset_adjustment);
self.0.remove(old_id); self.0.remove(old_id);
self.0.insert(new_id.clone(), new_offset); self.0.insert(new_id.clone(), new_offset);
} }
...@@ -94,7 +94,7 @@ impl<T: RegisterDomain> PointerDomain<T> { ...@@ -94,7 +94,7 @@ impl<T: RegisterDomain> PointerDomain<T> {
pub fn add_to_offset(&self, value: &T) -> PointerDomain<T> { pub fn add_to_offset(&self, value: &T) -> PointerDomain<T> {
let mut result = self.clone(); let mut result = self.clone();
for offset in result.0.values_mut() { for offset in result.0.values_mut() {
*offset = offset.bin_op(BinOpType::PLUS, value); *offset = offset.bin_op(BinOpType::IntAdd, value);
} }
result result
} }
...@@ -103,7 +103,7 @@ impl<T: RegisterDomain> PointerDomain<T> { ...@@ -103,7 +103,7 @@ impl<T: RegisterDomain> PointerDomain<T> {
pub fn sub_from_offset(&self, value: &T) -> PointerDomain<T> { pub fn sub_from_offset(&self, value: &T) -> PointerDomain<T> {
let mut result = self.clone(); let mut result = self.clone();
for offset in result.0.values_mut() { for offset in result.0.values_mut() {
*offset = offset.bin_op(BinOpType::MINUS, value); *offset = offset.bin_op(BinOpType::IntSub, value);
} }
result result
} }
...@@ -149,7 +149,7 @@ mod tests { ...@@ -149,7 +149,7 @@ mod tests {
fn new_id(name: &str) -> AbstractIdentifier { fn new_id(name: &str) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new("time0"), Tid::new("time0"),
AbstractLocation::Register(name.into(), 64), AbstractLocation::Register(name.into(), ByteSize::new(8)),
) )
} }
......
...@@ -39,8 +39,8 @@ ...@@ -39,8 +39,8 @@
//! The artificial *CallReturn* nodes enable enriching the information flowing through a return edge //! The artificial *CallReturn* nodes enable enriching the information flowing through a return edge
//! with information recovered from the corresponding callsite during a fixpoint computation. //! with information recovered from the corresponding callsite during a fixpoint computation.
use crate::intermediate_representation::*;
use crate::prelude::*; use crate::prelude::*;
use crate::term::*;
use petgraph::graph::{DiGraph, NodeIndex}; use petgraph::graph::{DiGraph, NodeIndex};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
...@@ -166,19 +166,22 @@ impl<'a> GraphBuilder<'a> { ...@@ -166,19 +166,22 @@ impl<'a> GraphBuilder<'a> {
jump: &'a Term<Jmp>, jump: &'a Term<Jmp>,
untaken_conditional: Option<&'a Term<Jmp>>, untaken_conditional: Option<&'a Term<Jmp>>,
) { ) {
match &jump.term.kind { match &jump.term {
JmpKind::Goto(Label::Direct(tid)) => { Jmp::Branch(tid)
| Jmp::CBranch {
target: tid,
condition: _,
} => {
self.graph.add_edge( self.graph.add_edge(
source, source,
self.jump_targets[&tid].0, self.jump_targets[&tid].0,
Edge::Jump(jump, untaken_conditional), Edge::Jump(jump, untaken_conditional),
); );
} }
JmpKind::Goto(Label::Indirect(_)) => (), // TODO: add handling of indirect edges! Jmp::BranchInd(_) => (), // TODO: add handling of indirect edges!
JmpKind::Call(ref call) => { Jmp::Call { target, return_ } => {
if let Label::Direct(ref target_tid) = call.target { if self.extern_subs.contains(target) {
if self.extern_subs.contains(target_tid) { if let Some(return_tid) = return_ {
if let Some(Label::Direct(ref return_tid)) = call.return_ {
self.graph.add_edge( self.graph.add_edge(
source, source,
self.jump_targets[&return_tid].0, self.jump_targets[&return_tid].0,
...@@ -186,27 +189,32 @@ impl<'a> GraphBuilder<'a> { ...@@ -186,27 +189,32 @@ impl<'a> GraphBuilder<'a> {
); );
} }
} else { } else {
if let Some(target) = self.jump_targets.get(&target_tid) { if let Some(target_node) = self.jump_targets.get(&target) {
self.graph.add_edge(source, target.0, Edge::Call(jump)); self.graph.add_edge(source, target_node.0, Edge::Call(jump));
} } // TODO: Log message for the else-case?
if let Some(ref return_tid) = return_ {
if let Some(Label::Direct(ref return_tid)) = call.return_ {
let return_index = self.jump_targets[return_tid].0; let return_index = self.jump_targets[return_tid].0;
self.return_addresses self.return_addresses
.entry(target_tid.clone()) .entry(target.clone())
.and_modify(|vec| vec.push((source, return_index))) .and_modify(|vec| vec.push((source, return_index)))
.or_insert_with(|| vec![(source, return_index)]); .or_insert_with(|| vec![(source, return_index)]);
} }
// TODO: Non-returning calls and tail calls both have no return target in BAP.
// Thus we need to distinguish them somehow to correctly handle tail calls.
} }
} }
Jmp::CallInd {
target: _,
return_: _,
} => {
// TODO: add handling of indirect calls!
}
Jmp::CallOther {
description: _,
return_: _,
} => {
// TODO: Decide how to represent CallOther edges.
// Right now they are dead ends in the control flow graph.
} }
JmpKind::Interrupt { Jmp::Return(_) => {} // return edges are handled in a different function
value: _,
return_addr: _,
} => (), // TODO: Add some handling for interrupts
JmpKind::Return(_) => {} // return edges are handled in a different function
} }
} }
...@@ -244,7 +252,7 @@ impl<'a> GraphBuilder<'a> { ...@@ -244,7 +252,7 @@ impl<'a> GraphBuilder<'a> {
.term .term
.jmps .jmps
.iter() .iter()
.find(|jump| matches!(jump.term.kind, JmpKind::Call(_))) .find(|jump| matches!(jump.term, Jmp::Call{..}))
.unwrap(); .unwrap();
let cr_combine_node = self.graph.add_node(Node::CallReturn { let cr_combine_node = self.graph.add_node(Node::CallReturn {
call: call_block, call: call_block,
...@@ -267,7 +275,7 @@ impl<'a> GraphBuilder<'a> { ...@@ -267,7 +275,7 @@ impl<'a> GraphBuilder<'a> {
.term .term
.jmps .jmps
.iter() .iter()
.any(|jmp| matches!(jmp.term.kind, JmpKind::Return(_))) .any(|jmp| matches!(jmp.term, Jmp::Return(_)))
{ {
let return_from_node = self.jump_targets[&block.tid].1; let return_from_node = self.jump_targets[&block.tid].1;
self.add_call_return_node_and_edges(sub, return_from_node); self.add_call_return_node_and_edges(sub, return_from_node);
...@@ -326,29 +334,18 @@ mod tests { ...@@ -326,29 +334,18 @@ mod tests {
use super::*; use super::*;
fn mock_program() -> Term<Program> { fn mock_program() -> Term<Program> {
use Label::*;
let call = Call {
target: Direct(Tid::new("sub2")),
return_: Some(Direct(Tid::new("sub1_blk2"))),
};
let call_term = Term { let call_term = Term {
tid: Tid::new("call".to_string()), tid: Tid::new("call".to_string()),
term: Jmp { term: Jmp::Call {
condition: None, target: Tid::new("sub2"),
kind: JmpKind::Call(call), return_: Some(Tid::new("sub1_blk2")),
}, },
}; };
let return_term = Term { let return_term = Term {
tid: Tid::new("return".to_string()), tid: Tid::new("return".to_string()),
term: Jmp { term: Jmp::Return(Expression::Const(Bitvector::zero(64.into()))), // The return term does not matter
condition: None,
kind: JmpKind::Return(Direct(Tid::new("sub1_blk2"))),
},
};
let jmp = Jmp {
condition: None,
kind: JmpKind::Goto(Direct(Tid::new("sub1_blk1"))),
}; };
let jmp = Jmp::Branch(Tid::new("sub1_blk1"));
let jmp_term = Term { let jmp_term = Term {
tid: Tid::new("jump"), tid: Tid::new("jump"),
term: jmp, term: jmp,
......
...@@ -13,9 +13,8 @@ ...@@ -13,9 +13,8 @@
use super::fixpoint::Context as GeneralFPContext; use super::fixpoint::Context as GeneralFPContext;
use super::graph::*; use super::graph::*;
use crate::bil::Expression; use crate::intermediate_representation::*;
use crate::prelude::*; use crate::prelude::*;
use crate::term::*;
use fnv::FnvHashMap; use fnv::FnvHashMap;
use petgraph::graph::{EdgeIndex, NodeIndex}; use petgraph::graph::{EdgeIndex, NodeIndex};
use std::marker::PhantomData; use std::marker::PhantomData;
......
use super::object::ObjectType; use super::object::ObjectType;
use crate::abstract_domain::*; use crate::abstract_domain::*;
use crate::analysis::graph::Graph; use crate::analysis::graph::Graph;
use crate::bil::Expression; use crate::intermediate_representation::*;
use crate::prelude::*; use crate::prelude::*;
use crate::term::symbol::ExternSymbol;
use crate::term::*;
use crate::utils::log::*; use crate::utils::log::*;
use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::collections::{BTreeMap, BTreeSet, HashSet};
...@@ -84,10 +82,10 @@ impl<'a> Context<'a> { ...@@ -84,10 +82,10 @@ impl<'a> Context<'a> {
return_term: &Term<Jmp>, return_term: &Term<Jmp>,
) { ) {
let expected_stack_pointer_offset = match self.project.cpu_architecture.as_str() { let expected_stack_pointer_offset = match self.project.cpu_architecture.as_str() {
"x86" | "x86_64" => Bitvector::from_u16(self.project.get_pointer_bitsize() / 8) "x86" | "x86_64" => Bitvector::from_u64(u64::from(self.project.get_pointer_bytesize()))
.into_zero_extend(self.project.get_pointer_bitsize() as usize) .into_truncate(apint::BitWidth::from(self.project.get_pointer_bytesize()))
.unwrap(), .unwrap(),
_ => Bitvector::zero((self.project.get_pointer_bitsize() as usize).into()), _ => Bitvector::zero(apint::BitWidth::from(self.project.get_pointer_bytesize())),
}; };
match state_before_return.get_register(&self.project.stack_pointer_register) { match state_before_return.get_register(&self.project.stack_pointer_register) {
Ok(Data::Pointer(pointer)) => { Ok(Data::Pointer(pointer)) => {
...@@ -134,21 +132,18 @@ impl<'a> Context<'a> { ...@@ -134,21 +132,18 @@ impl<'a> Context<'a> {
call.tid.clone(), call.tid.clone(),
AbstractLocation::from_var(return_register).unwrap(), AbstractLocation::from_var(return_register).unwrap(),
); );
let address_bitsize = self.project.stack_pointer_register.bitsize().unwrap(); let address_bytesize = self.project.get_pointer_bytesize();
state.memory.add_abstract_object( state.memory.add_abstract_object(
object_id.clone(), object_id.clone(),
Bitvector::zero((address_bitsize as usize).into()).into(), Bitvector::zero(apint::BitWidth::from(address_bytesize)).into(),
super::object::ObjectType::Heap, super::object::ObjectType::Heap,
address_bitsize, address_bytesize,
); );
let pointer = PointerDomain::new( let pointer = PointerDomain::new(
object_id, object_id,
Bitvector::zero((address_bitsize as usize).into()).into(), Bitvector::zero(apint::BitWidth::from(address_bytesize)).into(),
);
self.log_debug(
state.set_register(return_register, pointer.into()),
Some(&call.tid),
); );
state.set_register(return_register, pointer.into());
Some(state) Some(state)
} }
Err(err) => { Err(err) => {
...@@ -159,6 +154,27 @@ impl<'a> Context<'a> { ...@@ -159,6 +154,27 @@ impl<'a> Context<'a> {
} }
} }
/// Evaluate the value of a parameter of an extern symbol for the given state.
fn eval_parameter_arg(&self, state: &State, parameter: &Arg) -> Result<Data, Error> {
match parameter {
Arg::Register(var) => state.eval(&Expression::Var(var.clone())),
Arg::Stack { offset, size } => state.load_value(
&Expression::BinOp {
op: BinOpType::IntAdd,
lhs: Box::new(Expression::Var(self.project.stack_pointer_register.clone())),
rhs: Box::new(Expression::Const(
Bitvector::from_i64(*offset)
.into_truncate(apint::BitWidth::from(
self.project.get_pointer_bytesize(),
))
.unwrap(),
)),
},
*size,
),
}
}
/// Mark the object that the parameter of a call is pointing to as freed. /// Mark the object that the parameter of a call is pointing to as freed.
/// If the object may have been already freed, generate a CWE warning. /// If the object may have been already freed, generate a CWE warning.
/// This models the behaviour of `free` and similar functions. /// This models the behaviour of `free` and similar functions.
...@@ -170,7 +186,9 @@ impl<'a> Context<'a> { ...@@ -170,7 +186,9 @@ impl<'a> Context<'a> {
extern_symbol: &ExternSymbol, extern_symbol: &ExternSymbol,
) -> Option<State> { ) -> Option<State> {
match extern_symbol.get_unique_parameter() { match extern_symbol.get_unique_parameter() {
Ok(parameter_expression) => match state.eval(parameter_expression) { Ok(parameter) => {
let parameter_value = self.eval_parameter_arg(state, parameter);
match parameter_value {
Ok(memory_object_pointer) => { Ok(memory_object_pointer) => {
if let Data::Pointer(pointer) = memory_object_pointer { if let Data::Pointer(pointer) = memory_object_pointer {
if let Err(possible_double_frees) = if let Err(possible_double_frees) =
...@@ -206,7 +224,8 @@ impl<'a> Context<'a> { ...@@ -206,7 +224,8 @@ impl<'a> Context<'a> {
self.log_debug(Err(err), Some(&call.tid)); self.log_debug(Err(err), Some(&call.tid));
Some(new_state) Some(new_state)
} }
}, }
}
Err(err) => { Err(err) => {
// We do not know which memory object to free // We do not know which memory object to free
self.log_debug(Err(err), Some(&call.tid)); self.log_debug(Err(err), Some(&call.tid));
...@@ -222,12 +241,8 @@ impl<'a> Context<'a> { ...@@ -222,12 +241,8 @@ impl<'a> Context<'a> {
call: &Term<Jmp>, call: &Term<Jmp>,
extern_symbol: &ExternSymbol, extern_symbol: &ExternSymbol,
) { ) {
for argument in extern_symbol for parameter in extern_symbol.parameters.iter() {
.arguments match self.eval_parameter_arg(state, parameter) {
.iter()
.filter(|arg| arg.intent.is_input())
{
match state.eval(&argument.location) {
Ok(value) => { Ok(value) => {
if state.memory.is_dangling_pointer(&value, true) { if state.memory.is_dangling_pointer(&value, true) {
let warning = CweWarning { let warning = CweWarning {
...@@ -247,8 +262,8 @@ impl<'a> Context<'a> { ...@@ -247,8 +262,8 @@ impl<'a> Context<'a> {
} }
Err(err) => self.log_debug( Err(err) => self.log_debug(
Err(err.context(format!( Err(err.context(format!(
"Function argument expression {:?} could not be evaluated", "Function parameter {:?} could not be evaluated",
argument.location parameter
))), ))),
Some(&call.tid), Some(&call.tid),
), ),
...@@ -266,11 +281,11 @@ impl<'a> Context<'a> { ...@@ -266,11 +281,11 @@ impl<'a> Context<'a> {
extern_symbol: &ExternSymbol, extern_symbol: &ExternSymbol,
) -> Option<State> { ) -> Option<State> {
self.log_debug( self.log_debug(
new_state.clear_stack_parameter(extern_symbol), new_state.clear_stack_parameter(extern_symbol, &self.project.stack_pointer_register),
Some(&call.tid), Some(&call.tid),
); );
let mut possible_referenced_ids = BTreeSet::new(); let mut possible_referenced_ids = BTreeSet::new();
if extern_symbol.arguments.is_empty() { if extern_symbol.parameters.is_empty() && extern_symbol.return_values.is_empty() {
// We assume here that we do not know the parameters and approximate them by all possible parameter registers. // We assume here that we do not know the parameters and approximate them by all possible parameter registers.
// This approximation is wrong if the function is known but has neither parameters nor return values. // This approximation is wrong if the function is known but has neither parameters nor return values.
// We cannot distinguish these two cases yet. // We cannot distinguish these two cases yet.
...@@ -280,12 +295,8 @@ impl<'a> Context<'a> { ...@@ -280,12 +295,8 @@ impl<'a> Context<'a> {
} }
} }
} else { } else {
for parameter in extern_symbol for parameter in extern_symbol.parameters.iter() {
.arguments if let Ok(data) = self.eval_parameter_arg(state, parameter) {
.iter()
.filter(|arg| arg.intent.is_input())
{
if let Ok(data) = state.eval(&parameter.location) {
possible_referenced_ids.append(&mut data.referenced_ids()); possible_referenced_ids.append(&mut data.referenced_ids());
} }
} }
...@@ -312,13 +323,13 @@ impl<'a> Context<'a> { ...@@ -312,13 +323,13 @@ impl<'a> Context<'a> {
if *stack_id == state.stack_id { if *stack_id == state.stack_id {
stack_offset_domain.clone() stack_offset_domain.clone()
} else { } else {
BitvectorDomain::new_top(stack_pointer.bitsize()) BitvectorDomain::new_top(stack_pointer.bytesize())
} }
} else { } else {
BitvectorDomain::new_top(self.project.stack_pointer_register.bitsize().unwrap()) BitvectorDomain::new_top(self.project.stack_pointer_register.size)
} }
} else { } else {
BitvectorDomain::new_top(self.project.stack_pointer_register.bitsize().unwrap()) BitvectorDomain::new_top(self.project.stack_pointer_register.size)
} }
} }
} }
......
use super::*; use super::*;
use crate::bil::variable::*;
fn bv(value: i64) -> BitvectorDomain { fn bv(value: i64) -> BitvectorDomain {
BitvectorDomain::Value(Bitvector::from_i64(value)) BitvectorDomain::Value(Bitvector::from_i64(value))
...@@ -8,59 +7,51 @@ fn bv(value: i64) -> BitvectorDomain { ...@@ -8,59 +7,51 @@ fn bv(value: i64) -> BitvectorDomain {
fn new_id(time: &str, reg_name: &str) -> AbstractIdentifier { fn new_id(time: &str, reg_name: &str) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new(time), Tid::new(time),
AbstractLocation::Register(reg_name.to_string(), 64), AbstractLocation::Register(reg_name.to_string(), ByteSize::new(8)),
) )
} }
fn mock_extern_symbol(name: &str) -> ExternSymbol { fn mock_extern_symbol(name: &str) -> ExternSymbol {
use crate::bil; let arg = Arg::Register(register("RAX"));
let arg = Arg {
var: register("RAX"),
location: bil::Expression::Var(register("RAX")),
intent: ArgIntent::Both,
};
ExternSymbol { ExternSymbol {
tid: Tid::new("extern_".to_string() + name), tid: Tid::new("extern_".to_string() + name),
address: "somewhere".into(),
name: name.into(), name: name.into(),
calling_convention: None, calling_convention: None,
arguments: vec![arg], parameters: vec![arg.clone()],
return_values: vec![arg],
no_return: false,
} }
} }
fn register(name: &str) -> Variable { fn register(name: &str) -> Variable {
Variable { Variable {
name: name.into(), name: name.into(),
type_: crate::bil::variable::Type::Immediate(64), size: ByteSize::new(8),
is_temp: false, is_temp: false,
} }
} }
fn reg_add_term(name: &str, value: i64, tid_name: &str) -> Term<Def> { fn reg_add_term(name: &str, value: i64, tid_name: &str) -> Term<Def> {
let add_expr = Expression::BinOp { let add_expr = Expression::BinOp {
op: crate::bil::BinOpType::PLUS, op: BinOpType::IntAdd,
lhs: Box::new(Expression::Var(register(name))), lhs: Box::new(Expression::Var(register(name))),
rhs: Box::new(Expression::Const(Bitvector::from_i64(value))), rhs: Box::new(Expression::Const(Bitvector::from_i64(value))),
}; };
Term { Term {
tid: Tid::new(format!("{}", tid_name)), tid: Tid::new(format!("{}", tid_name)),
term: Def { term: Def::Assign {
lhs: register(name), var: register(name),
rhs: add_expr, value: add_expr,
}, },
} }
} }
fn call_term(target_name: &str) -> Term<Jmp> { fn call_term(target_name: &str) -> Term<Jmp> {
let call = Call {
target: Label::Direct(Tid::new(target_name)),
return_: None,
};
Term { Term {
tid: Tid::new(format!("call_{}", target_name)), tid: Tid::new(format!("call_{}", target_name)),
term: Jmp { term: Jmp::Call {
condition: None, target: Tid::new(target_name),
kind: JmpKind::Call(call), return_: None,
}, },
} }
} }
...@@ -68,10 +59,10 @@ fn call_term(target_name: &str) -> Term<Jmp> { ...@@ -68,10 +59,10 @@ fn call_term(target_name: &str) -> Term<Jmp> {
fn return_term(target_name: &str) -> Term<Jmp> { fn return_term(target_name: &str) -> Term<Jmp> {
Term { Term {
tid: Tid::new(format!("return")), tid: Tid::new(format!("return")),
term: Jmp { term: Jmp::Return(Expression::Unknown {
condition: None, description: target_name.into(),
kind: JmpKind::Return(Label::Direct(Tid::new(target_name))), size: ByteSize::new(8),
}, }),
} }
} }
...@@ -102,7 +93,6 @@ fn mock_project() -> Project { ...@@ -102,7 +93,6 @@ fn mock_project() -> Project {
fn context_problem_implementation() { fn context_problem_implementation() {
use crate::analysis::interprocedural_fixpoint::Context as IpFpContext; use crate::analysis::interprocedural_fixpoint::Context as IpFpContext;
use crate::analysis::pointer_inference::Data; use crate::analysis::pointer_inference::Data;
use crate::bil::*;
use Expression::*; use Expression::*;
let project = mock_project(); let project = mock_project();
...@@ -113,10 +103,10 @@ fn context_problem_implementation() { ...@@ -113,10 +103,10 @@ fn context_problem_implementation() {
let def = Term { let def = Term {
tid: Tid::new("def"), tid: Tid::new("def"),
term: Def { term: Def::Assign {
lhs: register("RSP"), var: register("RSP"),
rhs: BinOp { value: BinOp {
op: BinOpType::PLUS, op: BinOpType::IntAdd,
lhs: Box::new(Var(register("RSP"))), lhs: Box::new(Var(register("RSP"))),
rhs: Box::new(Const(Bitvector::from_i64(-16))), rhs: Box::new(Const(Bitvector::from_i64(-16))),
}, },
...@@ -124,15 +114,9 @@ fn context_problem_implementation() { ...@@ -124,15 +114,9 @@ fn context_problem_implementation() {
}; };
let store_term = Term { let store_term = Term {
tid: Tid::new("store"), tid: Tid::new("store"),
term: Def { term: Def::Store {
lhs: register("memory"), // technically false, but not checked at the moment address: Var(register("RSP")),
rhs: Store { value: Const(Bitvector::from_i64(42)),
address: Box::new(Var(register("RSP"))),
endian: Endianness::LittleEndian,
memory: Box::new(Var(register("memory"))), // This is technically false, but the field is ignored at the moment
value: Box::new(Const(Bitvector::from_i64(42))),
size: 64,
},
}, },
}; };
...@@ -183,12 +167,8 @@ fn context_problem_implementation() { ...@@ -183,12 +167,8 @@ fn context_problem_implementation() {
state.get_register(&register("RSP")).unwrap() state.get_register(&register("RSP")).unwrap()
); );
state state.set_register(&register("callee_saved_reg"), Data::Value(bv(13)));
.set_register(&register("callee_saved_reg"), Data::Value(bv(13))) state.set_register(&register("other_reg"), Data::Value(bv(14)));
.unwrap();
state
.set_register(&register("other_reg"), Data::Value(bv(14)))
.unwrap();
let malloc = call_term("extern_malloc"); let malloc = call_term("extern_malloc");
let mut state_after_malloc = context.update_call_stub(&state, &malloc).unwrap(); let mut state_after_malloc = context.update_call_stub(&state, &malloc).unwrap();
...@@ -205,7 +185,7 @@ fn context_problem_implementation() { ...@@ -205,7 +185,7 @@ fn context_problem_implementation() {
state state
.get_register(&register("RSP")) .get_register(&register("RSP"))
.unwrap() .unwrap()
.bin_op(BinOpType::PLUS, &Data::Value(bv(8))) .bin_op(BinOpType::IntAdd, &Data::Value(bv(8)))
); );
assert_eq!( assert_eq!(
state_after_malloc state_after_malloc
...@@ -218,15 +198,13 @@ fn context_problem_implementation() { ...@@ -218,15 +198,13 @@ fn context_problem_implementation() {
.unwrap() .unwrap()
.is_top()); .is_top());
state_after_malloc state_after_malloc.set_register(
.set_register(
&register("callee_saved_reg"), &register("callee_saved_reg"),
Data::Pointer(PointerDomain::new( Data::Pointer(PointerDomain::new(
new_id("call_extern_malloc", "RAX"), new_id("call_extern_malloc", "RAX"),
bv(0), bv(0),
)), )),
) );
.unwrap();
let free = call_term("extern_free"); let free = call_term("extern_free");
let state_after_free = context let state_after_free = context
.update_call_stub(&state_after_malloc, &free) .update_call_stub(&state_after_malloc, &free)
...@@ -254,7 +232,7 @@ fn context_problem_implementation() { ...@@ -254,7 +232,7 @@ fn context_problem_implementation() {
state state
.get_register(&register("RSP")) .get_register(&register("RSP"))
.unwrap() .unwrap()
.bin_op(BinOpType::PLUS, &Data::Value(bv(8))) .bin_op(BinOpType::IntAdd, &Data::Value(bv(8)))
); );
assert_eq!( assert_eq!(
state_after_other_fn state_after_other_fn
...@@ -290,7 +268,7 @@ fn update_return() { ...@@ -290,7 +268,7 @@ fn update_return() {
callsite_id.clone(), callsite_id.clone(),
bv(0).into(), bv(0).into(),
ObjectType::Stack, ObjectType::Stack,
64, ByteSize::new(8),
); );
state_before_return state_before_return
.caller_stack_ids .caller_stack_ids
...@@ -304,7 +282,7 @@ fn update_return() { ...@@ -304,7 +282,7 @@ fn update_return() {
other_callsite_id.clone(), other_callsite_id.clone(),
bv(0).into(), bv(0).into(),
ObjectType::Stack, ObjectType::Stack,
64, ByteSize::new(8),
); );
state_before_return state_before_return
.caller_stack_ids .caller_stack_ids
...@@ -312,15 +290,13 @@ fn update_return() { ...@@ -312,15 +290,13 @@ fn update_return() {
state_before_return state_before_return
.ids_known_to_caller .ids_known_to_caller
.insert(other_callsite_id.clone()); .insert(other_callsite_id.clone());
state_before_return state_before_return.set_register(
.set_register(
&register("RAX"), &register("RAX"),
Data::Pointer(PointerDomain::new( Data::Pointer(PointerDomain::new(
new_id("call_callee_other", "RSP"), new_id("call_callee_other", "RSP"),
bv(-32), bv(-32),
)), )),
) );
.unwrap();
let state_before_call = State::new(&register("RSP"), Tid::new("original_caller_id")); let state_before_call = State::new(&register("RSP"), Tid::new("original_caller_id"));
let mut state_before_call = context let mut state_before_call = context
...@@ -334,7 +310,7 @@ fn update_return() { ...@@ -334,7 +310,7 @@ fn update_return() {
caller_caller_id.clone(), caller_caller_id.clone(),
bv(0).into(), bv(0).into(),
ObjectType::Stack, ObjectType::Stack,
64, ByteSize::new(8),
); );
state_before_call state_before_call
.caller_stack_ids .caller_stack_ids
......
...@@ -14,8 +14,7 @@ ...@@ -14,8 +14,7 @@
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::prelude::*; use crate::intermediate_representation::*;
use crate::term::*;
use crate::utils::log::*; use crate::utils::log::*;
use petgraph::graph::NodeIndex; use petgraph::graph::NodeIndex;
use petgraph::visit::IntoNodeReferences; use petgraph::visit::IntoNodeReferences;
......
...@@ -22,8 +22,8 @@ impl DerefMut for AbstractObject { ...@@ -22,8 +22,8 @@ impl DerefMut for AbstractObject {
impl AbstractObject { impl AbstractObject {
/// Create a new abstract object with given object type and address bitsize. /// Create a new abstract object with given object type and address bitsize.
pub fn new(type_: ObjectType, address_bitsize: BitSize) -> AbstractObject { pub fn new(type_: ObjectType, address_bytesize: ByteSize) -> AbstractObject {
AbstractObject(Arc::new(AbstractObjectInfo::new(type_, address_bitsize))) AbstractObject(Arc::new(AbstractObjectInfo::new(type_, address_bytesize)))
} }
/// Short-circuits the `AbstractObjectInfo::merge` function if `self==other`. /// Short-circuits the `AbstractObjectInfo::merge` function if `self==other`.
...@@ -53,20 +53,19 @@ pub struct AbstractObjectInfo { ...@@ -53,20 +53,19 @@ pub struct AbstractObjectInfo {
impl AbstractObjectInfo { impl AbstractObjectInfo {
/// Create a new abstract object with known object type and address bitsize /// Create a new abstract object with known object type and address bitsize
pub fn new(type_: ObjectType, address_bitsize: BitSize) -> AbstractObjectInfo { pub fn new(type_: ObjectType, address_bytesize: ByteSize) -> AbstractObjectInfo {
AbstractObjectInfo { AbstractObjectInfo {
pointer_targets: BTreeSet::new(), pointer_targets: BTreeSet::new(),
is_unique: true, is_unique: true,
state: Some(ObjectState::Alive), state: Some(ObjectState::Alive),
type_: Some(type_), type_: Some(type_),
memory: MemRegion::new(address_bitsize), memory: MemRegion::new(address_bytesize),
} }
} }
/// Read the value at the given offset of the given size (in bits, not bytes) inside the memory region. /// Read the value at the given offset of the given size (in bits, not bytes) inside the memory region.
pub fn get_value(&self, offset: Bitvector, bitsize: BitSize) -> Data { pub fn get_value(&self, offset: Bitvector, bytesize: ByteSize) -> Data {
assert_eq!(bitsize % 8, 0); self.memory.get(offset, bytesize)
self.memory.get(offset, (bitsize / 8) as u64)
} }
/// Write a value at the given offset to the memory region. /// Write a value at the given offset to the memory region.
...@@ -83,12 +82,12 @@ impl AbstractObjectInfo { ...@@ -83,12 +82,12 @@ impl AbstractObjectInfo {
} else { } else {
let merged_value = self let merged_value = self
.memory .memory
.get(concrete_offset.clone(), (value.bitsize() / 8) as u64) .get(concrete_offset.clone(), value.bytesize())
.merge(&value); .merge(&value);
self.memory.add(merged_value, concrete_offset.clone()); self.memory.add(merged_value, concrete_offset.clone());
}; };
} else { } else {
self.memory = MemRegion::new(self.memory.get_address_bitsize()); self.memory = MemRegion::new(self.memory.get_address_bytesize());
} }
Ok(()) Ok(())
} }
...@@ -101,11 +100,11 @@ impl AbstractObjectInfo { ...@@ -101,11 +100,11 @@ impl AbstractObjectInfo {
if let BitvectorDomain::Value(ref concrete_offset) = offset { if let BitvectorDomain::Value(ref concrete_offset) = offset {
let merged_value = self let merged_value = self
.memory .memory
.get(concrete_offset.clone(), (value.bitsize() / 8) as u64) .get(concrete_offset.clone(), value.bytesize())
.merge(&value); .merge(&value);
self.memory.add(merged_value, concrete_offset.clone()); self.memory.add(merged_value, concrete_offset.clone());
} else { } else {
self.memory = MemRegion::new(self.memory.get_address_bitsize()); self.memory = MemRegion::new(self.memory.get_address_bytesize());
} }
} }
...@@ -164,7 +163,7 @@ impl AbstractObjectInfo { ...@@ -164,7 +163,7 @@ impl AbstractObjectInfo {
/// Represents the effect of unknown write instructions to the object /// Represents the effect of unknown write instructions to the object
/// which may include writing pointers to targets from the `additional_targets` set to the object. /// which may include writing pointers to targets from the `additional_targets` set to the object.
pub fn assume_arbitrary_writes(&mut self, additional_targets: &BTreeSet<AbstractIdentifier>) { pub fn assume_arbitrary_writes(&mut self, additional_targets: &BTreeSet<AbstractIdentifier>) {
self.memory = MemRegion::new(self.memory.get_address_bitsize()); self.memory = MemRegion::new(self.memory.get_address_bytesize());
self.pointer_targets self.pointer_targets
.extend(additional_targets.iter().cloned()); .extend(additional_targets.iter().cloned());
} }
...@@ -294,7 +293,7 @@ mod tests { ...@@ -294,7 +293,7 @@ mod tests {
is_unique: true, is_unique: true,
state: Some(ObjectState::Alive), state: Some(ObjectState::Alive),
type_: Some(ObjectType::Heap), type_: Some(ObjectType::Heap),
memory: MemRegion::new(64), memory: MemRegion::new(ByteSize::new(8)),
}; };
AbstractObject(Arc::new(obj_info)) AbstractObject(Arc::new(obj_info))
} }
...@@ -310,7 +309,7 @@ mod tests { ...@@ -310,7 +309,7 @@ mod tests {
fn new_id(tid: &str, reg_name: &str) -> AbstractIdentifier { fn new_id(tid: &str, reg_name: &str) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new(tid), Tid::new(tid),
AbstractLocation::Register(reg_name.into(), 64), AbstractLocation::Register(reg_name.into(), ByteSize::new(8)),
) )
} }
...@@ -321,19 +320,22 @@ mod tests { ...@@ -321,19 +320,22 @@ mod tests {
let offset = bv(-15); let offset = bv(-15);
object.set_value(three, &offset).unwrap(); object.set_value(three, &offset).unwrap();
assert_eq!( assert_eq!(
object.get_value(Bitvector::from_i64(-16), 64), object.get_value(Bitvector::from_i64(-16), ByteSize::new(8)),
Data::Top(64) Data::Top(ByteSize::new(8))
);
assert_eq!(
object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)),
new_data(3)
); );
assert_eq!(object.get_value(Bitvector::from_i64(-15), 64), new_data(3));
object.set_value(new_data(4), &bv(-12)).unwrap(); object.set_value(new_data(4), &bv(-12)).unwrap();
assert_eq!( assert_eq!(
object.get_value(Bitvector::from_i64(-15), 64), object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)),
Data::Top(64) Data::Top(ByteSize::new(8))
); );
object.merge_value(new_data(5), &bv(-12)); object.merge_value(new_data(5), &bv(-12));
assert_eq!( assert_eq!(
object.get_value(Bitvector::from_i64(-12), 64), object.get_value(Bitvector::from_i64(-12), ByteSize::new(8)),
Data::Value(BitvectorDomain::new_top(64)) Data::Value(BitvectorDomain::new_top(ByteSize::new(8)))
); );
let mut other_object = new_abstract_object(); let mut other_object = new_abstract_object();
...@@ -341,11 +343,11 @@ mod tests { ...@@ -341,11 +343,11 @@ mod tests {
other_object.set_value(new_data(0), &bv(0)).unwrap(); other_object.set_value(new_data(0), &bv(0)).unwrap();
let merged_object = object.merge(&other_object); let merged_object = object.merge(&other_object);
assert_eq!( assert_eq!(
merged_object.get_value(Bitvector::from_i64(-12), 64), merged_object.get_value(Bitvector::from_i64(-12), ByteSize::new(8)),
Data::Top(64) Data::Top(ByteSize::new(8))
); );
assert_eq!( assert_eq!(
merged_object.get_value(Bitvector::from_i64(0), 64), merged_object.get_value(Bitvector::from_i64(0), ByteSize::new(8)),
new_data(0) new_data(0)
); );
} }
...@@ -370,7 +372,7 @@ mod tests { ...@@ -370,7 +372,7 @@ mod tests {
target_map.remove(&new_id("time_1", "RAX")); target_map.remove(&new_id("time_1", "RAX"));
let modified_pointer = PointerDomain::with_targets(target_map); let modified_pointer = PointerDomain::with_targets(target_map);
assert_eq!( assert_eq!(
object.get_value(Bitvector::from_i64(-15), 64), object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)),
modified_pointer.into() modified_pointer.into()
); );
...@@ -384,7 +386,7 @@ mod tests { ...@@ -384,7 +386,7 @@ mod tests {
target_map.insert(new_id("time_234", "RBX"), bv(50)); target_map.insert(new_id("time_234", "RBX"), bv(50));
let modified_pointer = PointerDomain::with_targets(target_map); let modified_pointer = PointerDomain::with_targets(target_map);
assert_eq!( assert_eq!(
object.get_value(Bitvector::from_i64(-15), 64), object.get_value(Bitvector::from_i64(-15), ByteSize::new(8)),
modified_pointer.into() modified_pointer.into()
); );
} }
......
...@@ -22,15 +22,15 @@ impl AbstractObjectList { ...@@ -22,15 +22,15 @@ impl AbstractObjectList {
/// The offset into the stack object will be set to zero. /// The offset into the stack object will be set to zero.
pub fn from_stack_id( pub fn from_stack_id(
stack_id: AbstractIdentifier, stack_id: AbstractIdentifier,
address_bitsize: BitSize, address_bytesize: ByteSize,
) -> AbstractObjectList { ) -> AbstractObjectList {
let mut objects = BTreeMap::new(); let mut objects = BTreeMap::new();
let stack_object = AbstractObject::new(ObjectType::Stack, address_bitsize); let stack_object = AbstractObject::new(ObjectType::Stack, address_bytesize);
objects.insert( objects.insert(
stack_id, stack_id,
( (
stack_object, stack_object,
Bitvector::zero((address_bitsize as usize).into()).into(), Bitvector::zero(apint::BitWidth::from(address_bytesize)).into(),
), ),
); );
AbstractObjectList { objects } AbstractObjectList { objects }
...@@ -68,7 +68,7 @@ impl AbstractObjectList { ...@@ -68,7 +68,7 @@ impl AbstractObjectList {
/// If the address is not unique, merge the value of all possible addresses. /// If the address is not unique, merge the value of all possible addresses.
/// ///
/// Returns an error if the address is a `Data::Value`, i.e. not a pointer. /// Returns an error if the address is a `Data::Value`, i.e. not a pointer.
pub fn get_value(&self, address: &Data, size: BitSize) -> Result<Data, Error> { pub fn get_value(&self, address: &Data, size: ByteSize) -> Result<Data, Error> {
match address { match address {
Data::Value(value) => Err(anyhow!("Load from non-pointer value:\n{:?}", value)), Data::Value(value) => Err(anyhow!("Load from non-pointer value:\n{:?}", value)),
Data::Top(_) => Ok(Data::new_top(size)), Data::Top(_) => Ok(Data::new_top(size)),
...@@ -158,9 +158,9 @@ impl AbstractObjectList { ...@@ -158,9 +158,9 @@ impl AbstractObjectList {
object_id: AbstractIdentifier, object_id: AbstractIdentifier,
initial_offset: BitvectorDomain, initial_offset: BitvectorDomain,
type_: ObjectType, type_: ObjectType,
address_bitsize: BitSize, address_bytesize: ByteSize,
) { ) {
let new_object = AbstractObject::new(type_, address_bitsize); let new_object = AbstractObject::new(type_, address_bytesize);
if let Some((object, offset)) = self.objects.get_mut(&object_id) { if let Some((object, offset)) = self.objects.get_mut(&object_id) {
// If the identifier already exists, we have to assume that more than one object may be referenced by this identifier. // If the identifier already exists, we have to assume that more than one object may be referenced by this identifier.
object.is_unique = false; object.is_unique = false;
...@@ -333,13 +333,14 @@ mod tests { ...@@ -333,13 +333,14 @@ mod tests {
fn new_id(name: &str) -> AbstractIdentifier { fn new_id(name: &str) -> AbstractIdentifier {
AbstractIdentifier::new( AbstractIdentifier::new(
Tid::new("time0"), Tid::new("time0"),
AbstractLocation::Register(name.into(), 64), AbstractLocation::Register(name.into(), ByteSize::new(8)),
) )
} }
#[test] #[test]
fn abstract_object_list() { fn abstract_object_list() {
let mut obj_list = AbstractObjectList::from_stack_id(new_id("RSP".into()), 64); let mut obj_list =
AbstractObjectList::from_stack_id(new_id("RSP".into()), ByteSize::new(8));
assert_eq!(obj_list.objects.len(), 1); assert_eq!(obj_list.objects.len(), 1);
assert_eq!(obj_list.objects.values().next().unwrap().1, bv(0)); assert_eq!(obj_list.objects.values().next().unwrap().1, bv(0));
...@@ -349,12 +350,13 @@ mod tests { ...@@ -349,12 +350,13 @@ mod tests {
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
obj_list obj_list
.get_value(&Data::Pointer(pointer.clone()), 64) .get_value(&Data::Pointer(pointer.clone()), ByteSize::new(8))
.unwrap(), .unwrap(),
Data::Value(bv(42)) Data::Value(bv(42))
); );
let mut other_obj_list = AbstractObjectList::from_stack_id(new_id("RSP".into()), 64); let mut other_obj_list =
AbstractObjectList::from_stack_id(new_id("RSP".into()), ByteSize::new(8));
let second_pointer = PointerDomain::new(new_id("RSP".into()), bv(-8)); let second_pointer = PointerDomain::new(new_id("RSP".into()), bv(-8));
other_obj_list other_obj_list
.set_value(pointer.clone(), Data::Value(bv(42))) .set_value(pointer.clone(), Data::Value(bv(42)))
...@@ -364,12 +366,17 @@ mod tests { ...@@ -364,12 +366,17 @@ mod tests {
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
other_obj_list other_obj_list
.get_value(&Data::Pointer(second_pointer.clone()), 64) .get_value(&Data::Pointer(second_pointer.clone()), ByteSize::new(8))
.unwrap(), .unwrap(),
Data::Value(bv(35)) Data::Value(bv(35))
); );
other_obj_list.add_abstract_object(new_id("RAX".into()), bv(0), ObjectType::Heap, 64); other_obj_list.add_abstract_object(
new_id("RAX".into()),
bv(0),
ObjectType::Heap,
ByteSize::new(8),
);
let heap_pointer = PointerDomain::new(new_id("RAX".into()), bv(8)); let heap_pointer = PointerDomain::new(new_id("RAX".into()), bv(8));
other_obj_list other_obj_list
.set_value(heap_pointer.clone(), Data::Value(bv(3))) .set_value(heap_pointer.clone(), Data::Value(bv(3)))
...@@ -378,19 +385,19 @@ mod tests { ...@@ -378,19 +385,19 @@ mod tests {
let mut merged = obj_list.merge(&other_obj_list); let mut merged = obj_list.merge(&other_obj_list);
assert_eq!( assert_eq!(
merged merged
.get_value(&Data::Pointer(pointer.clone()), 64) .get_value(&Data::Pointer(pointer.clone()), ByteSize::new(8))
.unwrap(), .unwrap(),
Data::Value(bv(42)) Data::Value(bv(42))
); );
assert_eq!( assert_eq!(
merged merged
.get_value(&Data::Pointer(second_pointer.clone()), 64) .get_value(&Data::Pointer(second_pointer.clone()), ByteSize::new(8))
.unwrap(), .unwrap(),
Data::new_top(64) Data::new_top(ByteSize::new(8))
); );
assert_eq!( assert_eq!(
merged merged
.get_value(&Data::Pointer(heap_pointer.clone()), 64) .get_value(&Data::Pointer(heap_pointer.clone()), ByteSize::new(8))
.unwrap(), .unwrap(),
Data::Value(bv(3)) Data::Value(bv(3))
); );
...@@ -401,13 +408,13 @@ mod tests { ...@@ -401,13 +408,13 @@ mod tests {
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
merged merged
.get_value(&Data::Pointer(pointer.clone()), 64) .get_value(&Data::Pointer(pointer.clone()), ByteSize::new(8))
.unwrap(), .unwrap(),
Data::Value(BitvectorDomain::new_top(64)) Data::Value(BitvectorDomain::new_top(ByteSize::new(8)))
); );
assert_eq!( assert_eq!(
merged merged
.get_value(&Data::Pointer(heap_pointer.clone()), 64) .get_value(&Data::Pointer(heap_pointer.clone()), ByteSize::new(8))
.unwrap(), .unwrap(),
Data::Value(bv(3)) Data::Value(bv(3))
); );
...@@ -435,7 +442,7 @@ mod tests { ...@@ -435,7 +442,7 @@ mod tests {
other_obj_list.replace_abstract_id(&new_id("RAX".into()), &new_id("ID2".into()), &bv(0)); other_obj_list.replace_abstract_id(&new_id("RAX".into()), &new_id("ID2".into()), &bv(0));
assert_eq!( assert_eq!(
other_obj_list other_obj_list
.get_value(&Data::Pointer(pointer.clone()), 64) .get_value(&Data::Pointer(pointer.clone()), ByteSize::new(8))
.unwrap(), .unwrap(),
Data::Pointer(modified_heap_pointer.clone()) Data::Pointer(modified_heap_pointer.clone())
); );
...@@ -481,10 +488,16 @@ mod tests { ...@@ -481,10 +488,16 @@ mod tests {
#[test] #[test]
fn append_unknown_objects_test() { fn append_unknown_objects_test() {
let mut obj_list = AbstractObjectList::from_stack_id(new_id("stack"), 64); let mut obj_list = AbstractObjectList::from_stack_id(new_id("stack"), ByteSize::new(8));
let mut other_obj_list = AbstractObjectList::from_stack_id(new_id("stack"), 64); let mut other_obj_list =
other_obj_list.add_abstract_object(new_id("heap_obj"), bv(0).into(), ObjectType::Heap, 64); AbstractObjectList::from_stack_id(new_id("stack"), ByteSize::new(8));
other_obj_list.add_abstract_object(
new_id("heap_obj"),
bv(0).into(),
ObjectType::Heap,
ByteSize::new(8),
);
obj_list.append_unknown_objects(&other_obj_list); obj_list.append_unknown_objects(&other_obj_list);
assert_eq!(obj_list.objects.len(), 2); assert_eq!(obj_list.objects.len(), 2);
......
use super::object_list::AbstractObjectList; use super::object_list::AbstractObjectList;
use super::Data; use super::Data;
use crate::abstract_domain::*; use crate::abstract_domain::*;
use crate::bil::*; use crate::intermediate_representation::*;
use crate::prelude::*; use crate::prelude::*;
use crate::term::symbol::ExternSymbol;
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
mod access_handling; mod access_handling;
...@@ -50,16 +49,13 @@ impl State { ...@@ -50,16 +49,13 @@ impl State {
stack_register.clone(), stack_register.clone(),
PointerDomain::new( PointerDomain::new(
stack_id.clone(), stack_id.clone(),
Bitvector::zero((stack_register.bitsize().unwrap() as usize).into()).into(), Bitvector::zero(apint::BitWidth::from(stack_register.size)).into(),
) )
.into(), .into(),
); );
State { State {
register, register,
memory: AbstractObjectList::from_stack_id( memory: AbstractObjectList::from_stack_id(stack_id.clone(), stack_register.size),
stack_id.clone(),
stack_register.bitsize().unwrap(),
),
stack_id, stack_id,
caller_stack_ids: BTreeSet::new(), caller_stack_ids: BTreeSet::new(),
ids_known_to_caller: BTreeSet::new(), ids_known_to_caller: BTreeSet::new(),
...@@ -89,18 +85,27 @@ impl State { ...@@ -89,18 +85,27 @@ impl State {
/// Mark those parameter values of an extern function call, that are passed on the stack, /// Mark those parameter values of an extern function call, that are passed on the stack,
/// as unknown data (since the function may modify them). /// as unknown data (since the function may modify them).
pub fn clear_stack_parameter(&mut self, extern_call: &ExternSymbol) -> Result<(), Error> { pub fn clear_stack_parameter(
&mut self,
extern_call: &ExternSymbol,
stack_pointer_register: &Variable,
) -> Result<(), Error> {
let mut result_log = Ok(()); let mut result_log = Ok(());
for arg in &extern_call.arguments { for arg in &extern_call.parameters {
match &arg.location { match arg {
Expression::Var(_) => {} Arg::Register(_) => (),
location_expression => { Arg::Stack { offset, size } => {
let arg_size = arg let data_top = Data::new_top(*size);
.var let location_expression = Expression::BinOp {
.bitsize() lhs: Box::new(Expression::Var(stack_pointer_register.clone())),
.expect("Encountered argument with unknown size"); op: BinOpType::IntAdd,
let data_top = Data::new_top(arg_size); rhs: Box::new(Expression::Const(
if let Err(err) = self.write_to_address(location_expression, &data_top) { Bitvector::from_i64(*offset)
.into_truncate(apint::BitWidth::from(stack_pointer_register.size))
.unwrap(),
)),
};
if let Err(err) = self.write_to_address(&location_expression, &data_top) {
result_log = Err(err); result_log = Err(err);
} }
} }
......
...@@ -12,7 +12,7 @@ fn run_pointer_inference(program_jsonbuilder_val: ocaml::Value) -> (Vec<CweWarni ...@@ -12,7 +12,7 @@ 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, false) crate::analysis::pointer_inference::run(&project.into(), false)
} }
caml!(rs_run_pointer_inference(program_jsonbuilder_val) { caml!(rs_run_pointer_inference(program_jsonbuilder_val) {
...@@ -31,7 +31,7 @@ fn run_pointer_inference_and_print_debug(program_jsonbuilder_val: ocaml::Value) ...@@ -31,7 +31,7 @@ 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, true); // Note: This discard all CweWarnings and log messages. crate::analysis::pointer_inference::run(&project.into(), 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) {
......
...@@ -52,6 +52,32 @@ pub enum Expression { ...@@ -52,6 +52,32 @@ pub enum Expression {
}, },
} }
impl Expression {
/// Return the size (in bytes) of the result value of the expression.
pub fn bytesize(&self) -> ByteSize {
use BinOpType::*;
use Expression::*;
match self {
Var(var) => var.size,
Const(bitvec) => bitvec.width().into(),
BinOp { op, lhs, rhs } => match op {
Piece => lhs.bytesize() + rhs.bytesize(),
IntEqual | IntNotEqual | IntLess | IntSLess | IntLessEqual | IntSLessEqual
| IntCarry | IntSCarry | IntSBorrow | BoolXOr | BoolOr | BoolAnd | FloatEqual
| FloatNotEqual | FloatLess | FloatLessEqual => ByteSize::new(1),
IntAdd | IntSub | IntAnd | IntOr | IntXOr | IntLeft | IntRight | IntSRight
| IntMult | IntDiv | IntRem | IntSDiv | IntSRem | FloatAdd | FloatSub
| FloatMult | FloatDiv => lhs.bytesize(),
},
UnOp { op, arg } => match op {
UnOpType::FloatNaN => ByteSize::new(1),
_ => arg.bytesize(),
},
Cast { size, .. } | Unknown { size, .. } | Subpiece { size, .. } => *size,
}
}
}
/// The type/mnemonic of a binary operation /// The type/mnemonic of a binary operation
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum BinOpType { pub enum BinOpType {
......
...@@ -73,6 +73,12 @@ impl From<ByteSize> for apint::BitWidth { ...@@ -73,6 +73,12 @@ impl From<ByteSize> for apint::BitWidth {
} }
} }
impl From<apint::BitWidth> for ByteSize {
fn from(bitwidth: apint::BitWidth) -> ByteSize {
ByteSize::new(bitwidth.to_usize() as u64 / 8)
}
}
impl ByteSize { impl ByteSize {
pub fn new(value: u64) -> ByteSize { pub fn new(value: u64) -> ByteSize {
ByteSize(value) ByteSize(value)
......
...@@ -181,6 +181,30 @@ pub struct ExternSymbol { ...@@ -181,6 +181,30 @@ pub struct ExternSymbol {
pub no_return: bool, pub no_return: bool,
} }
impl ExternSymbol {
/// If the extern symbol has exactly one return value that is passed in a register,
/// return the register.
pub fn get_unique_return_register(&self) -> Result<&Variable, Error> {
if self.return_values.len() == 1 {
match self.return_values[0] {
Arg::Register(ref var) => Ok(var),
Arg::Stack { .. } => Err(anyhow!("Return value is passed on the stak")),
}
} else {
Err(anyhow!("Wrong number of return values"))
}
}
/// If the extern symbol has exactly one parameter, return the parameter.
pub fn get_unique_parameter(&self) -> Result<&Arg, Error> {
if self.parameters.len() == 1 {
Ok(&self.parameters[0])
} else {
Err(anyhow!("Wrong number of parameter values"))
}
}
}
/// The `Program` structure represents a disassembled binary. /// The `Program` structure represents a disassembled binary.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Program { pub struct Program {
...@@ -205,4 +229,19 @@ pub struct Project { ...@@ -205,4 +229,19 @@ pub struct Project {
pub cpu_architecture: String, pub cpu_architecture: String,
/// The stack pointer register for the given CPU architecture. /// The stack pointer register for the given CPU architecture.
pub stack_pointer_register: Variable, pub stack_pointer_register: Variable,
/// The names of callee-saved registers for the standard calling convention
/// for the given CPU architecture.
/// Note that this field may be removed in the future.
pub callee_saved_registers: Vec<String>,
/// The names of parameter registers for the standard calling convention
/// for the given CPU architecture.
/// Note that this field may be removed in the future.
pub parameter_registers: Vec<String>,
}
impl Project {
/// Return the size (in bytes) for pointers of the given architecture.
pub fn get_pointer_bytesize(&self) -> ByteSize {
self.stack_pointer_register.size
}
} }
...@@ -9,7 +9,7 @@ use crate::prelude::*; ...@@ -9,7 +9,7 @@ use crate::prelude::*;
/// Temporary variables are only valid until the end of the current assembly instruction. /// Temporary variables are only valid until the end of the current assembly instruction.
/// However, one assembly instruction may span more than one basic block in the intermediate representation /// However, one assembly instruction may span more than one basic block in the intermediate representation
/// (but never more than one function). /// (but never more than one function).
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct Variable { pub struct Variable {
pub name: String, pub name: String,
pub size: ByteSize, pub size: ByteSize,
......
...@@ -21,6 +21,7 @@ mod prelude { ...@@ -21,6 +21,7 @@ mod prelude {
pub use serde::{Deserialize, Serialize}; pub use serde::{Deserialize, Serialize};
pub use crate::bil::{BitSize, Bitvector}; pub use crate::bil::{BitSize, Bitvector};
pub use crate::intermediate_representation::ByteSize;
pub use crate::intermediate_representation::{Term, Tid}; pub use crate::intermediate_representation::{Term, Tid};
pub use anyhow::{anyhow, Error}; pub use anyhow::{anyhow, Error};
} }
...@@ -375,10 +375,15 @@ impl From<Project> for IrProject { ...@@ -375,10 +375,15 @@ impl From<Project> for IrProject {
tid: project.program.tid, tid: project.program.tid,
term: project.program.term.into(), term: project.program.term.into(),
}; };
let (params, callee_saved) = crate::utils::get_generic_parameter_and_callee_saved_register(
&project.cpu_architecture,
);
IrProject { IrProject {
program, program,
cpu_architecture: project.cpu_architecture, cpu_architecture: project.cpu_architecture,
stack_pointer_register: project.stack_pointer_register.into(), stack_pointer_register: project.stack_pointer_register.into(),
callee_saved_registers: callee_saved,
parameter_registers: params,
} }
} }
} }
...@@ -657,11 +662,11 @@ mod tests { ...@@ -657,11 +662,11 @@ mod tests {
} }
}, },
"stack_pointer_register": { "stack_pointer_register": {
"name": "ESP", "name": "RSP",
"size": 32, "size": 8,
"is_virtual": false "is_virtual": false
}, },
"cpu_architecture": "x86_32" "cpu_architecture": "x86_64"
} }
"#, "#,
) )
......
...@@ -358,6 +358,8 @@ impl From<Project> for IrProject { ...@@ -358,6 +358,8 @@ impl From<Project> for IrProject {
program, program,
cpu_architecture: project.cpu_architecture, cpu_architecture: project.cpu_architecture,
stack_pointer_register: project.stack_pointer_register.into(), stack_pointer_register: project.stack_pointer_register.into(),
callee_saved_registers: project.callee_saved_registers,
parameter_registers: project.parameter_registers,
} }
} }
} }
......
pub mod log; pub mod log;
/// Get the names of parameter registers and callee saved registers
/// of the standard calling convention for the given architecture.
///
/// The registers are read from a configuration file.
pub fn get_generic_parameter_and_callee_saved_register(
cpu_architecture: &str,
) -> (Vec<String>, Vec<String>) {
let project_dirs = directories::ProjectDirs::from("", "", "cwe_checker")
.expect("Could not discern location of configuration files.");
let config_dir = project_dirs.config_dir();
let register_config_path = config_dir.join("registers.json");
let file = std::fs::read_to_string(register_config_path)
.expect("Could not read register configuration file");
let mut registers_json: serde_json::Value = serde_json::from_str(&file).unwrap();
match cpu_architecture {
"x86" | "x86_32" => registers_json = registers_json["elf"]["x86"]["cdecl"].clone(),
_ => registers_json = registers_json["elf"][cpu_architecture].clone(),
}
let mut callee_saved: Vec<String> =
serde_json::from_value(registers_json["callee_saved"].clone()).unwrap();
let mut callee_saved_float: Vec<String> =
serde_json::from_value(registers_json["float_callee_saved"].clone()).unwrap();
callee_saved.append(&mut callee_saved_float);
let mut params: Vec<String> = serde_json::from_value(registers_json["params"].clone()).unwrap();
let mut params_float: Vec<String> =
serde_json::from_value(registers_json["float_params"].clone()).unwrap();
params.append(&mut params_float);
(params, callee_saved)
}
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