use crate::analysis::ffi_function_resolver::{get_global_ffi_resolver, ResolvedFfiFunction};
use crate::capture::types::{AllocationInfo, TrackingError, TrackingResult};
use crate::core::{get_global_call_stack_normalizer, CallStackRef};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AllocationSource {
RustSafe,
UnsafeRust {
unsafe_block_location: String,
call_stack: CallStackRef,
risk_assessment: RiskAssessment,
},
FfiC {
resolved_function: ResolvedFfiFunction,
call_stack: CallStackRef,
libc_hook_info: LibCHookInfo,
},
CrossBoundary {
from: Box<AllocationSource>,
to: Box<AllocationSource>,
transfer_timestamp: u128,
transfer_metadata: TransferMetadata,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
pub struct StackFrame {
pub function_name: String,
pub file_name: Option<String>,
pub line_number: Option<u32>,
pub is_unsafe: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SafetyViolation {
DoubleFree {
first_free_stack: CallStackRef,
second_free_stack: CallStackRef,
timestamp: u128,
},
InvalidFree {
attempted_pointer: usize,
stack: CallStackRef,
timestamp: u128,
},
PotentialLeak {
allocation_stack: CallStackRef,
allocation_timestamp: u128,
leak_detection_timestamp: u128,
},
CrossBoundaryRisk {
risk_level: RiskLevel,
description: String,
stack: CallStackRef,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RiskLevel {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskAssessment {
pub risk_level: RiskLevel,
pub risk_factors: Vec<RiskFactor>,
pub mitigation_suggestions: Vec<String>,
pub confidence_score: f64,
pub assessment_timestamp: u128,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RiskFactor {
pub factor_type: RiskFactorType,
pub severity: f64,
pub description: String,
pub source_location: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RiskFactorType {
RawPointerDeref,
ManualMemoryManagement,
CrossBoundaryTransfer,
UncheckedCast,
LifetimeViolation,
UseAfterFree,
BufferOverflow,
DataRace,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibCHookInfo {
pub hook_method: HookMethod,
pub original_function: String,
pub hook_timestamp: u128,
pub allocation_metadata: AllocationMetadata,
pub hook_overhead_ns: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum HookMethod {
LdPreload,
DynamicLinker,
StaticInterposition,
RuntimePatching,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AllocationMetadata {
pub requested_size: usize,
pub actual_size: usize,
pub alignment: usize,
pub allocator_info: String,
pub protection_flags: Option<MemoryProtectionFlags>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryProtectionFlags {
pub readable: bool,
pub writable: bool,
pub executable: bool,
pub shared: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryPassport {
pub passport_id: String,
pub origin: AllocationOrigin,
pub journey: Vec<PassportStamp>,
pub current_owner: OwnershipInfo,
pub validity_status: ValidityStatus,
pub security_clearance: SecurityClearance,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AllocationOrigin {
pub context: String,
pub allocator_function: String,
pub timestamp: u128,
pub call_stack: CallStackRef,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PassportStamp {
pub timestamp: u128,
pub location: String,
pub operation: String,
pub authority: String,
pub verification_hash: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OwnershipInfo {
pub owner_context: String,
pub owner_function: String,
pub transfer_timestamp: u128,
pub expected_lifetime: Option<u128>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ValidityStatus {
Valid,
Expired,
Revoked,
Suspicious,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SecurityClearance {
Public,
Restricted,
Confidential,
Secret,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransferMetadata {
pub transfer_reason: String,
pub expected_return: Option<String>,
pub validation_method: ValidationMethod,
pub transfer_overhead_ns: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ValidationMethod {
None,
PointerCheck,
BoundsCheck,
IntegrityCheck,
CryptographicCheck,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnhancedAllocationInfo {
pub base: AllocationInfo,
pub source: AllocationSource,
pub call_stack: CallStackRef,
pub cross_boundary_events: Vec<BoundaryEvent>,
pub safety_violations: Vec<SafetyViolation>,
pub ffi_tracked: bool,
pub memory_passport: Option<MemoryPassport>,
pub ownership_history: Option<Vec<OwnershipTransferEvent>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BoundaryEvent {
pub event_type: BoundaryEventType,
pub timestamp: u128,
pub from_context: String,
pub to_context: String,
pub stack: CallStackRef,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BoundaryEventType {
RustToFfi,
FfiToRust,
OwnershipTransfer,
SharedAccess,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BoundaryEventAnalysis {
pub event_id: String,
pub ptr: usize,
pub event_type: BoundaryEventType,
pub from_context: String,
pub to_context: String,
pub transfer_size: usize,
pub timestamp: u128,
pub risk_assessment: BoundaryRiskAssessment,
pub ownership_chain: Vec<OwnershipRecord>,
pub security_implications: Vec<SecurityImplication>,
pub performance_impact: PerformanceImpact,
pub mitigation_recommendations: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BoundaryRiskAssessment {
pub overall_risk_level: RiskLevel,
pub risk_score: f64,
pub risk_factors: Vec<BoundaryRiskFactor>,
pub confidence_score: f64,
pub assessment_timestamp: u128,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BoundaryRiskFactor {
pub factor_type: BoundaryRiskFactorType,
pub severity: f64,
pub description: String,
pub mitigation: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BoundaryRiskFactorType {
RustToForeignTransfer,
ForeignToRustTransfer,
OwnershipTransfer,
SharedAccess,
LargeTransfer,
FrequentTransfers,
UnvalidatedTransfer,
PrivilegeBoundary,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OwnershipTransferEvent {
pub transfer_id: String,
pub ptr: usize,
pub from_context: String,
pub to_context: String,
pub transfer_timestamp: u128,
pub transfer_reason: String,
pub validation_status: OwnershipValidationStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OwnershipValidationStatus {
Valid,
Pending,
Warning,
Invalid,
Failed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OwnershipRecord {
pub context: String,
pub timestamp: u128,
pub transfer_reason: String,
pub validation_status: OwnershipValidationStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityImplication {
pub implication_type: SecurityImplicationType,
pub severity: RiskLevel,
pub description: String,
pub potential_impact: String,
pub recommended_action: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SecurityImplicationType {
PrivilegeEscalation,
DataExposure,
InjectionRisk,
BufferOverflow,
UseAfterFree,
RaceCondition,
InformationDisclosure,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceImpact {
pub impact_level: PerformanceImpactLevel,
pub estimated_overhead_ns: u64,
pub memory_overhead_bytes: usize,
pub cpu_overhead_percent: f64,
pub recommendations: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PerformanceImpactLevel {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BoundaryEventStatistics {
pub total_events: usize,
pub events_by_type: std::collections::HashMap<String, usize>,
pub risk_distribution: std::collections::HashMap<String, usize>,
pub average_transfer_size: f64,
pub total_transfer_volume: usize,
pub most_active_contexts: Vec<(String, usize)>,
pub security_incidents: usize,
pub performance_issues: usize,
pub analysis_timestamp: u128,
}
pub struct UnsafeFFITracker {
enhanced_allocations: Mutex<HashMap<usize, EnhancedAllocationInfo>>,
freed_pointers: Mutex<HashMap<usize, (CallStackRef, u128)>>,
violations: Mutex<Vec<SafetyViolation>>,
c_libraries: Mutex<HashMap<String, CLibraryInfo>>,
libc_hooks: Mutex<HashMap<String, EnhancedLibCHookInfo>>,
memory_passports: Mutex<HashMap<usize, MemoryPassport>>,
}
impl UnsafeFFITracker {
pub fn new() -> Self {
Self {
enhanced_allocations: Mutex::new(HashMap::new()),
freed_pointers: Mutex::new(HashMap::new()),
violations: Mutex::new(Vec::new()),
c_libraries: Mutex::new(HashMap::new()),
libc_hooks: Mutex::new(HashMap::new()),
memory_passports: Mutex::new(HashMap::new()),
}
}
fn create_default_unsafe_risk_assessment(&self, unsafe_location: &str) -> RiskAssessment {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
let risk_factors = vec![RiskFactor {
factor_type: RiskFactorType::ManualMemoryManagement,
severity: 5.0,
description: "Manual memory management in unsafe block".to_string(),
source_location: Some(unsafe_location.to_string()),
}];
RiskAssessment {
risk_level: RiskLevel::Medium,
risk_factors,
mitigation_suggestions: vec![
"Ensure proper memory cleanup".to_string(),
"Use RAII patterns where possible".to_string(),
],
confidence_score: 0.7,
assessment_timestamp: current_time,
}
}
fn create_default_libc_hook_info(&self, function_name: &str, size: usize) -> LibCHookInfo {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
LibCHookInfo {
hook_method: HookMethod::DynamicLinker,
original_function: function_name.to_string(),
hook_timestamp: current_time,
allocation_metadata: AllocationMetadata {
requested_size: size,
actual_size: size,
alignment: 8, allocator_info: "libc malloc".to_string(),
protection_flags: Some(MemoryProtectionFlags {
readable: true,
writable: true,
executable: false,
shared: false,
}),
},
hook_overhead_ns: Some(100), }
}
fn create_memory_passport(&self, ptr: usize, origin_context: &str) -> MemoryPassport {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
MemoryPassport {
passport_id: format!("passport_{ptr:x}_{current_time}"),
origin: AllocationOrigin {
context: origin_context.to_string(),
allocator_function: "unknown".to_string(),
timestamp: current_time,
call_stack: {
let normalizer = get_global_call_stack_normalizer();
let empty_frames = vec![];
let id = normalizer.normalize_call_stack(&empty_frames).unwrap_or(0);
CallStackRef::new(id, Some(0))
},
},
journey: Vec::new(),
current_owner: OwnershipInfo {
owner_context: origin_context.to_string(),
owner_function: "unknown".to_string(),
transfer_timestamp: current_time,
expected_lifetime: None,
},
validity_status: ValidityStatus::Valid,
security_clearance: SecurityClearance::Public,
}
}
pub fn track_unsafe_allocation(
&self,
ptr: usize,
size: usize,
unsafe_location: String,
) -> TrackingResult<()> {
let call_stack = self.capture_call_stack()?;
let base_allocation = AllocationInfo::new(ptr, size);
let risk_assessment = self.create_default_unsafe_risk_assessment(&unsafe_location);
let enhanced = EnhancedAllocationInfo {
base: base_allocation,
source: AllocationSource::UnsafeRust {
unsafe_block_location: unsafe_location,
call_stack: call_stack.clone(),
risk_assessment,
},
call_stack,
cross_boundary_events: Vec::new(),
safety_violations: Vec::new(),
ffi_tracked: false,
memory_passport: None,
ownership_history: None,
};
if let Ok(mut allocations) = self.enhanced_allocations.lock() {
allocations.insert(ptr, enhanced);
tracing::info!("Tracked unsafe allocation at {:x} (size: {})", ptr, size);
}
Ok(())
}
pub fn track_ffi_allocation(
&self,
ptr: usize,
size: usize,
library_name: String,
function_name: String,
) -> TrackingResult<()> {
let call_stack = self.capture_call_stack()?;
let base_allocation = AllocationInfo::new(ptr, size);
let libc_hook_info = self.create_default_libc_hook_info(&function_name, size);
let resolver = get_global_ffi_resolver();
let resolved_function = resolver
.resolve_function(&function_name, Some(&library_name))
.unwrap_or_else(|_| {
tracing::warn!(
"Failed to resolve FFI function: {}::{}",
library_name,
function_name
);
ResolvedFfiFunction {
library_name: library_name.clone(),
function_name: function_name.clone(),
signature: None,
category: crate::analysis::FfiFunctionCategory::Unknown,
risk_level: crate::analysis::FfiRiskLevel::Medium,
metadata: std::collections::HashMap::new(),
}
});
let enhanced = EnhancedAllocationInfo {
base: base_allocation,
source: AllocationSource::FfiC {
resolved_function,
call_stack: call_stack.clone(),
libc_hook_info,
},
call_stack,
cross_boundary_events: Vec::new(),
safety_violations: Vec::new(),
ffi_tracked: true,
memory_passport: None,
ownership_history: None,
};
if let Ok(mut allocations) = self.enhanced_allocations.lock() {
allocations.insert(ptr, enhanced);
tracing::info!("Tracked FFI allocation at {:x} (size: {})", ptr, size);
}
Ok(())
}
pub fn track_enhanced_deallocation(&self, ptr: usize) -> TrackingResult<()> {
let call_stack = self.capture_call_stack()?;
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis();
if let Ok(freed) = self.freed_pointers.lock() {
if let Some((first_free_stack, _first_timestamp)) = freed.get(&ptr) {
let violation = SafetyViolation::DoubleFree {
first_free_stack: first_free_stack.clone(),
second_free_stack: call_stack.clone(),
timestamp,
};
if let Ok(mut violations) = self.violations.lock() {
violations.push(violation);
}
tracing::error!("Double free detected at {:x}", ptr);
return Err(TrackingError::MemoryCorruption(
"Memory corruption detected".to_string(),
));
}
}
if let Ok(mut allocations) = self.enhanced_allocations.lock() {
if let Some(mut allocation) = allocations.remove(&ptr) {
allocation.base.mark_deallocated();
if let Ok(mut freed) = self.freed_pointers.lock() {
freed.insert(ptr, (call_stack, timestamp));
}
tracing::info!("Tracked enhanced deallocation at {:x}", ptr);
} else {
let violation = SafetyViolation::InvalidFree {
attempted_pointer: ptr,
stack: call_stack,
timestamp,
};
if let Ok(mut violations) = self.violations.lock() {
violations.push(violation);
}
tracing::error!("Invalid free detected at {:x}", ptr);
return Err(TrackingError::InvalidPointer(format!(
"Invalid pointer: 0x{ptr:x}"
)));
}
}
Ok(())
}
pub fn record_boundary_event(
&self,
ptr: usize,
event_type: BoundaryEventType,
from_context: String,
to_context: String,
) -> TrackingResult<()> {
let call_stack = self.capture_call_stack()?;
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis();
let event = BoundaryEvent {
event_type,
timestamp,
from_context,
to_context,
stack: call_stack,
};
if let Ok(mut allocations) = self.enhanced_allocations.lock() {
if let Some(allocation) = allocations.get_mut(&ptr) {
allocation.cross_boundary_events.push(event);
tracing::info!("Recorded boundary event for {:x}", ptr);
}
}
Ok(())
}
pub fn create_or_update_passport(
&self,
ptr: usize,
operation: &str,
context: &str,
) -> TrackingResult<()> {
if let Ok(mut allocations) = self.enhanced_allocations.lock() {
if let Some(allocation) = allocations.get_mut(&ptr) {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
if allocation.memory_passport.is_none() {
allocation.memory_passport = Some(self.create_memory_passport(ptr, context));
}
if let Some(passport) = &mut allocation.memory_passport {
let stamp = PassportStamp {
timestamp: current_time,
location: context.to_string(),
operation: operation.to_string(),
authority: "UnsafeFFITracker".to_string(),
verification_hash: format!("{:x}", ptr ^ current_time as usize),
};
passport.journey.push(stamp);
}
}
}
Ok(())
}
pub fn update_ownership(
&self,
ptr: usize,
new_owner_context: String,
new_owner_function: String,
) -> TrackingResult<()> {
if let Ok(mut allocations) = self.enhanced_allocations.lock() {
if let Some(allocation) = allocations.get_mut(&ptr) {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
if let Some(passport) = &mut allocation.memory_passport {
passport.current_owner = OwnershipInfo {
owner_context: new_owner_context,
owner_function: new_owner_function,
transfer_timestamp: current_time,
expected_lifetime: None,
};
}
}
}
Ok(())
}
pub fn validate_passport(&self, ptr: usize) -> TrackingResult<bool> {
if let Ok(allocations) = self.enhanced_allocations.lock() {
if let Some(allocation) = allocations.get(&ptr) {
if let Some(passport) = &allocation.memory_passport {
match passport.validity_status {
ValidityStatus::Valid => Ok(true),
ValidityStatus::Expired
| ValidityStatus::Revoked
| ValidityStatus::Suspicious => Ok(false),
}
} else {
Ok(false) }
} else {
Ok(false) }
} else {
Err(TrackingError::LockError(
"Failed to acquire allocations lock".to_string(),
))
}
}
pub fn get_safety_violations(&self) -> TrackingResult<Vec<SafetyViolation>> {
self.violations
.lock()
.map(|v| v.clone())
.map_err(|e| TrackingError::LockError(e.to_string()))
}
pub fn get_enhanced_allocations(&self) -> TrackingResult<Vec<EnhancedAllocationInfo>> {
self.enhanced_allocations
.lock()
.map(|allocations| allocations.values().cloned().collect())
.map_err(|e| TrackingError::LockError(e.to_string()))
}
fn capture_call_stack(&self) -> TrackingResult<CallStackRef> {
let frames = vec![StackFrame {
function_name: "current_function".to_string(),
file_name: Some("src/unsafe_ffi_tracker.rs".to_string()),
line_number: Some(42),
is_unsafe: true,
}];
let normalizer = get_global_call_stack_normalizer();
let id = match normalizer.normalize_call_stack(&frames) {
Ok(id) => id,
Err(e) => {
return Err(TrackingError::AnalysisError(format!(
"Failed to normalize call stack: {}",
e
)))
}
};
Ok(CallStackRef::new(id, Some(frames.len())))
}
pub fn detect_leaks(&self, threshold_ms: u128) -> TrackingResult<Vec<SafetyViolation>> {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis();
let mut leaks = Vec::new();
if let Ok(allocations) = self.enhanced_allocations.lock() {
for allocation in allocations.values() {
let alloc_time = allocation.base.timestamp_alloc as u128;
let age = current_time.saturating_sub(alloc_time);
if age > threshold_ms && allocation.base.is_active() {
leaks.push(SafetyViolation::PotentialLeak {
allocation_stack: allocation.call_stack.clone(),
allocation_timestamp: allocation.base.timestamp_alloc as u128,
leak_detection_timestamp: current_time,
});
}
}
}
Ok(leaks)
}
}
impl Default for UnsafeFFITracker {
fn default() -> Self {
Self::new()
}
}
static GLOBAL_UNSAFE_FFI_TRACKER: std::sync::OnceLock<std::sync::Arc<UnsafeFFITracker>> =
std::sync::OnceLock::new();
pub fn get_global_unsafe_ffi_tracker() -> std::sync::Arc<UnsafeFFITracker> {
GLOBAL_UNSAFE_FFI_TRACKER
.get_or_init(|| std::sync::Arc::new(UnsafeFFITracker::new()))
.clone()
}
#[macro_export]
macro_rules! track_unsafe_alloc {
($ptr:expr, $size:expr) => {{
let tracker = $crate::unsafe_ffi_tracker::get_global_unsafe_ffi_tracker();
let location = format!("{}:{}:{}", file!(), line!(), column!());
let _ = tracker.track_unsafe_allocation($ptr as usize, $size, location);
}};
}
#[macro_export]
macro_rules! track_ffi_alloc {
($ptr:expr, $size:expr, $lib:expr, $func:expr) => {{
let tracker = $crate::unsafe_ffi_tracker::get_global_unsafe_ffi_tracker();
let _ =
tracker.track_ffi_allocation($ptr as usize, $size, $lib.to_string(), $func.to_string());
}};
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UnsafeFFIStats {
pub total_operations: usize,
pub unsafe_blocks: usize,
pub ffi_calls: usize,
pub raw_pointer_operations: usize,
pub memory_violations: usize,
pub risk_score: f64,
pub operations: Vec<UnsafeOperation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UnsafeOperation {
pub operation_type: UnsafeOperationType,
pub location: String,
pub risk_level: RiskLevel,
pub timestamp: u128,
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum UnsafeOperationType {
RawPointerDeref,
FfiCall,
UnsafeBlock,
MemoryViolation,
CrossBoundaryTransfer,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CLibraryInfo {
pub library_name: String,
pub library_version: Option<String>,
pub library_path: Option<String>,
pub functions_called: HashMap<String, CFunctionInfo>,
pub total_allocations: usize,
pub total_bytes_allocated: usize,
pub load_timestamp: u128,
pub metadata: LibraryMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CFunctionInfo {
pub function_name: String,
pub function_signature: Option<String>,
pub call_count: usize,
pub bytes_allocated: usize,
pub average_allocation_size: f64,
pub risk_assessment: RiskAssessment,
pub performance_metrics: FunctionPerformanceMetrics,
pub first_call_timestamp: u128,
pub last_call_timestamp: u128,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionPerformanceMetrics {
pub avg_execution_time_ns: u64,
pub min_execution_time_ns: u64,
pub max_execution_time_ns: u64,
pub total_execution_time_ns: u64,
pub tracking_overhead_ns: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LibraryMetadata {
pub architecture: String,
pub operating_system: String,
pub compiler_info: Option<String>,
pub has_debug_symbols: bool,
pub security_features: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnhancedLibCHookInfo {
pub base_info: LibCHookInfo,
pub function_tracking: CFunctionInfo,
pub installation_details: HookInstallationDetails,
pub runtime_analysis: RuntimeBehaviorAnalysis,
pub security_analysis: SecurityAnalysis,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HookInstallationDetails {
pub installation_method: HookInstallationMethod,
pub installation_success: bool,
pub installation_error: Option<String>,
pub installation_timestamp: u128,
pub process_id: u32,
pub thread_id: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum HookInstallationMethod {
Preload,
SymbolInterposition,
BinaryPatching,
DebuggerHook,
KernelHook,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RuntimeBehaviorAnalysis {
pub memory_patterns: Vec<MemoryAccessPattern>,
pub size_distribution: SizeDistribution,
pub temporal_patterns: TemporalPatterns,
pub error_patterns: Vec<ErrorPattern>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryAccessPattern {
pub pattern_type: MemoryPatternType,
pub frequency: usize,
pub average_size: usize,
pub risk_level: RiskLevel,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MemoryPatternType {
Sequential,
Random,
Bulk,
Fragmented,
Reallocation,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SizeDistribution {
pub small_allocations: usize,
pub medium_allocations: usize,
pub large_allocations: usize,
pub average_size: f64,
pub size_std_dev: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemporalPatterns {
pub allocation_rate: f64,
pub peak_periods: Vec<PeakPeriod>,
pub burst_count: usize,
pub avg_time_between_allocs_ms: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PeakPeriod {
pub start_timestamp: u128,
pub end_timestamp: u128,
pub allocation_count: usize,
pub bytes_allocated: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorPattern {
pub error_type: ErrorType,
pub frequency: usize,
pub context: String,
pub mitigation: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ErrorType {
AllocationFailure,
InvalidFree,
DoubleFree,
MemoryLeak,
BufferOverflow,
UseAfterFree,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityAnalysis {
pub vulnerabilities: Vec<SecurityVulnerability>,
pub security_score: f64,
pub recommendations: Vec<String>,
pub compliance_status: ComplianceStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityVulnerability {
pub vulnerability_type: VulnerabilityType,
pub severity: RiskLevel,
pub description: String,
pub location: String,
pub potential_impact: String,
pub remediation: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum VulnerabilityType {
BufferOverflow,
UseAfterFree,
DoubleFree,
MemoryLeak,
IntegerOverflow,
FormatString,
RaceCondition,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceStatus {
pub memory_safety: bool,
pub thread_safety: bool,
pub api_usage: bool,
pub security_practices: bool,
pub overall_score: f64,
}
impl UnsafeFFITracker {
pub fn register_c_library(
&self,
library_name: String,
library_path: Option<String>,
library_version: Option<String>,
) -> TrackingResult<()> {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
let library_info = CLibraryInfo {
library_name: library_name.clone(),
library_version,
library_path,
functions_called: HashMap::new(),
total_allocations: 0,
total_bytes_allocated: 0,
load_timestamp: current_time,
metadata: LibraryMetadata {
architecture: std::env::consts::ARCH.to_string(),
operating_system: std::env::consts::OS.to_string(),
compiler_info: None,
has_debug_symbols: false,
security_features: Vec::new(),
},
};
if let Ok(mut libraries) = self.c_libraries.lock() {
libraries.insert(library_name.clone(), library_info);
tracing::info!("Registered C library: {}", library_name);
}
Ok(())
}
pub fn track_c_function_call(
&self,
library_name: &str,
function_name: &str,
allocation_size: usize,
execution_time_ns: u64,
) -> TrackingResult<()> {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
if let Ok(mut libraries) = self.c_libraries.lock() {
let library = libraries
.entry(library_name.to_string())
.or_insert_with(|| CLibraryInfo {
library_name: library_name.to_string(),
library_version: None,
library_path: None,
functions_called: HashMap::new(),
total_allocations: 0,
total_bytes_allocated: 0,
load_timestamp: current_time,
metadata: LibraryMetadata {
architecture: std::env::consts::ARCH.to_string(),
operating_system: std::env::consts::OS.to_string(),
compiler_info: None,
has_debug_symbols: false,
security_features: Vec::new(),
},
});
library.total_allocations += 1;
library.total_bytes_allocated += allocation_size;
let function_info = library
.functions_called
.entry(function_name.to_string())
.or_insert_with(|| CFunctionInfo {
function_name: function_name.to_string(),
function_signature: None,
call_count: 0,
bytes_allocated: 0,
average_allocation_size: 0.0,
risk_assessment: RiskAssessment {
risk_level: RiskLevel::Low,
risk_factors: Vec::new(),
mitigation_suggestions: Vec::new(),
confidence_score: 0.5,
assessment_timestamp: current_time,
},
performance_metrics: FunctionPerformanceMetrics {
avg_execution_time_ns: 0,
min_execution_time_ns: u64::MAX,
max_execution_time_ns: 0,
total_execution_time_ns: 0,
tracking_overhead_ns: 0,
},
first_call_timestamp: current_time,
last_call_timestamp: current_time,
});
function_info.call_count += 1;
function_info.bytes_allocated += allocation_size;
function_info.average_allocation_size =
function_info.bytes_allocated as f64 / function_info.call_count as f64;
function_info.last_call_timestamp = current_time;
let metrics = &mut function_info.performance_metrics;
metrics.total_execution_time_ns += execution_time_ns;
metrics.avg_execution_time_ns =
metrics.total_execution_time_ns / function_info.call_count as u64;
metrics.min_execution_time_ns = metrics.min_execution_time_ns.min(execution_time_ns);
metrics.max_execution_time_ns = metrics.max_execution_time_ns.max(execution_time_ns);
tracing::debug!(
"Tracked C function call: {}::{} (size: {}, time: {}ns)",
library_name,
function_name,
allocation_size,
execution_time_ns
);
}
Ok(())
}
pub fn install_enhanced_libc_hook(
&self,
function_name: String,
hook_method: HookInstallationMethod,
) -> TrackingResult<()> {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
let installation_details = HookInstallationDetails {
installation_method: hook_method,
installation_success: true, installation_error: None,
installation_timestamp: current_time,
process_id: std::process::id(),
thread_id: 0, };
let enhanced_hook = EnhancedLibCHookInfo {
base_info: LibCHookInfo {
hook_method: HookMethod::DynamicLinker,
original_function: function_name.clone(),
hook_timestamp: current_time,
allocation_metadata: AllocationMetadata {
requested_size: 0,
actual_size: 0,
alignment: 8,
allocator_info: format!("hooked_{function_name}"),
protection_flags: Some(MemoryProtectionFlags {
readable: true,
writable: true,
executable: false,
shared: false,
}),
},
hook_overhead_ns: Some(50),
},
function_tracking: CFunctionInfo {
function_name: function_name.clone(),
function_signature: None,
call_count: 0,
bytes_allocated: 0,
average_allocation_size: 0.0,
risk_assessment: RiskAssessment {
risk_level: RiskLevel::Medium,
risk_factors: Vec::new(),
mitigation_suggestions: vec![
"Monitor for memory leaks".to_string(),
"Validate all pointer operations".to_string(),
],
confidence_score: 0.7,
assessment_timestamp: current_time,
},
performance_metrics: FunctionPerformanceMetrics {
avg_execution_time_ns: 0,
min_execution_time_ns: u64::MAX,
max_execution_time_ns: 0,
total_execution_time_ns: 0,
tracking_overhead_ns: 50,
},
first_call_timestamp: current_time,
last_call_timestamp: current_time,
},
installation_details,
runtime_analysis: RuntimeBehaviorAnalysis {
memory_patterns: Vec::new(),
size_distribution: SizeDistribution {
small_allocations: 0,
medium_allocations: 0,
large_allocations: 0,
average_size: 0.0,
size_std_dev: 0.0,
},
temporal_patterns: TemporalPatterns {
allocation_rate: 0.0,
peak_periods: Vec::new(),
burst_count: 0,
avg_time_between_allocs_ms: 0.0,
},
error_patterns: Vec::new(),
},
security_analysis: SecurityAnalysis {
vulnerabilities: Vec::new(),
security_score: 5.0,
recommendations: vec![
"Enable memory protection".to_string(),
"Use safe allocation patterns".to_string(),
],
compliance_status: ComplianceStatus {
memory_safety: false,
thread_safety: false,
api_usage: true,
security_practices: false,
overall_score: 0.25,
},
},
};
if let Ok(mut hooks) = self.libc_hooks.lock() {
hooks.insert(function_name.clone(), enhanced_hook);
tracing::info!("Installed enhanced LibC hook for: {}", function_name);
}
Ok(())
}
pub fn create_and_register_passport(
&self,
ptr: usize,
origin_context: &str,
security_clearance: SecurityClearance,
) -> TrackingResult<String> {
let passport = self.create_memory_passport(ptr, origin_context);
let passport_id = passport.passport_id.clone();
let mut passport = passport;
passport.security_clearance = security_clearance;
if let Ok(mut passports) = self.memory_passports.lock() {
passports.insert(ptr, passport);
tracing::info!("Created memory passport {} for ptr {:x}", passport_id, ptr);
}
Ok(passport_id)
}
pub fn stamp_passport(
&self,
ptr: usize,
operation: &str,
location: &str,
authority: &str,
) -> TrackingResult<()> {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
if let Ok(mut passports) = self.memory_passports.lock() {
if let Some(passport) = passports.get_mut(&ptr) {
let stamp = PassportStamp {
timestamp: current_time,
location: location.to_string(),
operation: operation.to_string(),
authority: authority.to_string(),
verification_hash: format!("{:x}", ptr ^ current_time as usize),
};
passport.journey.push(stamp);
tracing::debug!("Stamped passport for ptr {:x}: {}", ptr, operation);
} else {
return Err(TrackingError::InvalidPointer(format!(
"No passport found for pointer: 0x{ptr:x}",
)));
}
}
Ok(())
}
pub fn transfer_passport_ownership(
&self,
ptr: usize,
new_owner_context: &str,
new_owner_function: &str,
) -> TrackingResult<()> {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
if let Ok(mut passports) = self.memory_passports.lock() {
if let Some(passport) = passports.get_mut(&ptr) {
passport.current_owner = OwnershipInfo {
owner_context: new_owner_context.to_string(),
owner_function: new_owner_function.to_string(),
transfer_timestamp: current_time,
expected_lifetime: None,
};
let stamp = PassportStamp {
timestamp: current_time,
location: new_owner_context.to_string(),
operation: "ownership_transfer".to_string(),
authority: "UnsafeFFITracker".to_string(),
verification_hash: format!("{:x}", ptr ^ current_time as usize),
};
passport.journey.push(stamp);
tracing::info!(
"Transferred passport ownership for ptr {:x} to {}::{}",
ptr,
new_owner_context,
new_owner_function
);
} else {
return Err(TrackingError::InvalidPointer(format!(
"No passport found for pointer: 0x{ptr:x}",
)));
}
}
Ok(())
}
pub fn revoke_passport(&self, ptr: usize, reason: &str) -> TrackingResult<()> {
if let Ok(mut passports) = self.memory_passports.lock() {
if let Some(passport) = passports.get_mut(&ptr) {
passport.validity_status = ValidityStatus::Revoked;
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
let stamp = PassportStamp {
timestamp: current_time,
location: "memory_freed".to_string(),
operation: format!("revoked: {reason}"),
authority: "UnsafeFFITracker".to_string(),
verification_hash: format!("{:x}", ptr ^ current_time as usize),
};
passport.journey.push(stamp);
tracing::info!("Revoked passport for ptr {ptr:x}: {reason}");
}
}
Ok(())
}
pub fn get_c_library_stats(&self) -> TrackingResult<HashMap<String, CLibraryInfo>> {
self.c_libraries
.lock()
.map(|libs| libs.clone())
.map_err(|e| TrackingError::LockError(e.to_string()))
}
pub fn get_libc_hook_info(&self) -> TrackingResult<HashMap<String, EnhancedLibCHookInfo>> {
self.libc_hooks
.lock()
.map(|hooks| hooks.clone())
.map_err(|e| TrackingError::LockError(e.to_string()))
}
pub fn get_memory_passports(&self) -> TrackingResult<HashMap<usize, MemoryPassport>> {
self.memory_passports
.lock()
.map(|passports| passports.clone())
.map_err(|e| TrackingError::LockError(e.to_string()))
}
pub fn analyze_cross_boundary_risks(&self) -> TrackingResult<Vec<SafetyViolation>> {
let mut risks = Vec::new();
if let Ok(passports) = self.memory_passports.lock() {
for (ptr, passport) in passports.iter() {
if passport.journey.len() > 10 {
risks.push(SafetyViolation::CrossBoundaryRisk {
risk_level: RiskLevel::Medium,
description: format!(
"Memory at {ptr:x} has crossed boundaries {} times",
passport.journey.len()
),
stack: {
let normalizer = get_global_call_stack_normalizer();
let empty_frames = vec![];
let id = normalizer.normalize_call_stack(&empty_frames).unwrap_or(0);
CallStackRef::new(id, Some(0))
},
});
}
if matches!(passport.validity_status, ValidityStatus::Expired) {
risks.push(SafetyViolation::CrossBoundaryRisk {
risk_level: RiskLevel::High,
description: format!("Expired passport detected for memory at {ptr:x}"),
stack: {
let normalizer = get_global_call_stack_normalizer();
let empty_frames = vec![];
let id = normalizer.normalize_call_stack(&empty_frames).unwrap_or(0);
CallStackRef::new(id, Some(0))
},
});
}
}
}
Ok(risks)
}
pub fn process_boundary_event(
&self,
ptr: usize,
event_type: BoundaryEventType,
from_context: &str,
to_context: &str,
transfer_size: usize,
) -> TrackingResult<BoundaryEventAnalysis> {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
self.record_boundary_event(
ptr,
event_type.clone(),
from_context.to_string(),
to_context.to_string(),
)?;
let risk_analysis = self.assess_boundary_transfer_risk(
ptr,
&event_type,
from_context,
to_context,
transfer_size,
)?;
self.track_ownership_transfer(ptr, from_context, to_context)?;
let analysis = BoundaryEventAnalysis {
event_id: format!("boundary_{ptr}_{current_time}"),
ptr,
event_type: event_type.clone(),
from_context: from_context.to_string(),
to_context: to_context.to_string(),
transfer_size,
timestamp: current_time,
risk_assessment: risk_analysis.clone(),
ownership_chain: self.get_ownership_chain(ptr)?,
security_implications: self.analyze_security_implications(
ptr,
from_context,
to_context,
)?,
performance_impact: self.estimate_performance_impact(&event_type, transfer_size),
mitigation_recommendations: self.generate_mitigation_recommendations(&risk_analysis),
};
Ok(analysis)
}
fn assess_boundary_transfer_risk(
&self,
ptr: usize,
event_type: &BoundaryEventType,
_from_context: &str,
_to_context: &str,
transfer_size: usize,
) -> TrackingResult<BoundaryRiskAssessment> {
let mut risk_factors = Vec::new();
let mut risk_score = 0.0;
match event_type {
BoundaryEventType::RustToFfi => {
risk_factors.push(BoundaryRiskFactor {
factor_type: BoundaryRiskFactorType::RustToForeignTransfer,
severity: 6.0,
description: "Memory allocated in Rust being passed to foreign code"
.to_string(),
mitigation: "Ensure foreign code doesn't free Rust-allocated memory"
.to_string(),
});
risk_score += 6.0;
}
BoundaryEventType::FfiToRust => {
risk_factors.push(BoundaryRiskFactor {
factor_type: BoundaryRiskFactorType::ForeignToRustTransfer,
severity: 7.0,
description: "Foreign-allocated memory being passed to Rust".to_string(),
mitigation: "Validate memory layout and lifetime guarantees".to_string(),
});
risk_score += 7.0;
}
BoundaryEventType::OwnershipTransfer => {
risk_factors.push(BoundaryRiskFactor {
factor_type: BoundaryRiskFactorType::OwnershipTransfer,
severity: 8.0,
description: "Memory ownership being transferred across language boundary"
.to_string(),
mitigation: "Clearly document ownership transfer and cleanup responsibilities"
.to_string(),
});
risk_score += 8.0;
}
BoundaryEventType::SharedAccess => {
risk_factors.push(BoundaryRiskFactor {
factor_type: BoundaryRiskFactorType::SharedAccess,
severity: 5.0,
description: "Memory being shared between Rust and foreign code".to_string(),
mitigation: "Implement proper synchronization mechanisms".to_string(),
});
risk_score += 5.0;
}
}
if transfer_size > 1024 * 1024 {
risk_factors.push(BoundaryRiskFactor {
factor_type: BoundaryRiskFactorType::LargeTransfer,
severity: 4.0,
description: format!("Large memory transfer: {transfer_size} bytes"),
mitigation: "Consider streaming or chunked transfer for large data".to_string(),
});
risk_score += 4.0;
}
if let Ok(allocations) = self.enhanced_allocations.lock() {
if let Some(allocation) = allocations.get(&ptr) {
if allocation.cross_boundary_events.len() > 5 {
risk_factors.push(BoundaryRiskFactor {
factor_type: BoundaryRiskFactorType::FrequentTransfers,
severity: 3.0,
description: format!(
"Memory has crossed boundaries {} times",
allocation.cross_boundary_events.len()
),
mitigation: "Consider reducing boundary crossings or caching".to_string(),
});
risk_score += 3.0;
}
}
}
let risk_level = if risk_score >= 15.0 {
RiskLevel::Critical
} else if risk_score >= 10.0 {
RiskLevel::High
} else if risk_score >= 5.0 {
RiskLevel::Medium
} else {
RiskLevel::Low
};
Ok(BoundaryRiskAssessment {
overall_risk_level: risk_level,
risk_score,
risk_factors,
confidence_score: 0.8, assessment_timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos(),
})
}
fn track_ownership_transfer(
&self,
ptr: usize,
from_context: &str,
to_context: &str,
) -> TrackingResult<()> {
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
if let Ok(mut passports) = self.memory_passports.lock() {
if let Some(passport) = passports.get_mut(&ptr) {
let stamp = PassportStamp {
timestamp: current_time,
location: to_context.to_string(),
operation: format!("ownership_transfer_from_{from_context}"),
authority: "BoundaryEventProcessor".to_string(),
verification_hash: format!("{:x}", ptr ^ current_time as usize),
};
passport.journey.push(stamp);
passport.current_owner = OwnershipInfo {
owner_context: to_context.to_string(),
owner_function: "unknown".to_string(),
transfer_timestamp: current_time,
expected_lifetime: None,
};
}
}
if let Ok(mut allocations) = self.enhanced_allocations.lock() {
if let Some(allocation) = allocations.get_mut(&ptr) {
let ownership_event = OwnershipTransferEvent {
transfer_id: format!("transfer_{ptr}_{current_time}"),
ptr,
from_context: from_context.to_string(),
to_context: to_context.to_string(),
transfer_timestamp: current_time,
transfer_reason: "boundary_crossing".to_string(),
validation_status: OwnershipValidationStatus::Pending,
};
if allocation.ownership_history.is_none() {
allocation.ownership_history = Some(Vec::new());
}
if let Some(ref mut history) = allocation.ownership_history {
history.push(ownership_event);
}
}
}
Ok(())
}
fn get_ownership_chain(&self, ptr: usize) -> TrackingResult<Vec<OwnershipRecord>> {
let mut chain = Vec::new();
if let Ok(allocations) = self.enhanced_allocations.lock() {
if let Some(allocation) = allocations.get(&ptr) {
if let Some(ref history) = allocation.ownership_history {
for transfer in history {
chain.push(OwnershipRecord {
context: transfer.to_context.clone(),
timestamp: transfer.transfer_timestamp,
transfer_reason: transfer.transfer_reason.clone(),
validation_status: transfer.validation_status.clone(),
});
}
}
}
}
Ok(chain)
}
fn analyze_security_implications(
&self,
_ptr: usize,
from_context: &str,
to_context: &str,
) -> TrackingResult<Vec<SecurityImplication>> {
let mut implications = Vec::new();
if from_context.contains("user") && to_context.contains("system") {
implications.push(SecurityImplication {
implication_type: SecurityImplicationType::PrivilegeEscalation,
severity: RiskLevel::High,
description: "Memory transfer from user context to system context".to_string(),
potential_impact: "Potential privilege escalation vulnerability".to_string(),
recommended_action: "Validate and sanitize all data before system context access"
.to_string(),
});
}
if from_context.contains("secure") || to_context.contains("secure") {
implications.push(SecurityImplication {
implication_type: SecurityImplicationType::DataExposure,
severity: RiskLevel::Medium,
description: "Memory transfer involving secure context".to_string(),
potential_impact: "Potential sensitive data exposure".to_string(),
recommended_action: "Ensure proper data sanitization and access controls"
.to_string(),
});
}
if to_context.contains("interpreter") || to_context.contains("eval") {
implications.push(SecurityImplication {
implication_type: SecurityImplicationType::InjectionRisk,
severity: RiskLevel::Critical,
description: "Memory transfer to code interpretation context".to_string(),
potential_impact: "Potential code injection vulnerability".to_string(),
recommended_action: "Validate and sanitize all input data before interpretation"
.to_string(),
});
}
Ok(implications)
}
fn estimate_performance_impact(
&self,
event_type: &BoundaryEventType,
transfer_size: usize,
) -> PerformanceImpact {
let base_overhead_ns = match event_type {
BoundaryEventType::RustToFfi => 100,
BoundaryEventType::FfiToRust => 150,
BoundaryEventType::OwnershipTransfer => 200,
BoundaryEventType::SharedAccess => 50,
};
let size_overhead_ns = (transfer_size / 1024) as u64 * 10; let total_overhead_ns = base_overhead_ns + size_overhead_ns;
let impact_level = if total_overhead_ns > 10000 {
PerformanceImpactLevel::High
} else if total_overhead_ns > 1000 {
PerformanceImpactLevel::Medium
} else {
PerformanceImpactLevel::Low
};
PerformanceImpact {
impact_level,
estimated_overhead_ns: total_overhead_ns,
memory_overhead_bytes: transfer_size / 10, cpu_overhead_percent: if total_overhead_ns > 5000 { 5.0 } else { 1.0 },
recommendations: vec![
"Consider batching small transfers".to_string(),
"Use memory mapping for large transfers".to_string(),
"Implement caching for frequently accessed data".to_string(),
],
}
}
fn generate_mitigation_recommendations(
&self,
risk_assessment: &BoundaryRiskAssessment,
) -> Vec<String> {
let mut recommendations = Vec::new();
match risk_assessment.overall_risk_level {
RiskLevel::Critical => {
recommendations
.push("URGENT: Review and redesign boundary crossing strategy".to_string());
recommendations.push("Implement comprehensive input validation".to_string());
recommendations.push("Add runtime safety checks".to_string());
recommendations
.push("Consider using safer alternatives to raw pointers".to_string());
}
RiskLevel::High => {
recommendations.push("Implement additional safety checks".to_string());
recommendations.push("Add comprehensive logging and monitoring".to_string());
recommendations.push("Review memory ownership patterns".to_string());
}
RiskLevel::Medium => {
recommendations.push("Monitor boundary crossing frequency".to_string());
recommendations.push("Consider performance optimizations".to_string());
recommendations.push("Document ownership transfer clearly".to_string());
}
RiskLevel::Low => {
recommendations.push("Continue current practices".to_string());
recommendations.push("Periodic review recommended".to_string());
}
}
for factor in &risk_assessment.risk_factors {
recommendations.push(factor.mitigation.clone());
}
recommendations.dedup();
recommendations
}
pub fn get_boundary_event_statistics(&self) -> TrackingResult<BoundaryEventStatistics> {
let mut stats = BoundaryEventStatistics {
total_events: 0,
events_by_type: std::collections::HashMap::new(),
risk_distribution: std::collections::HashMap::new(),
average_transfer_size: 0.0,
total_transfer_volume: 0,
most_active_contexts: Vec::new(),
security_incidents: 0,
performance_issues: 0,
analysis_timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos(),
};
if let Ok(allocations) = self.enhanced_allocations.lock() {
let mut context_activity: std::collections::HashMap<String, usize> =
std::collections::HashMap::new();
let mut total_size = 0usize;
let mut event_count = 0usize;
for allocation in allocations.values() {
for event in &allocation.cross_boundary_events {
stats.total_events += 1;
event_count += 1;
*stats
.events_by_type
.entry(format!("{:?}", event.event_type))
.or_insert(0) += 1;
*context_activity
.entry(event.from_context.clone())
.or_insert(0) += 1;
*context_activity
.entry(event.to_context.clone())
.or_insert(0) += 1;
let estimated_size = allocation.base.size;
total_size += estimated_size;
}
}
if event_count > 0 {
stats.average_transfer_size = total_size as f64 / event_count as f64;
}
stats.total_transfer_volume = total_size;
let mut context_vec: Vec<_> = context_activity.into_iter().collect();
context_vec.sort_by(|a, b| b.1.cmp(&a.1));
stats.most_active_contexts = context_vec.into_iter().take(10).collect();
}
Ok(stats)
}
pub fn get_stats(&self) -> UnsafeFFIStats {
let allocations = self
.enhanced_allocations
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let violations = self
.violations
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let mut stats = UnsafeFFIStats::default();
for allocation in allocations.values() {
match &allocation.source {
AllocationSource::UnsafeRust { .. } => {
stats.unsafe_blocks += 1;
stats.total_operations += 1;
}
AllocationSource::FfiC { .. } => {
stats.ffi_calls += 1;
stats.total_operations += 1;
}
AllocationSource::CrossBoundary { .. } => {
stats.total_operations += 1;
}
_ => {}
}
stats.memory_violations += allocation.safety_violations.len();
}
stats.memory_violations += violations.len();
stats.risk_score = if stats.total_operations > 0 {
let base_risk = (stats.unsafe_blocks as f64 * 1.0)
+ (stats.ffi_calls as f64 * 2.0)
+ (stats.memory_violations as f64 * 5.0);
(base_risk / stats.total_operations as f64).min(10.0)
} else {
0.0
};
for allocation in allocations.values() {
let (op_type, risk_level, description) = match &allocation.source {
AllocationSource::UnsafeRust {
unsafe_block_location,
..
} => (
UnsafeOperationType::UnsafeBlock,
RiskLevel::Medium,
format!("Unsafe block at {unsafe_block_location}"),
),
AllocationSource::FfiC {
resolved_function, ..
} => (
UnsafeOperationType::FfiCall,
RiskLevel::High,
format!(
"FFI call to {}::{}",
resolved_function.library_name, resolved_function.function_name
),
),
AllocationSource::CrossBoundary { .. } => (
UnsafeOperationType::CrossBoundaryTransfer,
RiskLevel::Medium,
"Cross-boundary memory transfer".to_string(),
),
_ => continue,
};
stats.operations.push(UnsafeOperation {
operation_type: op_type,
location: "unknown".to_string(), risk_level,
timestamp: allocation.base.timestamp_alloc as u128,
description,
});
}
stats.operations.truncate(50);
stats
}
pub fn integrate_with_safety_analyzer(
&self,
safety_analyzer: &crate::analysis::SafetyAnalyzer,
) -> TrackingResult<()> {
let violations = if let Ok(violations) = self.violations.lock() {
violations.clone()
} else {
return Err(TrackingError::LockContention(
"Failed to lock violations".to_string(),
));
};
let allocations: Vec<AllocationInfo> =
if let Ok(enhanced_allocations) = self.enhanced_allocations.lock() {
enhanced_allocations
.values()
.map(|ea| ea.base.clone())
.collect()
} else {
return Err(TrackingError::LockContention(
"Failed to lock allocations".to_string(),
));
};
for violation in &violations {
let source = match violation {
SafetyViolation::DoubleFree { .. } => crate::analysis::UnsafeSource::RawPointer {
operation: "double_free".to_string(),
location: "memory_violation".to_string(),
},
SafetyViolation::InvalidFree {
attempted_pointer, ..
} => crate::analysis::UnsafeSource::RawPointer {
operation: "invalid_free".to_string(),
location: format!("0x{attempted_pointer:x}"),
},
SafetyViolation::PotentialLeak { .. } => {
crate::analysis::UnsafeSource::RawPointer {
operation: "potential_leak".to_string(),
location: "memory_leak".to_string(),
}
}
SafetyViolation::CrossBoundaryRisk { description, .. } => {
crate::analysis::UnsafeSource::FfiFunction {
library: "unknown".to_string(),
function: "cross_boundary".to_string(),
call_site: description.clone(),
}
}
};
let _report_id =
safety_analyzer.generate_unsafe_report(source, &allocations, &violations)?;
}
tracing::info!(
"🔗 Integrated {} violations with SafetyAnalyzer",
violations.len()
);
Ok(())
}
pub fn integrate_with_passport_tracker(
&self,
passport_tracker: &crate::analysis::MemoryPassportTracker,
) -> TrackingResult<()> {
if let Ok(enhanced_allocations) = self.enhanced_allocations.lock() {
for (ptr, allocation) in enhanced_allocations.iter() {
if allocation.ffi_tracked {
let type_name = allocation.base.type_name.clone();
let _passport_id = if type_name.is_none()
|| type_name
.as_ref()
.is_none_or(|t| t == "unknown" || t.is_empty())
{
passport_tracker.create_passport_with_inference(
*ptr,
allocation.base.size,
None,
"ffi_integration".to_string(),
allocation.base.var_name.clone(),
)?
} else {
passport_tracker.create_passport(
*ptr,
allocation.base.size,
"ffi_integration".to_string(),
type_name,
allocation.base.var_name.clone(),
)?
};
for event in &allocation.cross_boundary_events {
let event_type = match event.event_type {
BoundaryEventType::RustToFfi => {
crate::analysis::PassportEventType::HandoverToFfi
}
BoundaryEventType::FfiToRust => {
crate::analysis::PassportEventType::ReclaimedByRust
}
BoundaryEventType::OwnershipTransfer => {
crate::analysis::PassportEventType::OwnershipTransfer
}
BoundaryEventType::SharedAccess => {
crate::analysis::PassportEventType::BoundaryAccess
}
};
passport_tracker.record_passport_event(
*ptr,
event_type,
event.to_context.clone(),
std::collections::HashMap::new(),
)?;
}
}
}
}
tracing::info!("🔗 Integrated FFI allocations with MemoryPassportTracker");
Ok(())
}
pub fn perform_comprehensive_safety_analysis(
&self,
) -> TrackingResult<crate::analysis::ComprehensiveSafetyReport> {
let safety_analyzer = crate::analysis::SafetyAnalyzer::default();
let passport_tracker = crate::analysis::get_global_passport_tracker();
self.integrate_with_safety_analyzer(&safety_analyzer)?;
self.integrate_with_passport_tracker(&passport_tracker)?;
let leak_detection = passport_tracker.detect_leaks_at_shutdown();
let unsafe_reports = safety_analyzer.get_unsafe_reports();
let memory_passports = passport_tracker.get_all_passports();
let safety_stats = safety_analyzer.get_stats();
let passport_stats = passport_tracker.get_stats();
let report = crate::analysis::ComprehensiveSafetyReport {
unsafe_reports,
memory_passports,
leak_detection,
safety_stats,
passport_stats,
analysis_timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
};
tracing::info!("📊 Generated comprehensive safety analysis report");
Ok(report)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ComprehensiveSafetyReport {
pub unsafe_reports: std::collections::HashMap<String, crate::analysis::UnsafeReport>,
pub memory_passports: std::collections::HashMap<usize, crate::analysis::MemoryPassport>,
pub leak_detection: crate::analysis::LeakDetectionResult,
pub safety_stats: crate::analysis::SafetyAnalysisStats,
pub passport_stats: crate::analysis::PassportTrackerStats,
pub analysis_timestamp: u64,
}
static GLOBAL_UNSAFE_TRACKER: OnceLock<Arc<UnsafeFFITracker>> = OnceLock::new();
pub fn get_global_unsafe_tracker() -> Option<Arc<UnsafeFFITracker>> {
GLOBAL_UNSAFE_TRACKER.get().cloned()
}
pub fn init_global_unsafe_tracker() -> Arc<UnsafeFFITracker> {
GLOBAL_UNSAFE_TRACKER
.get_or_init(|| Arc::new(UnsafeFFITracker::new()))
.clone()
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
fn create_test_tracker() -> UnsafeFFITracker {
UnsafeFFITracker::new()
}
#[test]
fn test_unsafe_ffi_tracker_creation() {
let tracker = create_test_tracker();
assert!(tracker
.get_enhanced_allocations()
.expect("Should get enhanced allocations")
.is_empty());
assert!(tracker
.get_safety_violations()
.expect("Should get safety violations")
.is_empty());
assert!(tracker
.get_c_library_stats()
.expect("Should get C library stats")
.is_empty());
}
#[test]
fn test_unsafe_ffi_tracker_default() {
let tracker = UnsafeFFITracker::default();
assert!(tracker
.get_enhanced_allocations()
.expect("Should get enhanced allocations after reset")
.is_empty());
assert!(tracker
.get_safety_violations()
.expect("Should get safety violations after reset")
.is_empty());
}
#[test]
fn test_track_unsafe_allocation() {
let tracker = create_test_tracker();
let ptr = 0x1000;
let size = 1024;
let location = "test_file.rs:42:10".to_string();
let result = tracker.track_unsafe_allocation(ptr, size, location.clone());
assert!(result.is_ok());
let allocations = tracker.get_enhanced_allocations().unwrap();
assert_eq!(allocations.len(), 1);
let allocation = &allocations[0];
assert_eq!(allocation.base.ptr, ptr);
assert_eq!(allocation.base.size, size);
match &allocation.source {
AllocationSource::UnsafeRust {
unsafe_block_location,
..
} => {
assert_eq!(unsafe_block_location, &location);
}
_ => panic!("Expected UnsafeRust allocation source"),
}
}
#[test]
fn test_track_ffi_allocation() {
let tracker = create_test_tracker();
let ptr = 0x2000;
let size = 512;
let library_name = "libc".to_string();
let function_name = "malloc".to_string();
let result =
tracker.track_ffi_allocation(ptr, size, library_name.clone(), function_name.clone());
assert!(result.is_ok());
let allocations = tracker.get_enhanced_allocations().unwrap();
assert_eq!(allocations.len(), 1);
let allocation = &allocations[0];
assert_eq!(allocation.base.ptr, ptr);
assert_eq!(allocation.base.size, size);
assert!(allocation.ffi_tracked);
match &allocation.source {
AllocationSource::FfiC {
resolved_function, ..
} => {
assert_eq!(resolved_function.library_name, library_name);
assert_eq!(resolved_function.function_name, function_name);
}
_ => panic!("Expected FfiC allocation source"),
}
}
#[test]
fn test_track_enhanced_deallocation_valid() {
let tracker = create_test_tracker();
let ptr = 0x3000;
let size = 256;
tracker
.track_unsafe_allocation(ptr, size, "test_location".to_string())
.unwrap();
let result = tracker.track_enhanced_deallocation(ptr);
assert!(result.is_ok());
let allocations = tracker.get_enhanced_allocations().unwrap();
assert!(allocations.is_empty());
}
#[test]
fn test_track_enhanced_deallocation_invalid_free() {
let tracker = create_test_tracker();
let ptr = 0x4000;
let result = tracker.track_enhanced_deallocation(ptr);
assert!(result.is_err());
let violations = tracker.get_safety_violations().unwrap();
assert_eq!(violations.len(), 1);
match &violations[0] {
SafetyViolation::InvalidFree {
attempted_pointer, ..
} => {
assert_eq!(*attempted_pointer, ptr);
}
_ => panic!("Expected InvalidFree violation"),
}
}
#[test]
fn test_track_enhanced_deallocation_double_free() {
let tracker = create_test_tracker();
let ptr = 0x5000;
let size = 128;
tracker
.track_unsafe_allocation(ptr, size, "test_location".to_string())
.unwrap();
let result1 = tracker.track_enhanced_deallocation(ptr);
assert!(result1.is_ok());
let result2 = tracker.track_enhanced_deallocation(ptr);
assert!(result2.is_err());
let violations = tracker.get_safety_violations().unwrap();
assert_eq!(violations.len(), 1);
match &violations[0] {
SafetyViolation::DoubleFree { .. } => {
}
_ => panic!("Expected DoubleFree violation"),
}
}
#[test]
fn test_record_boundary_event() {
let tracker = create_test_tracker();
let ptr = 0x6000;
let size = 1024;
tracker
.track_unsafe_allocation(ptr, size, "test_location".to_string())
.unwrap();
let result = tracker.record_boundary_event(
ptr,
BoundaryEventType::RustToFfi,
"rust_context".to_string(),
"ffi_context".to_string(),
);
assert!(result.is_ok());
let allocations = tracker.get_enhanced_allocations().unwrap();
assert_eq!(allocations.len(), 1);
let allocation = &allocations[0];
assert_eq!(allocation.cross_boundary_events.len(), 1);
let event = &allocation.cross_boundary_events[0];
assert!(matches!(event.event_type, BoundaryEventType::RustToFfi));
assert_eq!(event.from_context, "rust_context");
assert_eq!(event.to_context, "ffi_context");
}
#[test]
fn test_register_c_library() {
let tracker = create_test_tracker();
let library_name = "test_lib".to_string();
let library_path = Some("/usr/lib/libtest.so".to_string());
let library_version = Some("1.0.0".to_string());
let result = tracker.register_c_library(
library_name.clone(),
library_path.clone(),
library_version.clone(),
);
assert!(result.is_ok());
let libraries = tracker.get_c_library_stats().unwrap();
assert_eq!(libraries.len(), 1);
let library = libraries.get(&library_name).unwrap();
assert_eq!(library.library_name, library_name);
assert_eq!(library.library_path, library_path);
assert_eq!(library.library_version, library_version);
assert_eq!(library.total_allocations, 0);
assert_eq!(library.total_bytes_allocated, 0);
}
#[test]
fn test_track_c_function_call() {
let tracker = create_test_tracker();
let library_name = "libc";
let function_name = "malloc";
let allocation_size = 1024;
let execution_time_ns = 500;
let result = tracker.track_c_function_call(
library_name,
function_name,
allocation_size,
execution_time_ns,
);
assert!(result.is_ok());
let libraries = tracker.get_c_library_stats().unwrap();
assert_eq!(libraries.len(), 1);
let library = libraries.get(library_name).unwrap();
assert_eq!(library.total_allocations, 1);
assert_eq!(library.total_bytes_allocated, allocation_size);
let function = library.functions_called.get(function_name).unwrap();
assert_eq!(function.call_count, 1);
assert_eq!(function.bytes_allocated, allocation_size);
assert_eq!(function.average_allocation_size, allocation_size as f64);
assert_eq!(
function.performance_metrics.avg_execution_time_ns,
execution_time_ns
);
}
#[test]
fn test_install_enhanced_libc_hook() {
let tracker = create_test_tracker();
let function_name = "malloc".to_string();
let hook_method = HookInstallationMethod::Preload;
let result = tracker.install_enhanced_libc_hook(function_name.clone(), hook_method);
assert!(result.is_ok());
let hooks = tracker.get_libc_hook_info().unwrap();
assert_eq!(hooks.len(), 1);
let hook = hooks.get(&function_name).unwrap();
assert_eq!(hook.base_info.original_function, function_name);
assert!(matches!(
hook.installation_details.installation_method,
HookInstallationMethod::Preload
));
assert!(hook.installation_details.installation_success);
}
#[test]
fn test_create_and_register_passport() {
let tracker = create_test_tracker();
let ptr = 0x7000;
let origin_context = "rust_main";
let security_clearance = SecurityClearance::Public;
let result = tracker.create_and_register_passport(ptr, origin_context, security_clearance);
assert!(result.is_ok());
let passport_id = result.unwrap();
assert!(!passport_id.is_empty());
let passports = tracker.get_memory_passports().unwrap();
assert_eq!(passports.len(), 1);
let passport = passports.get(&ptr).unwrap();
assert_eq!(passport.passport_id, passport_id);
assert_eq!(passport.origin.context, origin_context);
assert!(matches!(
passport.security_clearance,
SecurityClearance::Public
));
assert!(matches!(passport.validity_status, ValidityStatus::Valid));
}
#[test]
fn test_stamp_passport() {
let tracker = create_test_tracker();
let ptr = 0x8000;
tracker
.create_and_register_passport(ptr, "rust_main", SecurityClearance::Public)
.unwrap();
let result =
tracker.stamp_passport(ptr, "memory_access", "ffi_boundary", "UnsafeFFITracker");
assert!(result.is_ok());
let passports = tracker.get_memory_passports().unwrap();
let passport = passports.get(&ptr).unwrap();
assert_eq!(passport.journey.len(), 1);
let stamp = &passport.journey[0];
assert_eq!(stamp.operation, "memory_access");
assert_eq!(stamp.location, "ffi_boundary");
assert_eq!(stamp.authority, "UnsafeFFITracker");
}
#[test]
fn test_transfer_passport_ownership() {
let tracker = create_test_tracker();
let ptr = 0x9000;
tracker
.create_and_register_passport(ptr, "rust_main", SecurityClearance::Public)
.unwrap();
let result = tracker.transfer_passport_ownership(ptr, "ffi_context", "malloc");
assert!(result.is_ok());
let passports = tracker.get_memory_passports().unwrap();
let passport = passports.get(&ptr).unwrap();
assert_eq!(passport.current_owner.owner_context, "ffi_context");
assert_eq!(passport.current_owner.owner_function, "malloc");
assert_eq!(passport.journey.len(), 1);
assert_eq!(passport.journey[0].operation, "ownership_transfer");
}
#[test]
fn test_revoke_passport() {
let tracker = create_test_tracker();
let ptr = 0xa000;
tracker
.create_and_register_passport(ptr, "rust_main", SecurityClearance::Public)
.unwrap();
let result = tracker.revoke_passport(ptr, "memory_freed");
assert!(result.is_ok());
let passports = tracker.get_memory_passports().unwrap();
let passport = passports.get(&ptr).unwrap();
assert!(matches!(passport.validity_status, ValidityStatus::Revoked));
assert_eq!(passport.journey.len(), 1);
assert!(passport.journey[0].operation.contains("revoked"));
}
#[test]
fn test_validate_passport() {
let tracker = create_test_tracker();
let ptr = 0xb000;
tracker
.track_unsafe_allocation(ptr, 1024, "test_location".to_string())
.unwrap();
tracker
.create_or_update_passport(ptr, "create", "rust_main")
.unwrap();
let result = tracker.validate_passport(ptr);
assert!(result.is_ok());
let is_valid = result.unwrap();
assert!(is_valid);
let allocations = tracker.get_enhanced_allocations().unwrap();
assert_eq!(allocations.len(), 1);
let allocation = &allocations[0];
assert!(allocation.memory_passport.is_some());
let passport = allocation.memory_passport.as_ref().unwrap();
assert!(matches!(passport.validity_status, ValidityStatus::Valid));
let result = tracker.validate_passport(0xdead);
assert!(result.is_ok());
assert!(!result.unwrap());
let ptr2 = 0xc000;
tracker
.create_and_register_passport(ptr2, "rust_main", SecurityClearance::Public)
.unwrap();
let passports = tracker.get_memory_passports().unwrap();
assert_eq!(passports.len(), 1);
let passport = passports.get(&ptr2).unwrap();
assert!(matches!(passport.validity_status, ValidityStatus::Valid));
tracker.revoke_passport(ptr2, "test_revocation").unwrap();
let passports = tracker.get_memory_passports().unwrap();
let passport = passports.get(&ptr2).unwrap();
assert!(matches!(passport.validity_status, ValidityStatus::Revoked));
}
#[test]
fn test_detect_leaks() {
let tracker = create_test_tracker();
let ptr = 0xc000;
let size = 1024;
tracker
.track_unsafe_allocation(ptr, size, "test_location".to_string())
.unwrap();
std::thread::sleep(std::time::Duration::from_millis(10));
let result = tracker.detect_leaks(1); assert!(result.is_ok());
let leaks = result.unwrap();
if !leaks.is_empty() {
match &leaks[0] {
SafetyViolation::PotentialLeak { .. } => {
}
_ => panic!("Expected PotentialLeak violation"),
}
} else {
let allocations = tracker.get_enhanced_allocations().unwrap();
assert_eq!(allocations.len(), 1);
assert!(allocations[0].base.is_active());
}
}
#[test]
fn test_analyze_cross_boundary_risks() {
let tracker = create_test_tracker();
let ptr = 0xd000;
tracker
.create_and_register_passport(ptr, "rust_main", SecurityClearance::Public)
.unwrap();
for i in 0..15 {
tracker
.stamp_passport(ptr, &format!("operation_{i}"), "boundary", "test")
.unwrap();
}
let result = tracker.analyze_cross_boundary_risks();
assert!(result.is_ok());
let risks = result.unwrap();
assert!(!risks.is_empty());
let has_boundary_risk = risks
.iter()
.any(|risk| matches!(risk, SafetyViolation::CrossBoundaryRisk { .. }));
assert!(has_boundary_risk);
}
#[test]
fn test_get_stats() {
let tracker = create_test_tracker();
tracker
.track_unsafe_allocation(0x1000, 1024, "unsafe_block".to_string())
.unwrap();
tracker
.track_ffi_allocation(0x2000, 512, "libc".to_string(), "malloc".to_string())
.unwrap();
let stats = tracker.get_stats();
assert_eq!(stats.total_operations, 2);
assert_eq!(stats.unsafe_blocks, 1);
assert_eq!(stats.ffi_calls, 1);
assert_eq!(stats.memory_violations, 0);
assert!(stats.risk_score > 0.0);
assert_eq!(stats.operations.len(), 2);
}
#[test]
fn test_boundary_event_statistics() {
let tracker = create_test_tracker();
let ptr = 0xe000;
tracker
.track_unsafe_allocation(ptr, 1024, "test_location".to_string())
.unwrap();
tracker
.record_boundary_event(
ptr,
BoundaryEventType::RustToFfi,
"rust".to_string(),
"ffi".to_string(),
)
.unwrap();
tracker
.record_boundary_event(
ptr,
BoundaryEventType::FfiToRust,
"ffi".to_string(),
"rust".to_string(),
)
.unwrap();
let result = tracker.get_boundary_event_statistics();
assert!(result.is_ok());
let stats = result.unwrap();
assert_eq!(stats.total_events, 2);
assert!(stats.events_by_type.contains_key("RustToFfi"));
assert!(stats.events_by_type.contains_key("FfiToRust"));
assert!(stats.average_transfer_size > 0.0);
assert!(stats.total_transfer_volume > 0);
}
#[test]
fn test_risk_level_serialization() {
let risk_levels = vec![
RiskLevel::Low,
RiskLevel::Medium,
RiskLevel::High,
RiskLevel::Critical,
];
for risk_level in risk_levels {
let serialized = serde_json::to_string(&risk_level).expect("Failed to serialize");
let _deserialized: RiskLevel =
serde_json::from_str(&serialized).expect("Failed to deserialize");
}
}
#[test]
fn test_boundary_event_type_serialization() {
let event_types = vec![
BoundaryEventType::RustToFfi,
BoundaryEventType::FfiToRust,
BoundaryEventType::OwnershipTransfer,
BoundaryEventType::SharedAccess,
];
for event_type in event_types {
let serialized = serde_json::to_string(&event_type).expect("Failed to serialize");
let _deserialized: BoundaryEventType =
serde_json::from_str(&serialized).expect("Failed to deserialize");
}
}
#[test]
fn test_security_clearance_serialization() {
let clearances = vec![
SecurityClearance::Public,
SecurityClearance::Restricted,
SecurityClearance::Confidential,
SecurityClearance::Secret,
];
for clearance in clearances {
let serialized = serde_json::to_string(&clearance).expect("Failed to serialize");
let _deserialized: SecurityClearance =
serde_json::from_str(&serialized).expect("Failed to deserialize");
}
}
#[test]
fn test_global_tracker_initialization() {
let tracker1 = init_global_unsafe_tracker();
let tracker2 = init_global_unsafe_tracker();
assert!(Arc::ptr_eq(&tracker1, &tracker2));
}
#[test]
fn test_memory_protection_flags() {
let flags = MemoryProtectionFlags {
readable: true,
writable: true,
executable: false,
shared: false,
};
assert!(flags.readable);
assert!(flags.writable);
assert!(!flags.executable);
assert!(!flags.shared);
}
#[test]
fn test_allocation_metadata() {
let metadata = AllocationMetadata {
requested_size: 1024,
actual_size: 1024,
alignment: 8,
allocator_info: "test_allocator".to_string(),
protection_flags: Some(MemoryProtectionFlags {
readable: true,
writable: true,
executable: false,
shared: false,
}),
};
assert_eq!(metadata.requested_size, 1024);
assert_eq!(metadata.actual_size, 1024);
assert_eq!(metadata.alignment, 8);
assert!(metadata.protection_flags.is_some());
}
#[test]
fn test_comprehensive_workflow() {
let tracker = create_test_tracker();
tracker
.register_c_library(
"test_lib".to_string(),
Some("/lib/libtest.so".to_string()),
Some("1.0".to_string()),
)
.unwrap();
let ptr1 = 0x10000;
let ptr2 = 0x20000;
tracker
.track_unsafe_allocation(ptr1, 1024, "unsafe_block".to_string())
.unwrap();
tracker
.track_ffi_allocation(ptr2, 512, "test_lib".to_string(), "test_malloc".to_string())
.unwrap();
tracker
.create_and_register_passport(ptr1, "rust_main", SecurityClearance::Public)
.unwrap();
tracker
.create_and_register_passport(ptr2, "ffi_lib", SecurityClearance::Restricted)
.unwrap();
tracker
.record_boundary_event(
ptr1,
BoundaryEventType::RustToFfi,
"rust".to_string(),
"ffi".to_string(),
)
.unwrap();
tracker
.track_c_function_call("test_lib", "test_malloc", 512, 1000)
.unwrap();
tracker
.install_enhanced_libc_hook("malloc".to_string(), HookInstallationMethod::Preload)
.unwrap();
let allocations = tracker.get_enhanced_allocations().unwrap();
assert_eq!(allocations.len(), 2);
let libraries = tracker.get_c_library_stats().unwrap();
assert_eq!(libraries.len(), 1);
let passports = tracker.get_memory_passports().unwrap();
assert_eq!(passports.len(), 2);
let hooks = tracker.get_libc_hook_info().unwrap();
assert_eq!(hooks.len(), 1);
let stats = tracker.get_stats();
assert_eq!(stats.total_operations, 2);
assert_eq!(stats.unsafe_blocks, 1);
assert_eq!(stats.ffi_calls, 1);
tracker.track_enhanced_deallocation(ptr1).unwrap();
tracker.track_enhanced_deallocation(ptr2).unwrap();
let final_allocations = tracker.get_enhanced_allocations().unwrap();
assert!(final_allocations.is_empty());
}
}