diff options
Diffstat (limited to 'plugins/warp/src/cache.rs')
| -rw-r--r-- | plugins/warp/src/cache.rs | 413 |
1 files changed, 10 insertions, 403 deletions
diff --git a/plugins/warp/src/cache.rs b/plugins/warp/src/cache.rs index cc8dfded..09ad1444 100644 --- a/plugins/warp/src/cache.rs +++ b/plugins/warp/src/cache.rs @@ -1,29 +1,18 @@ -use crate::convert::{from_bn_symbol, from_bn_type_internal}; -use crate::{build_function, function_guid}; +pub mod container; +pub mod function; +pub mod guid; +pub mod type_reference; + +pub use function::*; +pub use guid::*; +pub use type_reference::*; + use binaryninja::binary_view::{BinaryView, BinaryViewExt}; -use binaryninja::confidence::MAX_CONFIDENCE; use binaryninja::function::Function as BNFunction; -use binaryninja::low_level_il::function::{FunctionMutability, LowLevelILFunction, NonSSA}; -use binaryninja::low_level_il::LowLevelILRegularFunction; use binaryninja::rc::Guard; use binaryninja::rc::Ref as BNRef; -use binaryninja::symbol::Symbol as BNSymbol; -use binaryninja::types::NamedTypeReference as BNNamedTypeReference; use binaryninja::ObjectDestructor; -use dashmap::mapref::one::Ref; -use dashmap::DashMap; -use std::collections::HashSet; use std::hash::{DefaultHasher, Hash, Hasher}; -use std::sync::OnceLock; -use warp::r#type::ComputedType; -use warp::signature::function::constraints::FunctionConstraint; -use warp::signature::function::{Function, FunctionGUID}; - -pub static MATCHED_FUNCTION_CACHE: OnceLock<DashMap<ViewID, MatchedFunctionCache>> = - OnceLock::new(); -pub static FUNCTION_CACHE: OnceLock<DashMap<ViewID, FunctionCache>> = OnceLock::new(); -pub static GUID_CACHE: OnceLock<DashMap<ViewID, GUIDCache>> = OnceLock::new(); -pub static TYPE_REF_CACHE: OnceLock<DashMap<ViewID, TypeRefCache>> = OnceLock::new(); pub fn register_cache_destructor() { pub static mut CACHE_DESTRUCTOR: CacheDestructor = CacheDestructor; @@ -34,351 +23,6 @@ pub fn register_cache_destructor() { }; } -pub fn cached_function_match<F>(function: &BNFunction, f: F) -> Option<Function> -where - F: Fn() -> Option<Function>, -{ - let view = function.view(); - let view_id = ViewID::from(view.as_ref()); - let function_id = FunctionID::from(function); - let function_cache = MATCHED_FUNCTION_CACHE.get_or_init(Default::default); - match function_cache.get(&view_id) { - Some(cache) => cache.get_or_insert(&function_id, f).to_owned(), - None => { - let cache = MatchedFunctionCache::default(); - let matched = cache.get_or_insert(&function_id, f).to_owned(); - function_cache.insert(view_id, cache); - matched - } - } -} - -pub fn try_cached_function_match(function: &BNFunction) -> Option<Function> { - let view = function.view(); - let view_id = ViewID::from(view); - let function_id = FunctionID::from(function); - let function_cache = MATCHED_FUNCTION_CACHE.get_or_init(Default::default); - function_cache - .get(&view_id)? - .get(&function_id)? - .value() - .to_owned() -} - -pub fn cached_function(function: &BNFunction, llil: &LowLevelILRegularFunction) -> Function { - let view = function.view(); - let view_id = ViewID::from(view.as_ref()); - let function_cache = FUNCTION_CACHE.get_or_init(Default::default); - match function_cache.get(&view_id) { - Some(cache) => cache.function(function, llil), - None => { - let cache = FunctionCache::default(); - let function = cache.function(function, llil); - function_cache.insert(view_id, cache); - function - } - } -} - -pub fn cached_call_site_constraints(function: &BNFunction) -> HashSet<FunctionConstraint> { - let view = function.view(); - let view_id = ViewID::from(view); - let guid_cache = GUID_CACHE.get_or_init(Default::default); - match guid_cache.get(&view_id) { - Some(cache) => cache.call_site_constraints(function), - None => { - let cache = GUIDCache::default(); - let constraints = cache.call_site_constraints(function); - guid_cache.insert(view_id, cache); - constraints - } - } -} - -pub fn cached_adjacency_constraints<F>( - function: &BNFunction, - filter: F, -) -> HashSet<FunctionConstraint> -where - F: Fn(&BNFunction) -> bool, -{ - let view = function.view(); - let view_id = ViewID::from(view); - let guid_cache = GUID_CACHE.get_or_init(Default::default); - match guid_cache.get(&view_id) { - Some(cache) => cache.adjacency_constraints(function, filter), - None => { - let cache = GUIDCache::default(); - let constraints = cache.adjacency_constraints(function, filter); - guid_cache.insert(view_id, cache); - constraints - } - } -} - -pub fn cached_function_guid<M: FunctionMutability>( - function: &BNFunction, - llil: &LowLevelILFunction<M, NonSSA>, -) -> FunctionGUID { - let view = function.view(); - let view_id = ViewID::from(view); - let guid_cache = GUID_CACHE.get_or_init(Default::default); - match guid_cache.get(&view_id) { - Some(cache) => cache.function_guid(function, llil), - None => { - let cache = GUIDCache::default(); - let guid = cache.function_guid(function, llil); - guid_cache.insert(view_id, cache); - guid - } - } -} - -pub fn try_cached_function_guid(function: &BNFunction) -> Option<FunctionGUID> { - let view = function.view(); - let view_id = ViewID::from(view); - let guid_cache = GUID_CACHE.get_or_init(Default::default); - guid_cache.get(&view_id)?.try_function_guid(function) -} - -pub fn cached_type_reference( - view: &BinaryView, - visited_refs: &mut HashSet<TypeRefID>, - type_ref: &BNNamedTypeReference, -) -> Option<ComputedType> { - let view_id = ViewID::from(view); - let type_ref_cache = TYPE_REF_CACHE.get_or_init(Default::default); - match type_ref_cache.get(&view_id) { - Some(cache) => cache.cached_type_reference(view, visited_refs, type_ref), - None => { - let cache = TypeRefCache::default(); - let ntr = cache.cached_type_reference(view, visited_refs, type_ref); - type_ref_cache.insert(view_id, cache); - ntr - } - } -} - -pub fn cached_type_references(view: &BinaryView) -> Option<Ref<ViewID, TypeRefCache>> { - let view_id = ViewID::from(view); - let type_ref_cache = TYPE_REF_CACHE.get_or_init(Default::default); - type_ref_cache.get(&view_id) -} - -#[derive(Clone, Debug, Default)] -pub struct MatchedFunctionCache { - pub cache: DashMap<FunctionID, Option<Function>>, -} - -impl MatchedFunctionCache { - pub fn get_or_insert<F>( - &self, - function_id: &FunctionID, - f: F, - ) -> Ref<'_, FunctionID, Option<Function>> - where - F: FnOnce() -> Option<Function>, - { - self.cache.get(function_id).unwrap_or_else(|| { - self.cache.insert(*function_id, f()); - self.cache.get(function_id).unwrap() - }) - } - - pub fn get(&self, function_id: &FunctionID) -> Option<Ref<'_, FunctionID, Option<Function>>> { - self.cache.get(function_id) - } -} - -#[derive(Clone, Debug, Default)] -pub struct FunctionCache { - pub cache: DashMap<FunctionID, Function>, -} - -impl FunctionCache { - pub fn function(&self, function: &BNFunction, llil: &LowLevelILRegularFunction) -> Function { - let function_id = FunctionID::from(function); - match self.cache.get(&function_id) { - Some(function) => function.value().to_owned(), - None => { - let function = build_function(function, llil); - self.cache.insert(function_id, function.clone()); - function - } - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct GUIDCache { - pub cache: DashMap<FunctionID, FunctionGUID>, -} - -impl GUIDCache { - pub fn call_site_constraints(&self, function: &BNFunction) -> HashSet<FunctionConstraint> { - let view = function.view(); - let func_id = FunctionID::from(function); - let func_start = function.start(); - let func_platform = function.platform(); - let mut constraints = HashSet::new(); - for call_site in &function.call_sites() { - for cs_ref_addr in view.code_refs_from_addr(call_site.address, Some(function)) { - match view.function_at(&func_platform, cs_ref_addr) { - Some(cs_ref_func) => { - // Call site is a function, constrain on it. - let cs_ref_func_id = FunctionID::from(cs_ref_func.as_ref()); - if cs_ref_func_id != func_id { - let call_site_offset: i64 = - call_site.address.wrapping_sub(func_start) as i64; - // TODO: If the function is thunk we should also insert the called function. - constraints - .insert(self.function_constraint(&cs_ref_func, call_site_offset)); - } - } - None => { - // We could be dealing with an extern symbol, get the symbol as a constraint. - let call_site_offset: i64 = - call_site.address.wrapping_sub(func_start) as i64; - if let Some(call_site_sym) = view.symbol_by_address(cs_ref_addr) { - constraints.insert( - self.function_constraint_from_symbol( - &call_site_sym, - call_site_offset, - ), - ); - } - } - } - } - } - constraints - } - - pub fn adjacency_constraints<F>( - &self, - function: &BNFunction, - filter: F, - ) -> HashSet<FunctionConstraint> - where - F: Fn(&BNFunction) -> bool, - { - let view = function.view(); - let func_id = FunctionID::from(function); - let func_start = function.start(); - let mut constraints = HashSet::new(); - - let mut func_addr_constraint = |func_start_addr| { - // NOTE: We could potentially have dozens of functions all at the same start address. - for curr_func in &view.functions_at(func_start_addr) { - let curr_func_id = FunctionID::from(curr_func.as_ref()); - if curr_func_id != func_id && filter(curr_func.as_ref()) { - // NOTE: For this to work the GUID has to have already been cached. If not it will just be the symbol. - // Function adjacent to another function, constrain on the pattern. - let curr_addr_offset = (func_start_addr as i64) - func_start as i64; - constraints.insert(self.function_constraint(&curr_func, curr_addr_offset)); - } - } - }; - - let mut before_func_start = func_start; - for _ in 0..2 { - before_func_start = view.function_start_before(before_func_start); - func_addr_constraint(before_func_start); - } - - let mut after_func_start = func_start; - for _ in 0..2 { - after_func_start = view.function_start_after(after_func_start); - func_addr_constraint(after_func_start); - } - - constraints - } - - /// Construct a function constraint, must pass the offset at which it is located. - pub fn function_constraint(&self, function: &BNFunction, offset: i64) -> FunctionConstraint { - let guid = self.try_function_guid(function); - let symbol = from_bn_symbol(&function.symbol()); - FunctionConstraint { - guid, - symbol: Some(symbol), - offset, - } - } - - /// Construct a function constraint from a symbol, typically used for extern function call sites, must pass the offset at which it is located. - pub fn function_constraint_from_symbol( - &self, - symbol: &BNSymbol, - offset: i64, - ) -> FunctionConstraint { - let symbol = from_bn_symbol(symbol); - FunctionConstraint { - guid: None, - symbol: Some(symbol), - offset, - } - } - - pub fn function_guid<M: FunctionMutability>( - &self, - function: &BNFunction, - llil: &LowLevelILFunction<M, NonSSA>, - ) -> FunctionGUID { - let function_id = FunctionID::from(function); - match self.cache.get(&function_id) { - Some(function_guid) => function_guid.value().to_owned(), - None => { - let function_guid = function_guid(function, llil); - self.cache.insert(function_id, function_guid); - function_guid - } - } - } - - pub fn try_function_guid(&self, function: &BNFunction) -> Option<FunctionGUID> { - let function_id = FunctionID::from(function); - self.cache - .get(&function_id) - .map(|function_guid| function_guid.value().to_owned()) - } -} - -#[derive(Clone, Debug, Default)] -pub struct TypeRefCache { - pub cache: DashMap<TypeRefID, Option<ComputedType>>, -} - -impl TypeRefCache { - /// NOTE: No self-referential type must be used on this function. - pub fn cached_type_reference( - &self, - view: &BinaryView, - visited_refs: &mut HashSet<TypeRefID>, - type_ref: &BNNamedTypeReference, - ) -> Option<ComputedType> { - let ntr_id = TypeRefID::from(type_ref); - match self.cache.get(&ntr_id) { - Some(cache) => cache.to_owned(), - None => match type_ref.target(view) { - Some(raw_ty) => { - let computed_ty = ComputedType::new(from_bn_type_internal( - view, - visited_refs, - &raw_ty, - MAX_CONFIDENCE, - )); - self.cache - .entry(ntr_id) - .insert(Some(computed_ty)) - .to_owned() - } - None => self.cache.entry(ntr_id).insert(None).to_owned(), - }, - } - } -} - /// A unique view ID, used for caching. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct ViewID(u64); @@ -430,48 +74,11 @@ impl From<Guard<'_, BNFunction>> for FunctionID { } } -/// A unique named type reference ID, used for caching. -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct TypeRefID(u64); - -impl From<&BNNamedTypeReference> for TypeRefID { - fn from(value: &BNNamedTypeReference) -> Self { - let mut hasher = DefaultHasher::new(); - hasher.write(value.id().as_bytes()); - Self(hasher.finish()) - } -} - -impl From<BNRef<BNNamedTypeReference>> for TypeRefID { - fn from(value: BNRef<BNNamedTypeReference>) -> Self { - Self::from(value.as_ref()) - } -} - -impl From<Guard<'_, BNNamedTypeReference>> for TypeRefID { - fn from(value: Guard<'_, BNNamedTypeReference>) -> Self { - Self::from(value.as_ref()) - } -} - pub struct CacheDestructor; impl ObjectDestructor for CacheDestructor { fn destruct_view(&self, view: &BinaryView) { - // Clear caches as the view is no longer alive. - let view_id = ViewID::from(view); - if let Some(cache) = MATCHED_FUNCTION_CACHE.get() { - cache.remove(&view_id); - } - if let Some(cache) = FUNCTION_CACHE.get() { - cache.remove(&view_id); - } - if let Some(cache) = GUID_CACHE.get() { - cache.remove(&view_id); - } - if let Some(cache) = TYPE_REF_CACHE.get() { - cache.remove(&view_id); - } + clear_type_ref_cache(view); log::debug!("Removed WARP caches for {:?}", view.file().filename()); } } |
