pub mod types;
pub mod data_race_detector;
pub mod double_free_detector;
pub mod leak_detector;
pub mod lifecycle_detector;
pub mod overflow_detector;
pub mod safety_detector;
pub mod uaf_detector;
pub use types::{
DetectionResult, DetectionStatistics, DetectorConfig, DetectorError, Issue, IssueCategory,
IssueSeverity, Location,
};
pub use data_race_detector::{DataRaceConfig, DataRaceDetector, DataRaceDetectorWithEvents};
pub use double_free_detector::{
DoubleFreeConfig, DoubleFreeDetector, DoubleFreeDetectorWithEvents,
};
pub use leak_detector::{LeakDetector, LeakDetectorConfig};
pub use lifecycle_detector::{LifecycleDetector, LifecycleDetectorConfig};
pub use overflow_detector::{OverflowDetector, OverflowDetectorConfig};
pub use safety_detector::{SafetyDetector, SafetyDetectorConfig};
pub use uaf_detector::{UafDetector, UafDetectorConfig};
use crate::capture::types::AllocationInfo;
use std::fmt;
pub trait Detector: Send + Sync + std::fmt::Debug {
fn name(&self) -> &str;
fn version(&self) -> &str;
fn detect(&self, allocations: &[AllocationInfo]) -> DetectionResult;
fn config(&self) -> &DetectorConfig;
fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError>;
}
#[derive(Debug, Default)]
pub struct DetectorRegistry {
detectors: Vec<Box<dyn Detector>>,
}
impl DetectorRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, detector: Box<dyn Detector>) {
self.detectors.push(detector);
}
pub fn unregister(&mut self, name: &str) -> bool {
let initial_len = self.detectors.len();
self.detectors.retain(|d| d.name() != name);
self.detectors.len() < initial_len
}
pub fn get_detector(&self, name: &str) -> Option<&dyn Detector> {
self.detectors
.iter()
.find(|d| d.name() == name)
.map(|d| d.as_ref())
}
pub fn run_all(&self, allocations: &[AllocationInfo]) -> Vec<DetectionResult> {
self.detectors
.iter()
.map(|detector| detector.detect(allocations))
.collect()
}
pub fn run_detector(
&self,
name: &str,
allocations: &[AllocationInfo],
) -> Option<DetectionResult> {
self.get_detector(name)
.map(|detector| detector.detect(allocations))
}
pub fn detector_names(&self) -> Vec<&str> {
self.detectors.iter().map(|d| d.name()).collect()
}
pub fn len(&self) -> usize {
self.detectors.len()
}
pub fn is_empty(&self) -> bool {
self.detectors.is_empty()
}
}
impl fmt::Display for DetectorRegistry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"DetectorRegistry({} detectors: {})",
self.len(),
self.detector_names().join(", ")
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::capture::types::AllocationInfo;
#[test]
fn test_detector_registry_new() {
let registry = DetectorRegistry::new();
assert!(registry.is_empty());
assert_eq!(registry.len(), 0);
}
#[test]
fn test_detector_registry_register() {
let mut registry = DetectorRegistry::new();
assert_eq!(registry.len(), 0);
#[derive(Debug)]
struct TestDetector {
config: DetectorConfig,
}
impl Detector for TestDetector {
fn name(&self) -> &str {
"TestDetector"
}
fn version(&self) -> &str {
"1.0.0"
}
fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
DetectionResult {
detector_name: self.name().to_string(),
issues: vec![],
statistics: DetectionStatistics::default(),
detection_time_ms: 0,
}
}
fn config(&self) -> &DetectorConfig {
&self.config
}
fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
self.config = config;
Ok(())
}
}
registry.register(Box::new(TestDetector {
config: DetectorConfig::default(),
}));
assert_eq!(registry.len(), 1);
}
#[test]
fn test_detector_registry_unregister() {
let mut registry = DetectorRegistry::new();
#[derive(Debug)]
struct TestDetector {
config: DetectorConfig,
}
impl Detector for TestDetector {
fn name(&self) -> &str {
"TestDetector"
}
fn version(&self) -> &str {
"1.0.0"
}
fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
DetectionResult {
detector_name: self.name().to_string(),
issues: vec![],
statistics: DetectionStatistics::default(),
detection_time_ms: 0,
}
}
fn config(&self) -> &DetectorConfig {
&self.config
}
fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
self.config = config;
Ok(())
}
}
registry.register(Box::new(TestDetector {
config: DetectorConfig::default(),
}));
assert_eq!(registry.len(), 1);
let removed = registry.unregister("TestDetector");
assert!(removed);
assert_eq!(registry.len(), 0);
let not_removed = registry.unregister("NonExistent");
assert!(!not_removed);
}
#[test]
fn test_detector_registry_get_detector() {
let mut registry = DetectorRegistry::new();
#[derive(Debug)]
struct TestDetector {
config: DetectorConfig,
}
impl Detector for TestDetector {
fn name(&self) -> &str {
"TestDetector"
}
fn version(&self) -> &str {
"1.0.0"
}
fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
DetectionResult {
detector_name: self.name().to_string(),
issues: vec![],
statistics: DetectionStatistics::default(),
detection_time_ms: 0,
}
}
fn config(&self) -> &DetectorConfig {
&self.config
}
fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
self.config = config;
Ok(())
}
}
registry.register(Box::new(TestDetector {
config: DetectorConfig::default(),
}));
let detector = registry.get_detector("TestDetector");
assert!(detector.is_some());
assert_eq!(detector.unwrap().name(), "TestDetector");
let not_found = registry.get_detector("NonExistent");
assert!(not_found.is_none());
}
#[test]
fn test_detector_registry_run_all() {
let mut registry = DetectorRegistry::new();
#[derive(Debug)]
struct TestDetector {
config: DetectorConfig,
}
impl Detector for TestDetector {
fn name(&self) -> &str {
"TestDetector"
}
fn version(&self) -> &str {
"1.0.0"
}
fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
DetectionResult {
detector_name: self.name().to_string(),
issues: vec![],
statistics: DetectionStatistics::default(),
detection_time_ms: 0,
}
}
fn config(&self) -> &DetectorConfig {
&self.config
}
fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
self.config = config;
Ok(())
}
}
registry.register(Box::new(TestDetector {
config: DetectorConfig::default(),
}));
let allocations = vec![
AllocationInfo::new(0x1000, 1024),
AllocationInfo::new(0x2000, 2048),
];
let results = registry.run_all(&allocations);
assert_eq!(results.len(), 1);
assert_eq!(results[0].detector_name, "TestDetector");
}
#[test]
fn test_detector_registry_run_detector() {
let mut registry = DetectorRegistry::new();
#[derive(Debug)]
struct TestDetector {
config: DetectorConfig,
}
impl Detector for TestDetector {
fn name(&self) -> &str {
"TestDetector"
}
fn version(&self) -> &str {
"1.0.0"
}
fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
DetectionResult {
detector_name: self.name().to_string(),
issues: vec![],
statistics: DetectionStatistics::default(),
detection_time_ms: 0,
}
}
fn config(&self) -> &DetectorConfig {
&self.config
}
fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
self.config = config;
Ok(())
}
}
registry.register(Box::new(TestDetector {
config: DetectorConfig::default(),
}));
let allocations = vec![AllocationInfo::new(0x1000, 1024)];
let result = registry.run_detector("TestDetector", &allocations);
assert!(result.is_some());
assert_eq!(result.unwrap().detector_name, "TestDetector");
let not_found = registry.run_detector("NonExistent", &allocations);
assert!(not_found.is_none());
}
#[test]
fn test_detector_registry_detector_names() {
let mut registry = DetectorRegistry::new();
#[derive(Debug)]
struct TestDetector1 {
config: DetectorConfig,
}
impl Detector for TestDetector1 {
fn name(&self) -> &str {
"TestDetector1"
}
fn version(&self) -> &str {
"1.0.0"
}
fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
DetectionResult {
detector_name: self.name().to_string(),
issues: vec![],
statistics: DetectionStatistics::default(),
detection_time_ms: 0,
}
}
fn config(&self) -> &DetectorConfig {
&self.config
}
fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
self.config = config;
Ok(())
}
}
#[derive(Debug)]
struct TestDetector2 {
config: DetectorConfig,
}
impl Detector for TestDetector2 {
fn name(&self) -> &str {
"TestDetector2"
}
fn version(&self) -> &str {
"1.0.0"
}
fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
DetectionResult {
detector_name: self.name().to_string(),
issues: vec![],
statistics: DetectionStatistics::default(),
detection_time_ms: 0,
}
}
fn config(&self) -> &DetectorConfig {
&self.config
}
fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
self.config = config;
Ok(())
}
}
registry.register(Box::new(TestDetector1 {
config: DetectorConfig::default(),
}));
registry.register(Box::new(TestDetector2 {
config: DetectorConfig::default(),
}));
let names = registry.detector_names();
assert_eq!(names.len(), 2);
assert!(names.contains(&"TestDetector1"));
assert!(names.contains(&"TestDetector2"));
}
#[test]
fn test_detector_registry_display() {
let registry = DetectorRegistry::new();
let display = format!("{}", registry);
assert!(display.contains("DetectorRegistry"));
assert!(display.contains("0 detectors"));
let mut registry = DetectorRegistry::new();
#[derive(Debug)]
struct TestDetector {
config: DetectorConfig,
}
impl Detector for TestDetector {
fn name(&self) -> &str {
"TestDetector"
}
fn version(&self) -> &str {
"1.0.0"
}
fn detect(&self, _allocations: &[AllocationInfo]) -> DetectionResult {
DetectionResult {
detector_name: self.name().to_string(),
issues: vec![],
statistics: DetectionStatistics::default(),
detection_time_ms: 0,
}
}
fn config(&self) -> &DetectorConfig {
&self.config
}
fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
self.config = config;
Ok(())
}
}
registry.register(Box::new(TestDetector {
config: DetectorConfig::default(),
}));
let display = format!("{}", registry);
assert!(display.contains("DetectorRegistry"));
assert!(display.contains("1 detectors"));
assert!(display.contains("TestDetector"));
}
}