1use crate::analysis::unsafe_ffi_tracker::StackFrame;
7use crate::core::types::{TrackingError, TrackingResult};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::sync::{Arc, Mutex};
11use std::time::{SystemTime, UNIX_EPOCH};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct MemoryPassport {
16 pub passport_id: String,
18 pub allocation_ptr: usize,
20 pub size_bytes: usize,
22 pub status_at_shutdown: PassportStatus,
24 pub lifecycle_events: Vec<PassportEvent>,
26 pub created_at: u64,
28 pub updated_at: u64,
30 pub metadata: HashMap<String, String>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
36pub enum PassportStatus {
37 FreedByRust,
39 HandoverToFfi,
41 FreedByForeign,
43 ReclaimedByRust,
45 InForeignCustody,
47 Unknown,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct PassportEvent {
54 pub event_type: PassportEventType,
56 pub timestamp: u64,
58 pub context: String,
60 pub call_stack: Vec<StackFrame>,
62 pub metadata: HashMap<String, String>,
64 pub sequence_number: u32,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
70pub enum PassportEventType {
71 AllocatedInRust,
73 HandoverToFfi,
75 FreedByForeign,
77 ReclaimedByRust,
79 BoundaryAccess,
81 OwnershipTransfer,
83 ValidationCheck,
85 CorruptionDetected,
87}
88
89pub struct MemoryPassportTracker {
91 passports: Arc<Mutex<HashMap<usize, MemoryPassport>>>,
93 sequence_counter: Arc<Mutex<u32>>,
95 event_sequence: Arc<Mutex<u32>>,
97 config: PassportTrackerConfig,
99 stats: Arc<Mutex<PassportTrackerStats>>,
101}
102
103#[derive(Debug, Clone)]
105pub struct PassportTrackerConfig {
106 pub detailed_logging: bool,
108 pub max_events_per_passport: usize,
110 pub enable_leak_detection: bool,
112 pub enable_validation: bool,
114 pub max_passports: usize,
116}
117
118impl Default for PassportTrackerConfig {
119 fn default() -> Self {
120 Self {
121 detailed_logging: true,
122 max_events_per_passport: 100,
123 enable_leak_detection: true,
124 enable_validation: true,
125 max_passports: 10000,
126 }
127 }
128}
129
130#[derive(Debug, Clone, Default, Serialize, Deserialize)]
132pub struct PassportTrackerStats {
133 pub total_passports_created: usize,
135 pub active_passports: usize,
137 pub passports_by_status: HashMap<String, usize>,
139 pub total_events_recorded: usize,
141 pub leaks_detected: usize,
143 pub validation_failures: usize,
145 pub tracker_start_time: u64,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct LeakDetectionResult {
152 pub leaked_passports: Vec<String>,
154 pub total_leaks: usize,
156 pub leak_details: Vec<LeakDetail>,
158 pub detected_at: u64,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct LeakDetail {
165 pub passport_id: String,
167 pub memory_address: usize,
169 pub size_bytes: usize,
171 pub last_context: String,
173 pub time_since_last_event: u64,
175 pub lifecycle_summary: String,
177}
178
179impl MemoryPassportTracker {
180 pub fn new(config: PassportTrackerConfig) -> Self {
182 tracing::info!("๐ Initializing Memory Passport Tracker");
183 tracing::info!(" โข Detailed logging: {}", config.detailed_logging);
184 tracing::info!(
185 " โข Max events per passport: {}",
186 config.max_events_per_passport
187 );
188 tracing::info!(" โข Leak detection: {}", config.enable_leak_detection);
189 tracing::info!(" โข Validation: {}", config.enable_validation);
190
191 Self {
192 passports: Arc::new(Mutex::new(HashMap::new())),
193 sequence_counter: Arc::new(Mutex::new(0)),
194 event_sequence: Arc::new(Mutex::new(0)),
195 config,
196 stats: Arc::new(Mutex::new(PassportTrackerStats {
197 tracker_start_time: SystemTime::now()
198 .duration_since(UNIX_EPOCH)
199 .unwrap_or_default()
200 .as_secs(),
201 ..Default::default()
202 })),
203 }
204 }
205
206 pub fn create_passport(
208 &self,
209 allocation_ptr: usize,
210 size_bytes: usize,
211 initial_context: String,
212 ) -> TrackingResult<String> {
213 let passport_id = self.generate_passport_id(allocation_ptr)?;
214 let current_time = SystemTime::now()
215 .duration_since(UNIX_EPOCH)
216 .unwrap_or_default()
217 .as_secs();
218
219 let initial_event = PassportEvent {
221 event_type: PassportEventType::AllocatedInRust,
222 timestamp: current_time,
223 context: initial_context,
224 call_stack: self.capture_call_stack()?,
225 metadata: HashMap::new(),
226 sequence_number: self.get_next_event_sequence(),
227 };
228
229 let passport = MemoryPassport {
230 passport_id: passport_id.clone(),
231 allocation_ptr,
232 size_bytes,
233 status_at_shutdown: PassportStatus::Unknown,
234 lifecycle_events: vec![initial_event],
235 created_at: current_time,
236 updated_at: current_time,
237 metadata: HashMap::new(),
238 };
239
240 if let Ok(mut passports) = self.passports.lock() {
242 if passports.len() >= self.config.max_passports {
244 return Err(TrackingError::ResourceExhausted(
245 "Maximum passport limit reached".to_string(),
246 ));
247 }
248 passports.insert(allocation_ptr, passport);
249 } else {
250 return Err(TrackingError::LockContention(
251 "Failed to lock passports".to_string(),
252 ));
253 }
254
255 self.update_stats_passport_created();
257
258 if self.config.detailed_logging {
259 tracing::info!(
260 "๐ Created passport: {} for 0x{:x} ({} bytes)",
261 passport_id,
262 allocation_ptr,
263 size_bytes
264 );
265 }
266
267 Ok(passport_id)
268 }
269
270 pub fn record_handover_to_ffi(
272 &self,
273 allocation_ptr: usize,
274 ffi_context: String,
275 function_name: String,
276 ) -> TrackingResult<()> {
277 let mut metadata = HashMap::new();
278 metadata.insert("ffi_function".to_string(), function_name);
279
280 self.record_passport_event(
281 allocation_ptr,
282 PassportEventType::HandoverToFfi,
283 ffi_context,
284 metadata,
285 )
286 }
287
288 pub fn record_freed_by_foreign(
290 &self,
291 allocation_ptr: usize,
292 foreign_context: String,
293 free_function: String,
294 ) -> TrackingResult<()> {
295 let mut metadata = HashMap::new();
296 metadata.insert("free_function".to_string(), free_function);
297
298 self.record_passport_event(
299 allocation_ptr,
300 PassportEventType::FreedByForeign,
301 foreign_context,
302 metadata,
303 )
304 }
305
306 pub fn record_reclaimed_by_rust(
308 &self,
309 allocation_ptr: usize,
310 rust_context: String,
311 reclaim_reason: String,
312 ) -> TrackingResult<()> {
313 let mut metadata = HashMap::new();
314 metadata.insert("reclaim_reason".to_string(), reclaim_reason);
315
316 self.record_passport_event(
317 allocation_ptr,
318 PassportEventType::ReclaimedByRust,
319 rust_context,
320 metadata,
321 )
322 }
323
324 pub fn record_passport_event(
326 &self,
327 allocation_ptr: usize,
328 event_type: PassportEventType,
329 context: String,
330 metadata: HashMap<String, String>,
331 ) -> TrackingResult<()> {
332 let current_time = SystemTime::now()
333 .duration_since(UNIX_EPOCH)
334 .unwrap_or_default()
335 .as_secs();
336
337 let event = PassportEvent {
338 event_type: event_type.clone(),
339 timestamp: current_time,
340 context: context.clone(),
341 call_stack: self.capture_call_stack()?,
342 metadata,
343 sequence_number: self.get_next_event_sequence(),
344 };
345
346 if let Ok(mut passports) = self.passports.lock() {
347 if let Some(passport) = passports.get_mut(&allocation_ptr) {
348 if passport.lifecycle_events.len() >= self.config.max_events_per_passport {
350 passport.lifecycle_events.remove(0); }
352
353 passport.lifecycle_events.push(event);
354 passport.updated_at = current_time;
355
356 self.update_stats_event_recorded();
358
359 if self.config.detailed_logging {
360 tracing::info!(
361 "๐ Recorded {:?} event for passport 0x{:x} in context: {}",
362 event_type,
363 allocation_ptr,
364 context
365 );
366 }
367 } else {
368 return Err(TrackingError::InvalidPointer(format!(
369 "No passport found for 0x{allocation_ptr:x}"
370 )));
371 }
372 } else {
373 return Err(TrackingError::LockContention(
374 "Failed to lock passports".to_string(),
375 ));
376 }
377
378 Ok(())
379 }
380
381 pub fn detect_leaks_at_shutdown(&self) -> LeakDetectionResult {
383 if !self.config.enable_leak_detection {
384 return LeakDetectionResult {
385 leaked_passports: Vec::new(),
386 total_leaks: 0,
387 leak_details: Vec::new(),
388 detected_at: SystemTime::now()
389 .duration_since(UNIX_EPOCH)
390 .unwrap_or_default()
391 .as_secs(),
392 };
393 }
394
395 let mut leaked_passports = Vec::new();
396 let mut leak_details = Vec::new();
397 let current_time = SystemTime::now()
398 .duration_since(UNIX_EPOCH)
399 .unwrap_or_default()
400 .as_secs();
401
402 if let Ok(mut passports) = self.passports.lock() {
403 for (ptr, passport) in passports.iter_mut() {
404 let final_status = self.determine_final_status(&passport.lifecycle_events);
406 passport.status_at_shutdown = final_status.clone();
407
408 if final_status == PassportStatus::InForeignCustody {
410 leaked_passports.push(passport.passport_id.clone());
411
412 let last_event = passport.lifecycle_events.last();
414 let last_context = last_event
415 .map(|e| e.context.clone())
416 .unwrap_or_else(|| "unknown".to_string());
417 let time_since_last = last_event
418 .map(|e| current_time.saturating_sub(e.timestamp))
419 .unwrap_or(0);
420
421 let lifecycle_summary =
422 self.create_lifecycle_summary(&passport.lifecycle_events);
423
424 leak_details.push(LeakDetail {
425 passport_id: passport.passport_id.clone(),
426 memory_address: *ptr,
427 size_bytes: passport.size_bytes,
428 last_context,
429 time_since_last_event: time_since_last,
430 lifecycle_summary,
431 });
432
433 tracing::warn!("๐จ MEMORY LEAK DETECTED: Passport {} (0x{:x}, {} bytes) in foreign custody",
434 passport.passport_id, ptr, passport.size_bytes);
435 }
436 }
437
438 if let Ok(mut stats) = self.stats.lock() {
440 stats.leaks_detected = leaked_passports.len();
441
442 stats.passports_by_status.clear();
444 for passport in passports.values() {
445 let status_key = format!("{:?}", passport.status_at_shutdown);
446 *stats.passports_by_status.entry(status_key).or_insert(0) += 1;
447 }
448 }
449 }
450
451 let total_leaks = leaked_passports.len();
452
453 tracing::info!(
454 "๐ Leak detection complete: {} leaks detected out of {} passports",
455 total_leaks,
456 self.get_passport_count()
457 );
458
459 LeakDetectionResult {
460 leaked_passports,
461 total_leaks,
462 leak_details,
463 detected_at: current_time,
464 }
465 }
466
467 pub fn get_passport(&self, allocation_ptr: usize) -> Option<MemoryPassport> {
469 self.passports.lock().ok()?.get(&allocation_ptr).cloned()
470 }
471
472 pub fn get_all_passports(&self) -> HashMap<usize, MemoryPassport> {
474 self.passports
475 .lock()
476 .unwrap_or_else(|_| {
477 tracing::error!("Failed to lock passports for reading");
478 std::process::exit(1);
479 })
480 .clone()
481 }
482
483 pub fn get_passports_by_status(&self, status: PassportStatus) -> Vec<MemoryPassport> {
485 if let Ok(passports) = self.passports.lock() {
486 passports
487 .values()
488 .filter(|p| p.status_at_shutdown == status)
489 .cloned()
490 .collect()
491 } else {
492 Vec::new()
493 }
494 }
495
496 pub fn get_stats(&self) -> PassportTrackerStats {
498 self.stats
499 .lock()
500 .unwrap_or_else(|_| {
501 tracing::error!("Failed to lock stats");
502 std::process::exit(1);
503 })
504 .clone()
505 }
506
507 pub fn validate_passport(&self, allocation_ptr: usize) -> TrackingResult<bool> {
509 if !self.config.enable_validation {
510 return Ok(true);
511 }
512
513 if let Ok(passports) = self.passports.lock() {
514 if let Some(passport) = passports.get(&allocation_ptr) {
515 let mut last_sequence = 0;
517 for event in &passport.lifecycle_events {
518 if event.sequence_number <= last_sequence {
519 self.update_stats_validation_failure();
520 return Ok(false);
521 }
522 last_sequence = event.sequence_number;
523 }
524
525 let mut last_timestamp = 0;
527 for event in &passport.lifecycle_events {
528 if event.timestamp < last_timestamp {
529 self.update_stats_validation_failure();
530 return Ok(false);
531 }
532 last_timestamp = event.timestamp;
533 }
534
535 Ok(true)
536 } else {
537 Err(TrackingError::InvalidPointer(format!(
538 "No passport found for 0x{allocation_ptr:x}",
539 )))
540 }
541 } else {
542 Err(TrackingError::LockContention(
543 "Failed to lock passports".to_string(),
544 ))
545 }
546 }
547
548 pub fn clear_all_passports(&self) {
550 if let Ok(mut passports) = self.passports.lock() {
551 passports.clear();
552 }
553
554 if let Ok(mut stats) = self.stats.lock() {
555 *stats = PassportTrackerStats {
556 tracker_start_time: stats.tracker_start_time,
557 ..Default::default()
558 };
559 }
560
561 tracing::info!("๐งน Cleared all passports");
562 }
563
564 fn generate_passport_id(&self, allocation_ptr: usize) -> TrackingResult<String> {
567 let sequence = if let Ok(mut counter) = self.sequence_counter.lock() {
568 *counter += 1;
569 *counter
570 } else {
571 return Err(TrackingError::LockContention(
572 "Failed to lock sequence counter".to_string(),
573 ));
574 };
575
576 let timestamp = SystemTime::now()
577 .duration_since(UNIX_EPOCH)
578 .unwrap_or_default()
579 .as_nanos();
580
581 Ok(format!(
582 "passport_{:x}_{:08x}_{}",
583 allocation_ptr,
584 sequence,
585 timestamp % 1000000
586 ))
587 }
588
589 fn get_next_event_sequence(&self) -> u32 {
590 if let Ok(mut counter) = self.event_sequence.lock() {
591 *counter += 1;
592 *counter
593 } else {
594 tracing::error!("Failed to lock event sequence counter");
595 0
596 }
597 }
598
599 fn capture_call_stack(&self) -> TrackingResult<Vec<StackFrame>> {
600 Ok(vec![StackFrame {
603 function_name: "memory_passport_tracker".to_string(),
604 file_name: Some("src/analysis/memory_passport_tracker.rs".to_string()),
605 line_number: Some(1),
606 is_unsafe: false,
607 }])
608 }
609
610 fn determine_final_status(&self, events: &[PassportEvent]) -> PassportStatus {
611 let mut has_handover = false;
612 let mut has_reclaim = false;
613 let mut has_foreign_free = false;
614
615 for event in events {
617 match event.event_type {
618 PassportEventType::HandoverToFfi => has_handover = true,
619 PassportEventType::ReclaimedByRust => {
620 has_reclaim = true;
621 has_handover = false; }
623 PassportEventType::FreedByForeign => {
624 has_foreign_free = true;
625 has_handover = false; }
627 _ => {}
628 }
629 }
630
631 if has_handover && !has_reclaim && !has_foreign_free {
633 PassportStatus::InForeignCustody } else if has_foreign_free {
635 PassportStatus::FreedByForeign
636 } else if has_reclaim {
637 PassportStatus::ReclaimedByRust
638 } else if has_handover {
639 PassportStatus::HandoverToFfi
640 } else {
641 PassportStatus::FreedByRust
642 }
643 }
644
645 fn create_lifecycle_summary(&self, events: &[PassportEvent]) -> String {
646 let event_types: Vec<String> = events
647 .iter()
648 .map(|e| format!("{:?}", e.event_type))
649 .collect();
650
651 if event_types.is_empty() {
652 "No events recorded".to_string()
653 } else {
654 format!("Events: {}", event_types.join(" -> "))
655 }
656 }
657
658 fn get_passport_count(&self) -> usize {
659 self.passports.lock().map(|p| p.len()).unwrap_or(0)
660 }
661
662 fn update_stats_passport_created(&self) {
663 if let Ok(mut stats) = self.stats.lock() {
664 stats.total_passports_created += 1;
665 stats.active_passports = self.get_passport_count();
666 }
667 }
668
669 fn update_stats_event_recorded(&self) {
670 if let Ok(mut stats) = self.stats.lock() {
671 stats.total_events_recorded += 1;
672 }
673 }
674
675 fn update_stats_validation_failure(&self) {
676 if let Ok(mut stats) = self.stats.lock() {
677 stats.validation_failures += 1;
678 }
679 }
680}
681
682impl Default for MemoryPassportTracker {
683 fn default() -> Self {
684 Self::new(PassportTrackerConfig::default())
685 }
686}
687
688static GLOBAL_PASSPORT_TRACKER: std::sync::OnceLock<Arc<MemoryPassportTracker>> =
690 std::sync::OnceLock::new();
691
692pub fn get_global_passport_tracker() -> Arc<MemoryPassportTracker> {
694 GLOBAL_PASSPORT_TRACKER
695 .get_or_init(|| Arc::new(MemoryPassportTracker::new(PassportTrackerConfig::default())))
696 .clone()
697}
698
699pub fn initialize_global_passport_tracker(
701 config: PassportTrackerConfig,
702) -> Arc<MemoryPassportTracker> {
703 let tracker = Arc::new(MemoryPassportTracker::new(config));
704 if GLOBAL_PASSPORT_TRACKER.set(tracker.clone()).is_err() {
705 tracing::warn!("Global passport tracker already initialized");
706 }
707 tracker
708}
709
710#[cfg(test)]
711mod tests {
712 use super::*;
713
714 #[test]
715 fn test_passport_creation() {
716 let tracker = MemoryPassportTracker::new(PassportTrackerConfig::default());
717 let ptr = 0x1000;
718 let size = 1024;
719
720 let passport_id = tracker
721 .create_passport(ptr, size, "test_context".to_string())
722 .expect("Failed to create passport");
723 assert!(!passport_id.is_empty());
724
725 let passport = tracker.get_passport(ptr).expect("Failed to get passport");
726 assert_eq!(passport.allocation_ptr, ptr);
727 assert_eq!(passport.size_bytes, size);
728 assert_eq!(passport.lifecycle_events.len(), 1);
729 assert_eq!(
730 passport.lifecycle_events[0].event_type,
731 PassportEventType::AllocatedInRust
732 );
733 }
734
735 #[test]
736 fn test_handover_to_ffi() {
737 let tracker = MemoryPassportTracker::new(PassportTrackerConfig::default());
738 let ptr = 0x2000;
739
740 tracker
741 .create_passport(ptr, 512, "rust_context".to_string())
742 .expect("Failed to create passport");
743 tracker
744 .record_handover_to_ffi(ptr, "ffi_context".to_string(), "malloc".to_string())
745 .expect("Failed to record handover");
746
747 let passport = tracker.get_passport(ptr).expect("Failed to get passport");
748 assert_eq!(passport.lifecycle_events.len(), 2);
749 assert_eq!(
750 passport.lifecycle_events[1].event_type,
751 PassportEventType::HandoverToFfi
752 );
753 }
754
755 #[test]
756 fn test_leak_detection() {
757 let tracker = MemoryPassportTracker::new(PassportTrackerConfig::default());
758 let ptr = 0x3000;
759
760 tracker
762 .create_passport(ptr, 256, "rust_context".to_string())
763 .expect("Failed to create passport");
764 tracker
765 .record_handover_to_ffi(ptr, "ffi_context".to_string(), "malloc".to_string())
766 .expect("Failed to record handover");
767
768 let leak_result = tracker.detect_leaks_at_shutdown();
770
771 assert_eq!(leak_result.total_leaks, 1);
772 assert_eq!(leak_result.leaked_passports.len(), 1);
773 assert_eq!(leak_result.leak_details.len(), 1);
774 assert_eq!(leak_result.leak_details[0].memory_address, ptr);
775
776 let passport = tracker.get_passport(ptr).expect("Test operation failed");
778 assert_eq!(
779 passport.status_at_shutdown,
780 PassportStatus::InForeignCustody
781 );
782 }
783
784 #[test]
785 fn test_reclaim_prevents_leak() {
786 let tracker = MemoryPassportTracker::new(PassportTrackerConfig::default());
787 let ptr = 0x4000;
788
789 tracker
791 .create_passport(ptr, 128, "rust_context".to_string())
792 .expect("Failed to create passport");
793 tracker
794 .record_handover_to_ffi(ptr, "ffi_context".to_string(), "malloc".to_string())
795 .expect("Failed to record handover");
796 tracker
797 .record_reclaimed_by_rust(ptr, "rust_context".to_string(), "cleanup".to_string())
798 .expect("Failed to record reclaim");
799
800 let leak_result = tracker.detect_leaks_at_shutdown();
802
803 assert_eq!(leak_result.total_leaks, 0);
804
805 let passport = tracker.get_passport(ptr).expect("Test operation failed");
807 assert_eq!(passport.status_at_shutdown, PassportStatus::ReclaimedByRust);
808 }
809
810 #[test]
811 fn test_passport_validation() {
812 let tracker = MemoryPassportTracker::new(PassportTrackerConfig::default());
813 let ptr = 0x5000;
814
815 tracker
816 .create_passport(ptr, 64, "test_context".to_string())
817 .expect("Failed to create passport");
818 tracker
819 .record_handover_to_ffi(ptr, "ffi_context".to_string(), "test_func".to_string())
820 .expect("Failed to record handover");
821
822 let is_valid = tracker
823 .validate_passport(ptr)
824 .expect("Failed to validate passport");
825 assert!(is_valid);
826 }
827}