Skip to main content

memscope_rs/analyzer/
detect.rs

1//! Detection analysis module.
2
3use crate::analyzer::report::{LeakInfo, LeakReport, SafetyReport, UafReport};
4use crate::snapshot::ActiveAllocation;
5use crate::view::MemoryView;
6use tracing::{debug, info, warn};
7
8/// Detection analysis module.
9///
10/// Provides leak, UAF, and safety detection.
11pub struct DetectionAnalysis {
12    view: MemoryView,
13}
14
15impl DetectionAnalysis {
16    /// Create from view.
17    pub fn from_view(view: &MemoryView) -> Self {
18        debug!("Creating DetectionAnalysis with {} allocations", view.len());
19        Self { view: view.clone() }
20    }
21
22    /// Detect memory leaks.
23    ///
24    /// A leak is an allocation that was never deallocated.
25    ///
26    /// # Filter Logic
27    ///
28    /// Uses `ptr.is_some()` to filter allocations because:
29    /// - `ptr.is_some()`: Heap allocation with a valid pointer → potential leak
30    /// - `ptr.is_none()`: Container/Value type (metadata, not heap memory) → not a leak
31    ///
32    /// Container/Value types represent stack-allocated container metadata
33    /// (e.g., the Vec struct itself), not heap allocations. Only heap
34    /// allocations can be memory leaks.
35    pub fn leaks(&self) -> LeakReport {
36        let allocations = self.view.allocations();
37        let leaked: Vec<&ActiveAllocation> = allocations
38            .into_iter()
39            .filter(|a| a.ptr.is_some())
40            .collect();
41
42        let leak_count = leaked.len();
43        let total_bytes: usize = leaked.iter().map(|a| a.size).sum();
44
45        if leak_count > 0 {
46            info!(
47                "Leak detection: found {} leaks totaling {} bytes",
48                leak_count, total_bytes
49            );
50        } else {
51            debug!("Leak detection: no leaks found");
52        }
53
54        LeakReport {
55            leak_count,
56            total_leaked_bytes: total_bytes,
57            leaked_allocations: leaked.into_iter().map(LeakInfo::from).collect(),
58        }
59    }
60
61    /// Detect use-after-free.
62    ///
63    /// Analyzes event stream for potential UAF patterns by tracking
64    /// deallocations and checking for subsequent accesses to the same address.
65    ///
66    /// Note:
67    /// - Reallocations are NOT flagged as UAF because realloc on a
68    ///   deallocated pointer is valid behavior (the allocator may reuse the address).
69    /// - Metadata events are NOT flagged as UAF because they represent
70    ///   type/container information, not actual memory access.
71    /// - Only actual memory access events (read/write operations) on freed
72    ///   memory would indicate UAF, but these are not currently tracked.
73    ///
74    /// Returns an empty report as UAF detection requires runtime memory
75    /// access tracking which is not implemented in the current architecture.
76    pub fn uaf(&self) -> UafReport {
77        // UAF detection requires tracking actual memory accesses (read/write)
78        // on freed pointers. The current event system does not track these
79        // operations. Realloc and Metadata events are not UAF indicators:
80        // - Realloc: valid allocator behavior, may reuse addresses
81        // - Metadata: type information only, no memory access
82        //
83        // For proper UAF detection, consider using sanitizers like ASAN
84        // or instrumenting memory access at runtime.
85        debug!("UAF detection: not implemented, returning empty report");
86        UafReport::empty()
87    }
88
89    /// Analyze memory safety.
90    ///
91    /// Checks for common safety issues.
92    pub fn safety(&self) -> SafetyReport {
93        let allocations = self.view.allocations();
94        let report = SafetyReport::from_allocations(&allocations);
95        if report.issue_count > 0 {
96            warn!(
97                "Safety analysis: found {} potential safety issues",
98                report.issue_count
99            );
100        }
101        report
102    }
103
104    /// Get detection summary.
105    pub fn summary(&self) -> DetectionSummary {
106        let leaks = self.leaks();
107        let safety = self.safety();
108        DetectionSummary {
109            leak_count: leaks.leak_count,
110            leaked_bytes: leaks.total_leaked_bytes,
111            uaf_count: 0, // UAF detection not implemented
112            safety_issues: safety.issue_count,
113        }
114    }
115}
116
117/// Detection summary.
118#[derive(Debug, Clone)]
119pub struct DetectionSummary {
120    /// Number of leaks
121    pub leak_count: usize,
122    /// Total leaked bytes
123    pub leaked_bytes: usize,
124    /// Number of UAF issues
125    pub uaf_count: usize,
126    /// Number of safety issues
127    pub safety_issues: usize,
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133    use crate::event_store::MemoryEvent;
134
135    #[test]
136    fn test_leak_detection() {
137        let events = vec![
138            MemoryEvent::allocate(0x1000, 64, 1),
139            MemoryEvent::allocate(0x2000, 128, 2),
140            MemoryEvent::deallocate(0x1000, 64, 1),
141        ];
142        let view = MemoryView::from_events(events);
143        let analysis = DetectionAnalysis::from_view(&view);
144        let leaks = analysis.leaks();
145        assert_eq!(leaks.leak_count, 1);
146        assert_eq!(leaks.total_leaked_bytes, 128);
147    }
148
149    #[test]
150    fn test_no_leaks() {
151        let events = vec![
152            MemoryEvent::allocate(0x1000, 64, 1),
153            MemoryEvent::deallocate(0x1000, 64, 1),
154        ];
155        let view = MemoryView::from_events(events);
156        let analysis = DetectionAnalysis::from_view(&view);
157        let leaks = analysis.leaks();
158        assert_eq!(leaks.leak_count, 0);
159    }
160
161    #[test]
162    fn test_uaf_returns_empty() {
163        // UAF detection is not implemented, should return empty report
164        let events = vec![
165            MemoryEvent::allocate(0x1000, 64, 1),
166            MemoryEvent::deallocate(0x1000, 64, 1),
167        ];
168        let view = MemoryView::from_events(events);
169        let analysis = DetectionAnalysis::from_view(&view);
170        let uaf = analysis.uaf();
171        assert_eq!(uaf.uaf_count, 0);
172    }
173}