#![doc(html_root_url = "https://docs.rs/haybale/")]
use llvm_ir::Type;
use std::collections::HashSet;
mod project;
pub use project::Project;
mod symex;
pub use symex::*;
pub mod config;
pub use config::Config;
mod error;
pub use error::*;
mod parameter_val;
pub use parameter_val::ParameterVal;
mod return_value;
pub use return_value::ReturnValue;
mod alloc;
pub mod alloc_utils;
pub mod backend;
pub mod callbacks;
pub mod cell_memory;
mod demangling;
mod double_keyed_map;
pub mod function_hooks;
mod global_allocations;
pub mod hook_utils;
mod hooks;
pub mod simple_memory;
pub mod solver_utils;
mod state;
pub use state::get_path_length;
mod varmap;
pub mod watchpoints;
use backend::*;
use itertools::Itertools;
use solver_utils::PossibleSolutions;
#[cfg(test)]
mod test_utils;
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum SolutionValue {
I8(i8),
I16(i16),
I32(i32),
I64(i64),
Ptr(u64),
}
impl SolutionValue {
pub fn unwrap_to_i8(self) -> i8 {
match self {
SolutionValue::I8(i) => i,
_ => panic!("unwrap_to_i8 on {:?}", self),
}
}
pub fn unwrap_to_i16(self) -> i16 {
match self {
SolutionValue::I16(i) => i,
_ => panic!("unwrap_to_i16 on {:?}", self),
}
}
pub fn unwrap_to_i32(self) -> i32 {
match self {
SolutionValue::I32(i) => i,
_ => panic!("unwrap_to_i32 on {:?}", self),
}
}
pub fn unwrap_to_i64(self) -> i64 {
match self {
SolutionValue::I64(i) => i,
_ => panic!("unwrap_to_i64 on {:?}", self),
}
}
pub fn unwrap_to_ptr(self) -> u64 {
match self {
SolutionValue::Ptr(u) => u,
_ => panic!("unwrap_to_ptr on {:?}", self),
}
}
}
pub fn find_zero_of_func<'p>(
funcname: &str,
project: &'p Project,
config: Config<'p, DefaultBackend>,
params: Option<Vec<ParameterVal>>,
) -> std::result::Result<Option<Vec<SolutionValue>>, String> {
let mut em: ExecutionManager<DefaultBackend> =
symex_function(funcname, project, config, params).unwrap();
let returnwidth = match em.func().return_type.as_ref() {
Type::VoidType => {
return Err("find_zero_of_func: function has void type".into());
},
ty => {
let width = project
.size_in_bits(&ty)
.expect("Function return type shouldn't be an opaque struct type");
assert_ne!(width, 0, "Function return type has width 0 bits but isn't void type"); width
},
};
let zero = em.state().zero(returnwidth);
let mut found = false;
while let Some(bvretval) = em.next() {
match bvretval {
Ok(ReturnValue::ReturnVoid) => panic!("Function shouldn't return void"),
Ok(ReturnValue::Throw(_)) => continue, Ok(ReturnValue::Abort) => continue,
Ok(ReturnValue::Return(bvretval)) => {
let state = em.mut_state();
bvretval._eq(&zero).assert();
if state.sat()? {
found = true;
break;
}
},
Err(Error::LoopBoundExceeded(_)) => continue, Err(e) => return Err(em.state().full_error_message_with_context(e)),
}
}
let param_bvs: Vec<_> = em.param_bvs().clone();
let func = em.func();
let state = em.mut_state();
if found {
Ok(Some(
func.parameters
.iter()
.zip_eq(param_bvs.iter())
.map(|(p, bv)| {
let param_as_u64 = state
.get_a_solution_for_bv(bv)?
.expect("since state.sat() passed, expected a solution for each var")
.as_u64()
.expect("parameter more than 64 bits wide");
Ok(match p.ty.as_ref() {
Type::IntegerType { bits: 8 } => SolutionValue::I8(param_as_u64 as i8),
Type::IntegerType { bits: 16 } => SolutionValue::I16(param_as_u64 as i16),
Type::IntegerType { bits: 32 } => SolutionValue::I32(param_as_u64 as i32),
Type::IntegerType { bits: 64 } => SolutionValue::I64(param_as_u64 as i64),
Type::PointerType { .. } => SolutionValue::Ptr(param_as_u64),
ty => unimplemented!("Function parameter with type {:?}", ty),
})
})
.collect::<Result<_>>()?,
))
} else {
Ok(None)
}
}
pub fn get_possible_return_values_of_func<'p>(
funcname: &str,
project: &'p Project,
config: Config<'p, DefaultBackend>,
params: Option<Vec<ParameterVal>>,
thrown_size: Option<u32>,
n: usize,
) -> PossibleSolutions<ReturnValue<u64>> {
let mut em: ExecutionManager<DefaultBackend> =
symex_function(funcname, project, config, params).unwrap();
let return_width = project
.size_in_bits(&em.func().return_type)
.expect("Function return type shouldn't be opaque struct type");
let mut candidate_values = HashSet::<ReturnValue<u64>>::new();
let mut have_throw = false; while let Some(bvretval) = em.next() {
match bvretval {
Err(e) => panic!("{}", em.state().full_error_message_with_context(e)),
Ok(ReturnValue::ReturnVoid) => {
candidate_values.insert(ReturnValue::ReturnVoid);
if candidate_values.len() > n {
break;
}
},
Ok(ReturnValue::Abort) => {
candidate_values.insert(ReturnValue::Abort);
if candidate_values.len() > n {
break;
}
},
Ok(ReturnValue::Return(bvretval)) => {
assert_eq!(bvretval.get_width(), return_width);
let state = em.mut_state();
for candidate in candidate_values.iter() {
if let ReturnValue::Return(candidate) = candidate {
bvretval
._ne(&state.bv_from_u64(*candidate, return_width))
.assert();
}
}
match state.get_possible_solutions_for_bv(&bvretval, n).unwrap() {
PossibleSolutions::Exactly(v) => {
candidate_values.extend(
v.iter()
.map(|bvsol| ReturnValue::Return(bvsol.as_u64().unwrap())),
);
if candidate_values.len() > n {
break;
}
},
PossibleSolutions::AtLeast(v) => {
candidate_values.extend(
v.iter()
.map(|bvsol| ReturnValue::Return(bvsol.as_u64().unwrap())),
);
break; },
};
},
Ok(ReturnValue::Throw(bvptr)) => {
let state = em.mut_state();
match thrown_size {
None => {
if !have_throw {
candidate_values.insert(ReturnValue::Throw(bvptr.as_u64().unwrap()));
have_throw = true;
if candidate_values.len() > n {
break;
}
}
},
Some(thrown_size) => {
let thrown_value = state.read(&bvptr, thrown_size).unwrap();
for candidate in candidate_values.iter() {
if let ReturnValue::Throw(candidate) = candidate {
thrown_value
._ne(&state.bv_from_u64(*candidate, thrown_size))
.assert();
}
}
match state
.get_possible_solutions_for_bv(&thrown_value, n)
.unwrap()
{
PossibleSolutions::Exactly(v) => {
candidate_values.extend(
v.iter()
.map(|bvsol| ReturnValue::Throw(bvsol.as_u64().unwrap())),
);
if candidate_values.len() > n {
break;
}
},
PossibleSolutions::AtLeast(v) => {
candidate_values.extend(
v.iter()
.map(|bvsol| ReturnValue::Throw(bvsol.as_u64().unwrap())),
);
break; },
}
},
}
},
}
}
if candidate_values.len() > n {
PossibleSolutions::AtLeast(candidate_values)
} else {
PossibleSolutions::Exactly(candidate_values)
}
}