From 1beb4b344f543e9630b446c75ad7a693ab4d4484 Mon Sep 17 00:00:00 2001 From: Vincent Thiberville Date: Sun, 22 Sep 2024 11:09:27 +0200 Subject: [PATCH 1/4] feat: add ModuleUserData struct Add a wrapper struct for ModuleUserData rather than a type alias. This will allow modifying the impl on it, notably the Debug one which will be lost when bounds on UnwindSafe are added. --- boreal/src/evaluator/module.rs | 2 +- boreal/src/module/math.rs | 28 ++++++++++++++-------------- boreal/src/module/mod.rs | 14 +++++++++----- boreal/src/scanner/mod.rs | 7 ++++--- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/boreal/src/evaluator/module.rs b/boreal/src/evaluator/module.rs index f0433466..1a38066a 100644 --- a/boreal/src/evaluator/module.rs +++ b/boreal/src/evaluator/module.rs @@ -218,7 +218,7 @@ mod tests { fn test_types_traits() { test_type_traits_non_clonable(EvalData { values: Vec::new(), - data_map: ModuleDataMap::new(&HashMap::new()), + data_map: ModuleDataMap::new(&ModuleUserData::default()), }); } } diff --git a/boreal/src/module/math.rs b/boreal/src/module/math.rs index fbdee7c2..4382d209 100644 --- a/boreal/src/module/math.rs +++ b/boreal/src/module/math.rs @@ -622,7 +622,7 @@ fn offset_length_to_start_end(offset: i64, length: i64) -> Option<(usize, usize) #[cfg(test)] mod tests { use crate::memory::Memory; - use crate::module::ModuleDataMap; + use crate::module::{ModuleDataMap, ModuleUserData}; use super::*; @@ -638,7 +638,7 @@ mod tests { #[test] fn test_in_range_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::in_range(&mut ctx, vec![]).is_none()); @@ -651,7 +651,7 @@ mod tests { #[test] fn test_deviation_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::deviation(&mut ctx, vec![]).is_none()); @@ -666,7 +666,7 @@ mod tests { #[test] fn test_mean_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::mean(&mut ctx, vec![]).is_none()); @@ -677,7 +677,7 @@ mod tests { #[test] fn test_serial_correlation_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::serial_correlation(&mut ctx, vec![]).is_none()); @@ -688,7 +688,7 @@ mod tests { #[test] fn test_monte_carlo_pi_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::monte_carlo_pi(&mut ctx, vec![]).is_none()); @@ -699,7 +699,7 @@ mod tests { #[test] fn test_entropy_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::entropy(&mut ctx, vec![]).is_none()); @@ -710,7 +710,7 @@ mod tests { #[test] fn test_min_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::min(&mut ctx, vec![]).is_none()); @@ -721,7 +721,7 @@ mod tests { #[test] fn test_max_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::max(&mut ctx, vec![]).is_none()); @@ -732,7 +732,7 @@ mod tests { #[test] fn test_to_number_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::to_number(&mut ctx, vec![]).is_none()); @@ -741,7 +741,7 @@ mod tests { #[test] fn test_abs_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::abs(&mut ctx, vec![]).is_none()); @@ -750,7 +750,7 @@ mod tests { #[test] fn test_count_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::count(&mut ctx, vec![]).is_none()); @@ -760,7 +760,7 @@ mod tests { #[test] fn test_percentage_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::percentage(&mut ctx, vec![]).is_none()); @@ -770,7 +770,7 @@ mod tests { #[test] fn test_mode_invalid_args() { - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut ctx = ctx!(&user_data); assert!(Math::mode(&mut ctx, vec![0.5.into()]).is_none()); diff --git a/boreal/src/module/mod.rs b/boreal/src/module/mod.rs index 402bfb65..8cd0d2db 100644 --- a/boreal/src/module/mod.rs +++ b/boreal/src/module/mod.rs @@ -246,7 +246,9 @@ impl std::fmt::Debug for EvalContext<'_, '_, '_> { } } -pub(crate) type ModuleUserData = HashMap>; +#[doc(hidden)] +#[derive(Default, Debug, Clone)] +pub struct ModuleUserData(pub HashMap>); /// Object holding the data of each module. See [`ModuleData`]. pub struct ModuleDataMap<'scanner> { @@ -423,6 +425,7 @@ impl<'scanner> ModuleDataMap<'scanner> { #[must_use] pub fn get_user_data(&self) -> Option<&T::UserData> { self.user_data + .0 .get(&TypeId::of::()) .and_then(|v| v.downcast_ref()) } @@ -475,10 +478,10 @@ pub enum Value { /// ``` /// # use std::collections::HashMap; /// # use boreal::memory::Memory; - /// # use boreal::module::{EvalContext, Value, ModuleDataMap}; + /// # use boreal::module::{EvalContext, Value, ModuleDataMap, ModuleUserData}; /// # let x = 3; /// # fn fun(_: &mut EvalContext, _: Vec) -> Option { None } - /// # let user_data = HashMap::new(); + /// # let user_data = ModuleUserData::default(); /// # let mut ctx = EvalContext { /// # mem: &mut Memory::Direct(b""), /// # module_data: &ModuleDataMap::new(&user_data), @@ -872,14 +875,15 @@ mod tests { #[test] fn test_types_traits() { + test_type_traits(ModuleUserData::default()); test_type_traits_non_clonable(ScanContext { region: &Region { start: 0, mem: b"" }, - module_data: &mut ModuleDataMap::new(&HashMap::new()), + module_data: &mut ModuleDataMap::new(&ModuleUserData::default()), process_memory: false, }); test_type_traits_non_clonable(EvalContext { mem: &mut Memory::Direct(b""), - module_data: &ModuleDataMap::new(&HashMap::new()), + module_data: &ModuleDataMap::new(&ModuleUserData::default()), process_memory: false, }); diff --git a/boreal/src/scanner/mod.rs b/boreal/src/scanner/mod.rs index 47486de4..2aa0c9af 100644 --- a/boreal/src/scanner/mod.rs +++ b/boreal/src/scanner/mod.rs @@ -136,7 +136,7 @@ impl Scanner { }), scan_params: ScanParams::default(), external_symbols_values, - module_user_data: HashMap::new(), + module_user_data: ModuleUserData::default(), } } @@ -330,6 +330,7 @@ impl Scanner { { let _r = self .module_user_data + .0 .insert(TypeId::of::(), Arc::new(data)); } @@ -972,7 +973,7 @@ mod tests { let _r = compiler.add_rules_str(rule_str).unwrap(); let scanner = compiler.into_scanner(); - let user_data = HashMap::new(); + let user_data = ModuleUserData::default(); let mut module_values = evaluator::module::EvalData::new(&scanner.inner.modules, &user_data); module_values.scan_region(&Region { start: 0, mem }, &scanner.inner.modules, false); @@ -1537,7 +1538,7 @@ mod tests { matched_rules: Vec::new(), module_values: evaluator::module::EvalData { values: Vec::new(), - data_map: crate::module::ModuleDataMap::new(&HashMap::new()), + data_map: crate::module::ModuleDataMap::new(&ModuleUserData::default()), }, statistics: None, timeout_checker: None, From e18c925bd674de571a7532d4307cd626190c2f2a Mon Sep 17 00:00:00 2001 From: Vincent Thiberville Date: Sun, 22 Sep 2024 11:48:21 +0200 Subject: [PATCH 2/4] feat: add UnwindSafe traits for module user datas --- boreal/src/module/mod.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/boreal/src/module/mod.rs b/boreal/src/module/mod.rs index 8cd0d2db..ae1cbd7f 100644 --- a/boreal/src/module/mod.rs +++ b/boreal/src/module/mod.rs @@ -39,6 +39,7 @@ //! ``` use std::any::{Any, TypeId}; use std::collections::HashMap; +use std::panic::{RefUnwindSafe, UnwindSafe}; use std::sync::Arc; use crate::memory::{Memory, Region}; @@ -247,8 +248,16 @@ impl std::fmt::Debug for EvalContext<'_, '_, '_> { } #[doc(hidden)] -#[derive(Default, Debug, Clone)] -pub struct ModuleUserData(pub HashMap>); +#[derive(Default, Clone)] +pub struct ModuleUserData( + pub HashMap>, +); + +impl std::fmt::Debug for ModuleUserData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("ModuleUserData").finish() + } +} /// Object holding the data of each module. See [`ModuleData`]. pub struct ModuleDataMap<'scanner> { @@ -387,7 +396,7 @@ pub trait ModuleData: Module { /// /// This data is provided by the user with calls to /// [`Scanner::set_module_data`](crate::scanner::Scanner::set_module_data). - type UserData: Any + Send + Sync; + type UserData: Any + Send + Sync + UnwindSafe + RefUnwindSafe; } impl<'scanner> ModuleDataMap<'scanner> { @@ -424,10 +433,12 @@ impl<'scanner> ModuleDataMap<'scanner> { /// Retrieve the user data of a module. #[must_use] pub fn get_user_data(&self) -> Option<&T::UserData> { - self.user_data - .0 - .get(&TypeId::of::()) - .and_then(|v| v.downcast_ref()) + self.user_data.0.get(&TypeId::of::()).and_then(|v| { + // For some reason, having "dyn Any + Send + Sync + UnwindSafe + RefUnwindSafe" + // causes the rust compiler to fail to resolve `v.downcast_ref()`, so we need to + // explicit where this method comes from. + ::downcast_ref(&**v) + }) } } From db8557c5eef911ab9ba56820e172a438a4100502 Mon Sep 17 00:00:00 2001 From: Vincent Thiberville Date: Sun, 22 Sep 2024 11:53:39 +0200 Subject: [PATCH 3/4] feat: add UnwindSafe traits to module private datas --- boreal/src/module/console.rs | 5 +++-- boreal/src/module/mod.rs | 22 ++++++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/boreal/src/module/console.rs b/boreal/src/module/console.rs index bae257be..5987f5e9 100644 --- a/boreal/src/module/console.rs +++ b/boreal/src/module/console.rs @@ -1,4 +1,5 @@ use std::fmt::Write; +use std::panic::{RefUnwindSafe, UnwindSafe}; use std::{collections::HashMap, sync::Arc}; use super::{EvalContext, Module, ModuleData, ModuleDataMap, StaticValue, Type, Value}; @@ -9,7 +10,7 @@ pub struct Console { } /// Type of callback called when a message is logged. -pub type LogCallback = dyn Fn(String) + Send + Sync; +pub type LogCallback = dyn Fn(String) + Send + Sync + UnwindSafe + RefUnwindSafe; impl Module for Console { fn get_name(&self) -> &'static str { @@ -69,7 +70,7 @@ impl Console { #[must_use] pub fn with_callback(callback: T) -> Self where - T: Fn(String) + Send + Sync + 'static, + T: Fn(String) + Send + Sync + UnwindSafe + RefUnwindSafe + 'static, { Self { callback: Arc::new(callback), diff --git a/boreal/src/module/mod.rs b/boreal/src/module/mod.rs index ae1cbd7f..218f3f33 100644 --- a/boreal/src/module/mod.rs +++ b/boreal/src/module/mod.rs @@ -261,7 +261,7 @@ impl std::fmt::Debug for ModuleUserData { /// Object holding the data of each module. See [`ModuleData`]. pub struct ModuleDataMap<'scanner> { - private_data: HashMap>, + private_data: HashMap>, user_data: &'scanner ModuleUserData, } @@ -390,7 +390,7 @@ pub trait ModuleData: Module { /// Private Data to associate with the module. /// /// This is the data that the module can store and retrieve during a scan. - type PrivateData: Any + Send + Sync; + type PrivateData: Any + Send + Sync + UnwindSafe + RefUnwindSafe; /// Data that the user can provide to the module. /// @@ -417,17 +417,23 @@ impl<'scanner> ModuleDataMap<'scanner> { /// Retrieve the private data of a module. #[must_use] pub fn get(&self) -> Option<&T::PrivateData> { - self.private_data - .get(&TypeId::of::()) - .and_then(|v| v.downcast_ref()) + self.private_data.get(&TypeId::of::()).and_then(|v| { + // For some reason, having "dyn Any + Send + Sync + UnwindSafe + RefUnwindSafe" + // causes the rust compiler to fail to resolve `v.downcast_ref()`, so we need to + // explicit where this method comes from. + ::downcast_ref(&**v) + }) } /// Retrieve a mutable borrow on the private data of a module. #[must_use] pub fn get_mut(&mut self) -> Option<&mut T::PrivateData> { - self.private_data - .get_mut(&TypeId::of::()) - .and_then(|v| v.downcast_mut()) + self.private_data.get_mut(&TypeId::of::()).and_then(|v| { + // For some reason, having "dyn Any + Send + Sync + UnwindSafe + RefUnwindSafe" + // causes the rust compiler to fail to resolve `v.downcast_mut()`, so we need to + // explicit where this method comes from. + ::downcast_mut(&mut **v) + }) } /// Retrieve the user data of a module. From ac0e17279b12b7384ff6a815bbcc41504b07a10a Mon Sep 17 00:00:00 2001 From: Vincent Thiberville Date: Sun, 22 Sep 2024 11:59:56 +0200 Subject: [PATCH 4/4] feat: ensure Scanner is UnwindSafe --- boreal/src/matcher/validator/dfa.rs | 3 ++- boreal/src/module/mod.rs | 2 +- boreal/src/scanner/mod.rs | 6 +++++- boreal/src/test_helpers.rs | 6 +++++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/boreal/src/matcher/validator/dfa.rs b/boreal/src/matcher/validator/dfa.rs index 0711bd42..66f98c5c 100644 --- a/boreal/src/matcher/validator/dfa.rs +++ b/boreal/src/matcher/validator/dfa.rs @@ -1,3 +1,4 @@ +use std::panic::{RefUnwindSafe, UnwindSafe}; use std::sync::Arc; use regex_automata::hybrid::dfa::{Builder, Cache, DFA}; @@ -11,7 +12,7 @@ use crate::matcher::widener::widen_hir; use crate::matcher::{MatchType, Modifiers}; use crate::regex::{regex_hir_to_string, Hir}; -type PoolCreateFn = Box Cache + Send + Sync>; +type PoolCreateFn = Box Cache + Send + Sync + UnwindSafe + RefUnwindSafe>; #[derive(Debug)] pub(crate) struct DfaValidator { diff --git a/boreal/src/module/mod.rs b/boreal/src/module/mod.rs index 218f3f33..825e6a7a 100644 --- a/boreal/src/module/mod.rs +++ b/boreal/src/module/mod.rs @@ -109,7 +109,7 @@ pub use self::cuckoo::{Cuckoo, CuckooData}; /// /// - As dynamic values, whose shape depend on the memory being scanned. This often includes /// arrays such as `elf.sections`, or raw values such as `pe.machine`. -pub trait Module: Send + Sync { +pub trait Module: Send + Sync + UnwindSafe + RefUnwindSafe { /// Name of the module, used in `import` clauses. fn get_name(&self) -> &'static str; diff --git a/boreal/src/scanner/mod.rs b/boreal/src/scanner/mod.rs index 2aa0c9af..f6db0d24 100644 --- a/boreal/src/scanner/mod.rs +++ b/boreal/src/scanner/mod.rs @@ -882,7 +882,9 @@ impl std::fmt::Display for DefineSymbolError { #[cfg(test)] mod tests { use crate::module::{EvalContext, ScanContext, StaticValue, Type, Value as ModuleValue}; - use crate::test_helpers::{test_type_traits, test_type_traits_non_clonable}; + use crate::test_helpers::{ + test_type_traits, test_type_traits_non_clonable, test_type_unwind_safe, + }; use crate::Compiler; use super::*; @@ -1515,6 +1517,8 @@ mod tests { Vec::new(), BytesPool::default(), )); + test_type_unwind_safe::(); + test_type_traits_non_clonable(ScanResult { matched_rules: Vec::new(), module_values: Vec::new(), diff --git a/boreal/src/test_helpers.rs b/boreal/src/test_helpers.rs index 1f41093b..8a27673c 100644 --- a/boreal/src/test_helpers.rs +++ b/boreal/src/test_helpers.rs @@ -1,3 +1,5 @@ +use std::panic::{RefUnwindSafe, UnwindSafe}; + use boreal_parser::hex_string::parse_hex_string; use boreal_parser::regex::parse_regex; @@ -19,9 +21,11 @@ pub fn expr_to_hir(expr: &str) -> Hir { pub fn test_type_traits(t: T) { #[allow(clippy::redundant_clone)] let _r = t.clone(); - let _r = format!("{:?}", &t); + test_type_traits_non_clonable(t); } pub fn test_type_traits_non_clonable(t: T) { let _r = format!("{:?}", &t); } + +pub fn test_type_unwind_safe() {}