1use crate::analysis::detectors::{
23 DetectionResult, DetectionStatistics, Detector, DetectorConfig, DetectorError, Issue,
24 IssueCategory, IssueSeverity,
25};
26use crate::capture::types::AllocationInfo;
27
28#[derive(Debug, Clone)]
30pub struct OverflowDetectorConfig {
31 pub enable_heap_overflow_detection: bool,
33
34 pub enable_stack_overflow_detection: bool,
36
37 pub enable_integer_overflow_detection: bool,
39
40 pub min_buffer_size_bytes: usize,
42
43 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, }
56 }
57}
58
59#[derive(Debug)]
69pub struct OverflowDetector {
70 config: OverflowDetectorConfig,
71 base_config: DetectorConfig,
72}
73
74impl OverflowDetector {
75 pub fn new(config: OverflowDetectorConfig) -> Self {
90 Self {
91 config,
92 base_config: DetectorConfig::default(),
93 }
94 }
95
96 pub fn overflow_config(&self) -> &OverflowDetectorConfig {
98 &self.config
99 }
100
101 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 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 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 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 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 if alloc.size < self.config.min_buffer_size_bytes {
174 continue;
175 }
176
177 if self.is_buffer_type(alloc) {
179 if let Some(access_tracking) = &alloc.access_tracking {
181 for event in &access_tracking.access_events {
182 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 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 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 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 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 if self.is_stack_allocation(alloc) {
286 if alloc.size > 8 * 1024 {
288 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 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 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 if let Some(type_name) = &alloc.type_name {
351 if self.is_integer_type(type_name) {
352 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 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 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 fn assess_overflow_severity(
432 &self,
433 alloc: &AllocationInfo,
434 event: &crate::capture::types::MemoryAccessEvent,
435 ) -> IssueSeverity {
436 let start_distance = alloc.ptr.saturating_sub(event.address);
438 let end_distance = event.address.saturating_sub(alloc.ptr + alloc.size);
439
440 if start_distance > 1024 || end_distance > 1024 {
442 return IssueSeverity::Critical;
443 }
444
445 if matches!(
447 event.access_type,
448 crate::capture::types::MemoryAccessType::Write
449 ) {
450 return IssueSeverity::High;
451 }
452
453 IssueSeverity::Medium
455 }
456
457 fn assess_stack_overflow_severity(&self, alloc: &AllocationInfo) -> IssueSeverity {
459 if alloc.size > 1024 * 1024 {
461 return IssueSeverity::Critical;
462 }
463
464 if alloc.size > 256 * 1024 {
466 return IssueSeverity::High;
467 }
468
469 IssueSeverity::Medium
471 }
472
473 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 fn is_stack_allocation(&self, alloc: &AllocationInfo) -> bool {
486 alloc.stack_allocation.is_some()
487 }
488
489 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 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, 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, 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)]; 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, 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)]; 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)]; 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, 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}