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