Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Scanner object unwind safe #171

Merged
merged 4 commits into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion boreal/src/evaluator/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
});
}
}
3 changes: 2 additions & 1 deletion boreal/src/matcher/validator/dfa.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::panic::{RefUnwindSafe, UnwindSafe};
use std::sync::Arc;

use regex_automata::hybrid::dfa::{Builder, Cache, DFA};
Expand All @@ -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<dyn Fn() -> Cache + Send + Sync>;
type PoolCreateFn = Box<dyn Fn() -> Cache + Send + Sync + UnwindSafe + RefUnwindSafe>;

#[derive(Debug)]
pub(crate) struct DfaValidator {
Expand Down
5 changes: 3 additions & 2 deletions boreal/src/module/console.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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 {
Expand Down Expand Up @@ -69,7 +70,7 @@ impl Console {
#[must_use]
pub fn with_callback<T>(callback: T) -> Self
where
T: Fn(String) + Send + Sync + 'static,
T: Fn(String) + Send + Sync + UnwindSafe + RefUnwindSafe + 'static,
{
Self {
callback: Arc::new(callback),
Expand Down
28 changes: 14 additions & 14 deletions boreal/src/module/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -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());
Expand All @@ -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());
Expand Down
57 changes: 39 additions & 18 deletions boreal/src/module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -108,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;

Expand Down Expand Up @@ -246,11 +247,21 @@ impl std::fmt::Debug for EvalContext<'_, '_, '_> {
}
}

pub(crate) type ModuleUserData = HashMap<TypeId, Arc<dyn Any + Send + Sync>>;
#[doc(hidden)]
#[derive(Default, Clone)]
pub struct ModuleUserData(
pub HashMap<TypeId, Arc<dyn Any + Send + Sync + UnwindSafe + RefUnwindSafe>>,
);

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> {
private_data: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
private_data: HashMap<TypeId, Box<dyn Any + Send + Sync + UnwindSafe + RefUnwindSafe>>,
user_data: &'scanner ModuleUserData,
}

Expand Down Expand Up @@ -379,13 +390,13 @@ 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.
///
/// 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> {
Expand All @@ -406,25 +417,34 @@ impl<'scanner> ModuleDataMap<'scanner> {
/// Retrieve the private data of a module.
#[must_use]
pub fn get<T: Module + ModuleData + 'static>(&self) -> Option<&T::PrivateData> {
self.private_data
.get(&TypeId::of::<T>())
.and_then(|v| v.downcast_ref())
self.private_data.get(&TypeId::of::<T>()).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.
<dyn Any>::downcast_ref(&**v)
})
}

/// Retrieve a mutable borrow on the private data of a module.
#[must_use]
pub fn get_mut<T: Module + ModuleData + 'static>(&mut self) -> Option<&mut T::PrivateData> {
self.private_data
.get_mut(&TypeId::of::<T>())
.and_then(|v| v.downcast_mut())
self.private_data.get_mut(&TypeId::of::<T>()).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.
<dyn Any>::downcast_mut(&mut **v)
})
}

/// Retrieve the user data of a module.
#[must_use]
pub fn get_user_data<T: Module + ModuleData + 'static>(&self) -> Option<&T::UserData> {
self.user_data
.get(&TypeId::of::<T>())
.and_then(|v| v.downcast_ref())
self.user_data.0.get(&TypeId::of::<T>()).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.
<dyn Any>::downcast_ref(&**v)
})
}
}

Expand Down Expand Up @@ -475,10 +495,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<Value>) -> Option<Value> { 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),
Expand Down Expand Up @@ -872,14 +892,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,
});

Expand Down
13 changes: 9 additions & 4 deletions boreal/src/scanner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ impl Scanner {
}),
scan_params: ScanParams::default(),
external_symbols_values,
module_user_data: HashMap::new(),
module_user_data: ModuleUserData::default(),
}
}

Expand Down Expand Up @@ -330,6 +330,7 @@ impl Scanner {
{
let _r = self
.module_user_data
.0
.insert(TypeId::of::<Module>(), Arc::new(data));
}

Expand Down Expand Up @@ -881,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::*;
Expand Down Expand Up @@ -972,7 +975,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);
Expand Down Expand Up @@ -1514,6 +1517,8 @@ mod tests {
Vec::new(),
BytesPool::default(),
));
test_type_unwind_safe::<Scanner>();

test_type_traits_non_clonable(ScanResult {
matched_rules: Vec::new(),
module_values: Vec::new(),
Expand All @@ -1537,7 +1542,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,
Expand Down
6 changes: 5 additions & 1 deletion boreal/src/test_helpers.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::panic::{RefUnwindSafe, UnwindSafe};

use boreal_parser::hex_string::parse_hex_string;
use boreal_parser::regex::parse_regex;

Expand All @@ -19,9 +21,11 @@ pub fn expr_to_hir(expr: &str) -> Hir {
pub fn test_type_traits<T: Clone + std::fmt::Debug + Send + Sync>(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: std::fmt::Debug + Send + Sync>(t: T) {
let _r = format!("{:?}", &t);
}

pub fn test_type_unwind_safe<T: UnwindSafe + RefUnwindSafe>() {}
Loading