1use crate::analysis::detectors::{
21 DetectionResult, DetectionStatistics, Detector, DetectorConfig, DetectorError, Issue,
22 IssueCategory, IssueSeverity,
23};
24use crate::capture::types::AllocationInfo;
25
26#[derive(Debug, Clone)]
28pub struct OverflowDetectorConfig {
29 pub enable_heap_overflow_detection: bool,
31
32 pub enable_stack_overflow_detection: bool,
34
35 pub enable_integer_overflow_detection: bool,
37
38 pub min_buffer_size_bytes: usize,
40
41 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, }
54 }
55}
56
57#[derive(Debug)]
67pub struct OverflowDetector {
68 config: OverflowDetectorConfig,
69 base_config: DetectorConfig,
70}
71
72impl OverflowDetector {
73 pub fn new(config: OverflowDetectorConfig) -> Self {
88 Self {
89 config,
90 base_config: DetectorConfig::default(),
91 }
92 }
93
94 pub fn overflow_config(&self) -> &OverflowDetectorConfig {
96 &self.config
97 }
98
99 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 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 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 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 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 if alloc.size < self.config.min_buffer_size_bytes {
172 continue;
173 }
174
175 if self.is_buffer_type(alloc) {
177 if let Some(access_tracking) = &alloc.access_tracking {
179 for event in &access_tracking.access_events {
180 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 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 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 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 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 if self.is_stack_allocation(alloc) {
284 if alloc.size > 8 * 1024 {
286 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 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 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 if let Some(type_name) = &alloc.type_name {
349 if self.is_integer_type(type_name) {
350 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 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 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 fn assess_overflow_severity(
430 &self,
431 alloc: &AllocationInfo,
432 event: &crate::capture::types::MemoryAccessEvent,
433 ) -> IssueSeverity {
434 let start_distance = alloc.ptr.saturating_sub(event.address);
436 let end_distance = event.address.saturating_sub(alloc.ptr + alloc.size);
437
438 if start_distance > 1024 || end_distance > 1024 {
440 return IssueSeverity::Critical;
441 }
442
443 if matches!(
445 event.access_type,
446 crate::capture::types::MemoryAccessType::Write
447 ) {
448 return IssueSeverity::High;
449 }
450
451 IssueSeverity::Medium
453 }
454
455 fn assess_stack_overflow_severity(&self, alloc: &AllocationInfo) -> IssueSeverity {
457 if alloc.size > 1024 * 1024 {
459 return IssueSeverity::Critical;
460 }
461
462 if alloc.size > 256 * 1024 {
464 return IssueSeverity::High;
465 }
466
467 IssueSeverity::Medium
469 }
470
471 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 fn is_stack_allocation(&self, alloc: &AllocationInfo) -> bool {
484 alloc.stack_allocation.is_some()
485 }
486
487 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 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, 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, 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)]; 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, 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)]; 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)]; 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, 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}