Skip to main content

memscope_rs/analysis/detectors/
overflow_detector.rs

1//! Buffer overflow detection
2//!
3//! This module provides functionality for detecting buffer overflow issues in Rust programs.
4//!
5//! # Example
6//!
7//! ```rust
8//! # use memscope_rs::analysis::detectors::{OverflowDetector, OverflowDetectorConfig, Detector};
9//! # use memscope_rs::capture::types::AllocationInfo;
10//!
11//! let config = OverflowDetectorConfig::default();
12//! let detector = OverflowDetector::new(config);
13//!
14//! let allocations = vec![];
15//! let result = detector.detect(&allocations);
16//!
17//! println!("Found {} buffer overflow issues", result.issues.len());
18//! ```
19
20use crate::analysis::detectors::{
21    DetectionResult, DetectionStatistics, Detector, DetectorConfig, DetectorError, Issue,
22    IssueCategory, IssueSeverity,
23};
24use crate::capture::types::AllocationInfo;
25
26/// Configuration for overflow detector
27#[derive(Debug, Clone)]
28pub struct OverflowDetectorConfig {
29    /// Enable heap overflow detection
30    pub enable_heap_overflow_detection: bool,
31
32    /// Enable stack overflow detection
33    pub enable_stack_overflow_detection: bool,
34
35    /// Enable integer overflow detection
36    pub enable_integer_overflow_detection: bool,
37
38    /// Minimum buffer size in bytes to analyze
39    pub min_buffer_size_bytes: usize,
40
41    /// Maximum array size to analyze
42    pub max_array_size: usize,
43}
44
45impl Default for OverflowDetectorConfig {
46    fn default() -> Self {
47        Self {
48            enable_heap_overflow_detection: true,
49            enable_stack_overflow_detection: true,
50            enable_integer_overflow_detection: true,
51            min_buffer_size_bytes: 64,
52            max_array_size: 1024 * 1024, // 1MB
53        }
54    }
55}
56
57/// Buffer overflow detector
58///
59/// Detects buffer overflow issues by analyzing memory access patterns.
60///
61/// # Detection Methods
62///
63/// - **Heap overflow**: Detects overflows in heap-allocated buffers
64/// - **Stack overflow**: Detects overflows in stack-allocated buffers
65/// - **Integer overflow**: Detects arithmetic operations that exceed type bounds
66#[derive(Debug)]
67pub struct OverflowDetector {
68    config: OverflowDetectorConfig,
69    base_config: DetectorConfig,
70}
71
72impl OverflowDetector {
73    /// Create a new overflow detector
74    ///
75    /// # Arguments
76    ///
77    /// * `config` - Configuration for the overflow detector
78    ///
79    /// # Example
80    ///
81    /// ```rust
82    /// use memscope_rs::analysis::detectors::{OverflowDetector, OverflowDetectorConfig};
83    ///
84    /// let config = OverflowDetectorConfig::default();
85    /// let detector = OverflowDetector::new(config);
86    /// ```
87    pub fn new(config: OverflowDetectorConfig) -> Self {
88        Self {
89            config,
90            base_config: DetectorConfig::default(),
91        }
92    }
93
94    /// Get the overflow detector configuration
95    pub fn overflow_config(&self) -> &OverflowDetectorConfig {
96        &self.config
97    }
98
99    /// Update the overflow detector configuration
100    pub fn update_overflow_config(&mut self, config: OverflowDetectorConfig) {
101        self.config = config;
102    }
103}
104
105impl Detector for OverflowDetector {
106    fn name(&self) -> &str {
107        "OverflowDetector"
108    }
109
110    fn version(&self) -> &str {
111        "1.0.0"
112    }
113
114    fn detect(&self, allocations: &[AllocationInfo]) -> DetectionResult {
115        let start_time = std::time::Instant::now();
116
117        let mut statistics = DetectionStatistics::new();
118        statistics.total_allocations = allocations.len();
119
120        let mut issues = Vec::new();
121
122        // Detect heap buffer overflows
123        if self.config.enable_heap_overflow_detection {
124            let heap_issues = self.detect_buffer_overflow(allocations, &mut statistics);
125            issues.extend(heap_issues);
126        }
127
128        // Detect stack buffer overflows
129        if self.config.enable_stack_overflow_detection {
130            let stack_issues = self.detect_stack_overflow(allocations, &mut statistics);
131            issues.extend(stack_issues);
132        }
133
134        // Detect integer overflows
135        if self.config.enable_integer_overflow_detection {
136            let int_issues = self.detect_integer_overflow(allocations, &mut statistics);
137            issues.extend(int_issues);
138        }
139
140        let detection_time_ms = start_time.elapsed().as_millis() as u64;
141
142        DetectionResult {
143            detector_name: self.name().to_string(),
144            issues,
145            statistics,
146            detection_time_ms,
147        }
148    }
149
150    fn config(&self) -> &DetectorConfig {
151        &self.base_config
152    }
153
154    fn update_config(&mut self, config: DetectorConfig) -> Result<(), DetectorError> {
155        self.base_config = config;
156        Ok(())
157    }
158}
159
160impl OverflowDetector {
161    /// Detect buffer overflows (heap and stack)
162    fn detect_buffer_overflow(
163        &self,
164        allocations: &[AllocationInfo],
165        statistics: &mut DetectionStatistics,
166    ) -> Vec<Issue> {
167        let mut issues = Vec::new();
168
169        for (index, alloc) in allocations.iter().enumerate() {
170            // Skip allocations smaller than minimum buffer size
171            if alloc.size < self.config.min_buffer_size_bytes {
172                continue;
173            }
174
175            // Check if this is a buffer type (array, slice, or vector)
176            if self.is_buffer_type(alloc) {
177                // Check for potential buffer overflow based on access patterns
178                if let Some(access_tracking) = &alloc.access_tracking {
179                    for event in &access_tracking.access_events {
180                        // Check if access is outside buffer bounds
181                        if event.address < alloc.ptr || event.address >= alloc.ptr + alloc.size {
182                            let issue_id = format!("buffer_overflow_{}_{}", index, event.timestamp);
183                            let severity = self.assess_overflow_severity(alloc, event);
184
185                            let issue = Issue::new(
186                                issue_id,
187                                severity,
188                                IssueCategory::Safety,
189                                format!(
190                                    "Buffer overflow detected at 0x{:x}: access at 0x{:x} exceeds buffer bounds [0x{:x}-0x{:x}]",
191                                    alloc.ptr, event.address, alloc.ptr, alloc.ptr + alloc.size
192                                ),
193                            )
194                            .with_allocation_ptr(alloc.ptr)
195                            .with_suggested_fix(
196                                "Use bounds-checked access methods (get, get_mut) or validate indices before access".to_string(),
197                            );
198
199                            issues.push(issue);
200                            statistics.allocations_with_issues += 1;
201                        }
202                    }
203                }
204
205                // Check for suspicious write patterns
206                if let Some(access_tracking) = &alloc.access_tracking {
207                    let write_count = access_tracking
208                        .access_events
209                        .iter()
210                        .filter(|e| {
211                            matches!(
212                                e.access_type,
213                                crate::capture::types::MemoryAccessType::Write
214                            )
215                        })
216                        .count();
217
218                    // High frequency of writes might indicate overflow attempts
219                    if write_count > 100 && write_count > access_tracking.access_events.len() / 2 {
220                        let issue_id = format!("suspicious_write_pattern_{}", index);
221                        let severity = IssueSeverity::Medium;
222
223                        let issue = Issue::new(
224                            issue_id,
225                            severity,
226                            IssueCategory::Safety,
227                            format!(
228                                "Suspicious write pattern detected at 0x{:x}: {}/{} writes",
229                                alloc.ptr,
230                                write_count,
231                                access_tracking.access_events.len()
232                            ),
233                        )
234                        .with_allocation_ptr(alloc.ptr)
235                        .with_suggested_fix(
236                            "Review write operations for potential buffer overflow".to_string(),
237                        );
238
239                        issues.push(issue);
240                        statistics.allocations_with_issues += 1;
241                    }
242                }
243
244                // Check for very large arrays that might cause stack overflow
245                if alloc.size > self.config.max_array_size {
246                    let issue_id = format!("large_array_{}", index);
247                    let severity = IssueSeverity::High;
248
249                    let issue = Issue::new(
250                        issue_id,
251                        severity,
252                        IssueCategory::Safety,
253                        format!(
254                            "Large array detected at 0x{:x}: {} bytes (max: {} bytes)",
255                            alloc.ptr, alloc.size, self.config.max_array_size
256                        ),
257                    )
258                    .with_allocation_ptr(alloc.ptr)
259                    .with_suggested_fix(
260                        "Consider heap allocation (Vec, Box) instead of stack allocation"
261                            .to_string(),
262                    );
263
264                    issues.push(issue);
265                    statistics.allocations_with_issues += 1;
266                }
267            }
268        }
269
270        issues
271    }
272
273    /// Detect stack overflow
274    fn detect_stack_overflow(
275        &self,
276        allocations: &[AllocationInfo],
277        statistics: &mut DetectionStatistics,
278    ) -> Vec<Issue> {
279        let mut issues = Vec::new();
280
281        for (index, alloc) in allocations.iter().enumerate() {
282            // Check if allocation is on stack
283            if self.is_stack_allocation(alloc) {
284                // Check for large stack allocations
285                if alloc.size > 8 * 1024 {
286                    // 8KB threshold
287                    let issue_id = format!("large_stack_allocation_{}", index);
288                    let severity = self.assess_stack_overflow_severity(alloc);
289
290                    let issue = Issue::new(
291                        issue_id,
292                        severity,
293                        IssueCategory::Safety,
294                        format!(
295                            "Large stack allocation detected at 0x{:x}: {} bytes",
296                            alloc.ptr, alloc.size
297                        ),
298                    )
299                    .with_allocation_ptr(alloc.ptr)
300                    .with_suggested_fix(
301                        "Consider heap allocation (Box, Vec) to avoid stack overflow".to_string(),
302                    );
303
304                    issues.push(issue);
305                    statistics.allocations_with_issues += 1;
306                }
307
308                // Check for deep recursion pattern
309                if let Some(lifecycle) = &alloc.lifecycle_tracking {
310                    if lifecycle.lifecycle_events.len() > 100 {
311                        let issue_id = format!("deep_recursion_{}", index);
312                        let severity = IssueSeverity::High;
313
314                        let issue = Issue::new(
315                            issue_id,
316                            severity,
317                            IssueCategory::Safety,
318                            format!(
319                                "Deep recursion pattern detected: {} lifecycle events",
320                                lifecycle.lifecycle_events.len()
321                            ),
322                        )
323                        .with_allocation_ptr(alloc.ptr)
324                        .with_suggested_fix(
325                            "Consider iterative approach or increase stack size".to_string(),
326                        );
327
328                        issues.push(issue);
329                        statistics.allocations_with_issues += 1;
330                    }
331                }
332            }
333        }
334
335        issues
336    }
337
338    /// Detect integer overflow
339    fn detect_integer_overflow(
340        &self,
341        allocations: &[AllocationInfo],
342        statistics: &mut DetectionStatistics,
343    ) -> Vec<Issue> {
344        let mut issues = Vec::new();
345
346        for (index, alloc) in allocations.iter().enumerate() {
347            // Check for potential integer overflow in type usage
348            if let Some(type_name) = &alloc.type_name {
349                if self.is_integer_type(type_name) {
350                    // Check for suspicious large allocations that might overflow size calculations
351                    if alloc.size > usize::MAX / 2 {
352                        let issue_id = format!("integer_overflow_risk_{}", index);
353                        let severity = IssueSeverity::Critical;
354
355                        let issue = Issue::new(
356                            issue_id,
357                            severity,
358                            IssueCategory::Safety,
359                            format!(
360                                "Integer overflow risk detected: allocation size {} bytes exceeds safe range",
361                                alloc.size
362                            ),
363                        )
364                        .with_allocation_ptr(alloc.ptr)
365                            .with_suggested_fix(
366                                "Use checked arithmetic methods (checked_add, checked_mul) or saturating operations".to_string(),
367                            );
368
369                        issues.push(issue);
370                        statistics.allocations_with_issues += 1;
371                    }
372
373                    // Check for allocation near usize::MAX
374                    if alloc.size > usize::MAX - 1024 {
375                        let issue_id = format!("near_max_allocation_{}", index);
376                        let severity = IssueSeverity::Critical;
377
378                        let issue = Issue::new(
379                            issue_id,
380                            severity,
381                            IssueCategory::Safety,
382                            format!("Allocation near usize::MAX detected: {} bytes", alloc.size),
383                        )
384                        .with_allocation_ptr(alloc.ptr)
385                        .with_suggested_fix(
386                            "Reduce allocation size or use checked arithmetic".to_string(),
387                        );
388
389                        issues.push(issue);
390                        statistics.allocations_with_issues += 1;
391                    }
392                }
393            }
394
395            // Check for suspicious size patterns in clone operations
396            if let Some(clone_info) = &alloc.clone_info {
397                if clone_info.clone_count > 0
398                    && alloc.size * clone_info.clone_count > self.config.max_array_size
399                {
400                    let issue_id = format!("clone_overflow_risk_{}", index);
401                    let severity = IssueSeverity::High;
402
403                    let issue = Issue::new(
404                        issue_id,
405                        severity,
406                        IssueCategory::Safety,
407                        format!(
408                            "Clone operation overflow risk: {} clones of {} bytes = {} bytes",
409                            clone_info.clone_count,
410                            alloc.size,
411                            alloc.size * clone_info.clone_count
412                        ),
413                    )
414                    .with_allocation_ptr(alloc.ptr)
415                    .with_suggested_fix(
416                        "Validate clone counts or use Arc for shared ownership".to_string(),
417                    );
418
419                    issues.push(issue);
420                    statistics.allocations_with_issues += 1;
421                }
422            }
423        }
424
425        issues
426    }
427
428    /// Assess buffer overflow severity
429    fn assess_overflow_severity(
430        &self,
431        alloc: &AllocationInfo,
432        event: &crate::capture::types::MemoryAccessEvent,
433    ) -> IssueSeverity {
434        // Calculate distance from buffer bounds
435        let start_distance = alloc.ptr.saturating_sub(event.address);
436        let end_distance = event.address.saturating_sub(alloc.ptr + alloc.size);
437
438        // Critical if far outside bounds (>1KB)
439        if start_distance > 1024 || end_distance > 1024 {
440            return IssueSeverity::Critical;
441        }
442
443        // High if write access outside bounds
444        if matches!(
445            event.access_type,
446            crate::capture::types::MemoryAccessType::Write
447        ) {
448            return IssueSeverity::High;
449        }
450
451        // Medium if read access outside bounds
452        IssueSeverity::Medium
453    }
454
455    /// Assess stack overflow severity
456    fn assess_stack_overflow_severity(&self, alloc: &AllocationInfo) -> IssueSeverity {
457        // Critical if >1MB stack allocation
458        if alloc.size > 1024 * 1024 {
459            return IssueSeverity::Critical;
460        }
461
462        // High if >256KB stack allocation
463        if alloc.size > 256 * 1024 {
464            return IssueSeverity::High;
465        }
466
467        // Medium if >64KB stack allocation
468        IssueSeverity::Medium
469    }
470
471    /// Check if allocation is a buffer type
472    fn is_buffer_type(&self, alloc: &AllocationInfo) -> bool {
473        alloc
474            .type_name
475            .as_ref()
476            .map(|t| {
477                t.contains('[') || t.contains("Vec") || t.contains("slice") || t.contains("array")
478            })
479            .unwrap_or(false)
480    }
481
482    /// Check if allocation is on stack
483    fn is_stack_allocation(&self, alloc: &AllocationInfo) -> bool {
484        alloc.stack_allocation.is_some()
485    }
486
487    /// Check if type is an integer type
488    fn is_integer_type(&self, type_name: &str) -> bool {
489        type_name.contains("i8")
490            || type_name.contains("i16")
491            || type_name.contains("i32")
492            || type_name.contains("i64")
493            || type_name.contains("i128")
494            || type_name.contains("isize")
495            || type_name.contains("u8")
496            || type_name.contains("u16")
497            || type_name.contains("u32")
498            || type_name.contains("u64")
499            || type_name.contains("u128")
500            || type_name.contains("usize")
501    }
502}
503
504#[cfg(test)]
505mod tests {
506    use super::*;
507    use crate::capture::types::AllocationInfo;
508    use crate::capture::types::LocalityMetrics;
509
510    #[test]
511    fn test_overflow_detector_creation() {
512        let config = OverflowDetectorConfig::default();
513        let detector = OverflowDetector::new(config);
514
515        assert_eq!(detector.name(), "OverflowDetector");
516        assert_eq!(detector.version(), "1.0.0");
517    }
518
519    #[test]
520    fn test_overflow_detector_config() {
521        let config = OverflowDetectorConfig {
522            enable_heap_overflow_detection: false,
523            enable_stack_overflow_detection: false,
524            enable_integer_overflow_detection: false,
525            min_buffer_size_bytes: 128,
526            max_array_size: 2048,
527        };
528
529        let detector = OverflowDetector::new(config);
530        let overflow_config = detector.overflow_config();
531
532        assert!(!overflow_config.enable_heap_overflow_detection);
533        assert!(!overflow_config.enable_stack_overflow_detection);
534        assert!(!overflow_config.enable_integer_overflow_detection);
535        assert_eq!(overflow_config.min_buffer_size_bytes, 128);
536        assert_eq!(overflow_config.max_array_size, 2048);
537    }
538
539    #[test]
540    fn test_overflow_detector_detect() {
541        let config = OverflowDetectorConfig::default();
542        let detector = OverflowDetector::new(config);
543
544        let allocations = vec![
545            AllocationInfo::new(0x1000, 1024),
546            AllocationInfo::new(0x2000, 2048),
547        ];
548
549        let result = detector.detect(&allocations);
550
551        assert_eq!(result.detector_name, "OverflowDetector");
552        assert_eq!(result.statistics.total_allocations, 2);
553    }
554
555    #[test]
556    fn test_detect_buffer_overflow() {
557        let config = OverflowDetectorConfig::default();
558        let detector = OverflowDetector::new(config);
559
560        let mut allocations = vec![AllocationInfo::new(0x1000, 1024)];
561        allocations[0].type_name = Some("Vec<i32>".to_string());
562
563        // Create access tracking with out-of-bounds access
564        use crate::capture::types::{
565            AddressRange, CacheAccessInfo, CacheLatencyBreakdown, MemoryAccessEvent,
566            MemoryAccessPerformanceImpact, MemoryAccessTrackingInfo, MemoryAccessType,
567        };
568
569        allocations[0].access_tracking = Some(MemoryAccessTrackingInfo {
570            region_id: 0,
571            address_range: AddressRange {
572                start_address: 0x1000,
573                end_address: 0x1000 + 1024,
574                size: 1024,
575            },
576            access_events: vec![
577                MemoryAccessEvent {
578                    access_type: MemoryAccessType::Read,
579                    timestamp: 100,
580                    address: 0x1000, // Valid access
581                    size: 4,
582                    function_name: "test".to_string(),
583                    latency_ns: 100,
584                    cache_info: CacheAccessInfo {
585                        l1_hit: true,
586                        l2_hit: false,
587                        l3_hit: false,
588                        memory_access: false,
589                        latency_breakdown: CacheLatencyBreakdown {
590                            l1_latency_ns: 1.0,
591                            l2_latency_ns: 5.0,
592                            l3_latency_ns: 20.0,
593                            memory_latency_ns: 100.0,
594                        },
595                    },
596                },
597                MemoryAccessEvent {
598                    access_type: MemoryAccessType::Write,
599                    timestamp: 200,
600                    address: 0x2000, // Out of bounds
601                    size: 4,
602                    function_name: "test".to_string(),
603                    latency_ns: 100,
604                    cache_info: CacheAccessInfo {
605                        l1_hit: true,
606                        l2_hit: false,
607                        l3_hit: false,
608                        memory_access: false,
609                        latency_breakdown: CacheLatencyBreakdown {
610                            l1_latency_ns: 1.0,
611                            l2_latency_ns: 5.0,
612                            l3_latency_ns: 20.0,
613                            memory_latency_ns: 100.0,
614                        },
615                    },
616                },
617            ],
618            access_statistics: crate::capture::types::MemoryAccessStatistics {
619                total_reads: 1,
620                total_writes: 1,
621                read_write_ratio: 1.0,
622                avg_access_frequency: 10.0,
623                peak_access_frequency: 20.0,
624                locality_metrics: LocalityMetrics {
625                    temporal_locality: 0.8,
626                    spatial_locality: 0.9,
627                    sequential_access_percent: 95.0,
628                    random_access_percent: 5.0,
629                    stride_patterns: vec![],
630                },
631                bandwidth_utilization: crate::capture::types::BandwidthUtilization {
632                    peak_bandwidth: 100.0,
633                    avg_bandwidth: 50.0,
634                    efficiency_percent: 80.0,
635                    bottlenecks: vec![],
636                },
637            },
638            access_patterns: vec![],
639            performance_impact: MemoryAccessPerformanceImpact {
640                performance_score: 0.9,
641                cache_efficiency_impact: 0.1,
642                bandwidth_impact: 0.05,
643                pipeline_impact: 0.03,
644                optimization_recommendations: vec![],
645            },
646        });
647
648        let issues = detector.detect_buffer_overflow(&allocations, &mut DetectionStatistics::new());
649
650        assert!(!issues.is_empty());
651        assert!(issues
652            .iter()
653            .any(|i| i.description.contains("Buffer overflow detected")));
654    }
655
656    #[test]
657    fn test_detect_large_stack_allocation() {
658        let config = OverflowDetectorConfig::default();
659        let detector = OverflowDetector::new(config);
660
661        let mut allocations = vec![AllocationInfo::new(0x1000, 16 * 1024)]; // 16KB stack allocation
662        allocations[0].type_name = Some("[u8; 16384]".to_string());
663
664        use crate::capture::types::{ScopeType, StackAllocationInfo, StackScopeInfo};
665        allocations[0].stack_allocation = Some(StackAllocationInfo {
666            frame_id: 0,
667            var_name: "buffer".to_string(),
668            stack_offset: 1024,
669            size: 16 * 1024,
670            function_name: "test_function".to_string(),
671            stack_depth: 1,
672            scope_info: StackScopeInfo {
673                scope_type: ScopeType::Function,
674                start_line: Some(10),
675                end_line: Some(50),
676                parent_scope: None,
677                nesting_level: 0,
678            },
679        });
680
681        let issues = detector.detect_stack_overflow(&allocations, &mut DetectionStatistics::new());
682
683        assert!(!issues.is_empty());
684        assert!(issues
685            .iter()
686            .any(|i| i.description.contains("Large stack allocation")));
687    }
688
689    #[test]
690    fn test_detect_integer_overflow_risk() {
691        let config = OverflowDetectorConfig::default();
692        let detector = OverflowDetector::new(config);
693
694        let mut allocations = vec![AllocationInfo::new(0x1000, usize::MAX / 2 + 1)];
695        allocations[0].type_name = Some("Vec<i32>".to_string());
696
697        let issues =
698            detector.detect_integer_overflow(&allocations, &mut DetectionStatistics::new());
699
700        assert!(!issues.is_empty());
701        assert!(issues
702            .iter()
703            .any(|i| i.description.contains("Integer overflow risk")));
704    }
705
706    #[test]
707    fn test_is_buffer_type() {
708        let config = OverflowDetectorConfig::default();
709        let detector = OverflowDetector::new(config);
710
711        let mut alloc = AllocationInfo::new(0x1000, 1024);
712        alloc.type_name = Some("Vec<i32>".to_string());
713        assert!(detector.is_buffer_type(&alloc));
714
715        alloc.type_name = Some("[u8; 1024]".to_string());
716        assert!(detector.is_buffer_type(&alloc));
717
718        alloc.type_name = Some("Box<i32>".to_string());
719        assert!(!detector.is_buffer_type(&alloc));
720    }
721
722    #[test]
723    fn test_is_integer_type() {
724        let config = OverflowDetectorConfig::default();
725        let detector = OverflowDetector::new(config);
726
727        assert!(detector.is_integer_type("i32"));
728        assert!(detector.is_integer_type("u64"));
729        assert!(detector.is_integer_type("usize"));
730        assert!(!detector.is_integer_type("f32"));
731        assert!(!detector.is_integer_type("String"));
732    }
733
734    #[test]
735    fn test_overflow_assessment_severity() {
736        let config = OverflowDetectorConfig::default();
737        let detector = OverflowDetector::new(config);
738
739        let alloc = AllocationInfo::new(0x1000, 1024);
740
741        use crate::capture::types::{
742            CacheAccessInfo, CacheLatencyBreakdown, MemoryAccessEvent, MemoryAccessType,
743        };
744        let event = MemoryAccessEvent {
745            access_type: MemoryAccessType::Write,
746            timestamp: 100,
747            address: 0x3000, // Far outside bounds
748            size: 4,
749            function_name: "test".to_string(),
750            latency_ns: 100,
751            cache_info: CacheAccessInfo {
752                l1_hit: true,
753                l2_hit: false,
754                l3_hit: false,
755                memory_access: false,
756                latency_breakdown: CacheLatencyBreakdown {
757                    l1_latency_ns: 1.0,
758                    l2_latency_ns: 5.0,
759                    l3_latency_ns: 20.0,
760                    memory_latency_ns: 100.0,
761                },
762            },
763        };
764
765        let severity = detector.assess_overflow_severity(&alloc, &event);
766        assert_eq!(severity, IssueSeverity::Critical);
767    }
768
769    #[test]
770    fn test_overflow_detector_disabled() {
771        let config = OverflowDetectorConfig {
772            enable_heap_overflow_detection: false,
773            enable_stack_overflow_detection: false,
774            enable_integer_overflow_detection: false,
775            min_buffer_size_bytes: 64,
776            max_array_size: 1024 * 1024,
777        };
778        let detector = OverflowDetector::new(config);
779
780        let allocations = vec![AllocationInfo::new(0x1000, 1024)];
781        let result = detector.detect(&allocations);
782
783        assert_eq!(result.issues.len(), 0);
784    }
785
786    #[test]
787    fn test_detect_large_array() {
788        let config = OverflowDetectorConfig::default();
789        let detector = OverflowDetector::new(config);
790
791        let mut allocations = vec![AllocationInfo::new(0x1000, 2 * 1024 * 1024)]; // 2MB array
792        allocations[0].type_name = Some("Vec<i32>".to_string());
793
794        let issues = detector.detect_buffer_overflow(&allocations, &mut DetectionStatistics::new());
795
796        assert!(!issues.is_empty());
797        assert!(issues
798            .iter()
799            .any(|i| i.description.contains("Large array detected")));
800    }
801
802    #[test]
803    fn test_clone_overflow_risk() {
804        let config = OverflowDetectorConfig::default();
805        let detector = OverflowDetector::new(config);
806
807        let mut allocations = vec![AllocationInfo::new(0x1000, 512 * 1024)]; // 512KB
808        allocations[0].type_name = Some("Vec<i32>".to_string());
809
810        use crate::capture::types::CloneInfo;
811        allocations[0].clone_info = Some(CloneInfo {
812            clone_count: 3, // 3 * 512KB = 1.5MB > max_array_size
813            is_clone: true,
814            original_ptr: Some(0x1000),
815            _source: None,
816            _confidence: None,
817        });
818
819        let issues =
820            detector.detect_integer_overflow(&allocations, &mut DetectionStatistics::new());
821
822        assert!(!issues.is_empty());
823        assert!(issues
824            .iter()
825            .any(|i| i.description.contains("Clone operation overflow risk")));
826    }
827}