use std::sync::{Arc, Barrier};
use std::thread;
use std::time::Duration;
use zipora::memory::{MemoryPool, PoolConfig};
#[test]
#[ignore] fn test_use_after_free_vulnerability() {
let pool = Arc::new(MemoryPool::new(PoolConfig::new(64, 10, 8)).unwrap());
let barrier = Arc::new(Barrier::new(2));
let pool1 = pool.clone();
let barrier1 = barrier.clone();
let handle1 = thread::spawn(move || {
let ptr = pool1.allocate().unwrap();
let raw_ptr = ptr.as_ptr(); let raw_addr = raw_ptr as usize;
unsafe {
for i in 0..8 {
raw_ptr.add(i).write(0xAA);
}
}
pool1.deallocate(ptr).unwrap();
barrier1.wait();
unsafe {
for i in 0..8 {
raw_ptr.add(i).write(0xDD);
}
}
raw_addr });
let pool2 = pool.clone();
let barrier2 = barrier.clone();
let handle2 = thread::spawn(move || {
barrier2.wait();
let ptr = pool2.allocate().unwrap();
let mut corrupted = false;
unsafe {
for i in 0..8 {
let value = ptr.as_ptr().add(i).read();
if value == 0xDD {
corrupted = true;
break;
}
}
}
pool2.deallocate(ptr).unwrap();
corrupted
});
let _raw_addr1 = handle1.join().unwrap();
let corrupted = handle2.join().unwrap();
if corrupted {
println!("VULNERABILITY CONFIRMED: Use-after-free detected!");
println!("Thread 2 read data written by Thread 1 AFTER deallocation");
}
}
#[test]
fn test_lost_deallocations_under_contention() {
let pool = Arc::new(MemoryPool::new(PoolConfig::new(64, 100, 8)).unwrap());
let initial_stats = pool.stats();
let handles: Vec<_> = (0..10)
.map(|_| {
let pool = pool.clone();
thread::spawn(move || {
let mut allocations = Vec::new();
for _ in 0..10 {
if let Ok(ptr) = pool.allocate() {
allocations.push(ptr);
}
}
for ptr in allocations {
let _ = pool.deallocate(ptr);
}
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
thread::sleep(Duration::from_millis(100));
let final_stats = pool.stats();
if final_stats.dealloc_count < 100 {
println!(
"WARNING: Only {} deallocations recorded out of 100",
final_stats.dealloc_count
);
println!("Lost deallocations: {}", 100 - final_stats.dealloc_count);
}
if final_stats.allocated > initial_stats.allocated {
println!(
"MEMORY LEAK DETECTED: {} bytes leaked",
final_stats.allocated - initial_stats.allocated
);
}
}
#[test]
fn test_stats_race_condition() {
let pool = Arc::new(MemoryPool::new(PoolConfig::new(64, 50, 8)).unwrap());
let barrier = Arc::new(Barrier::new(5));
let handles: Vec<_> = (0..5)
.map(|_| {
let pool = pool.clone();
let barrier = barrier.clone();
thread::spawn(move || {
barrier.wait();
for _ in 0..100 {
if let Ok(ptr) = pool.allocate() {
let _ = pool.deallocate(ptr);
}
}
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
let stats = pool.stats();
let expected_allocs = 500; let expected_deallocs = 500;
if stats.alloc_count != expected_allocs {
println!(
"Stats race detected: Expected {} allocations, got {}",
expected_allocs, stats.alloc_count
);
}
if stats.dealloc_count != expected_deallocs {
println!(
"Stats race detected: Expected {} deallocations, got {}",
expected_deallocs, stats.dealloc_count
);
}
let total_pool_ops = stats.pool_hits + stats.pool_misses;
if total_pool_ops != stats.alloc_count {
println!(
"Stats inconsistency: pool_hits({}) + pool_misses({}) != alloc_count({})",
stats.pool_hits, stats.pool_misses, stats.alloc_count
);
}
}
#[test]
fn test_double_free_attempt() {
let pool = Arc::new(MemoryPool::new(PoolConfig::new(64, 10, 8)).unwrap());
let ptr = pool.allocate().unwrap();
assert!(pool.deallocate(ptr).is_ok());
let result = pool.deallocate(ptr);
if result.is_ok() {
println!("VULNERABILITY: Double-free succeeded! Pool is now corrupted.");
let ptr1 = pool.allocate().unwrap();
let ptr2 = pool.allocate().unwrap();
if ptr1.as_ptr() == ptr.as_ptr() || ptr2.as_ptr() == ptr.as_ptr() {
println!("CRITICAL: Same memory allocated multiple times!");
}
}
}
#[test]
#[should_panic]
#[ignore] fn test_clear_during_active_use() {
let pool = Arc::new(MemoryPool::new(PoolConfig::new(64, 10, 8)).unwrap());
let pool1 = pool.clone();
let handle = thread::spawn(move || {
let mut ptrs = Vec::new();
for _ in 0..5 {
if let Ok(ptr) = pool1.allocate() {
ptrs.push(ptr);
unsafe {
ptr.as_ptr().write(42);
}
}
}
thread::sleep(Duration::from_millis(50));
for ptr in ptrs {
unsafe {
let _value = ptr.as_ptr().read();
}
}
});
thread::sleep(Duration::from_millis(25));
pool.clear().unwrap();
handle.join().unwrap();
}
#[test]
fn test_send_sync_safety() {
fn is_send<T: Send>() {}
fn is_sync<T: Sync>() {}
is_send::<MemoryPool>();
is_sync::<MemoryPool>();
println!("WARNING: MemoryPool implements Send/Sync despite containing raw pointers!");
println!("This violates Rust's memory safety guarantees.");
}
#[test]
fn demonstrate_toctou_vulnerability() {
let pool = Arc::new(MemoryPool::new(PoolConfig::new(64, 1, 8)).unwrap());
let barrier = Arc::new(Barrier::new(2));
let pool1 = pool.clone();
let barrier1 = barrier.clone();
let handle1 = thread::spawn(move || {
barrier1.wait();
match pool1.allocate() {
Ok(ptr) => Ok(ptr.as_ptr() as usize), Err(e) => Err(e),
}
});
let pool2 = pool.clone();
let barrier2 = barrier.clone();
let handle2 = thread::spawn(move || {
barrier2.wait();
match pool2.allocate() {
Ok(ptr) => Ok(ptr.as_ptr() as usize), Err(e) => Err(e),
}
});
let result1 = handle1.join().unwrap();
let result2 = handle2.join().unwrap();
if result1.is_ok() && result2.is_ok() {
println!("Potential TOCTOU: Both threads got allocation from single-chunk pool");
let stats = pool.stats();
if stats.pool_hits > 0 && stats.pool_misses > 0 {
println!("Confirmed: Stats show both pool hit and miss for concurrent access");
}
}
}