use turkos_os_frame_metadata::metadata::{ Backend, Error, MAX_PINS, OwnedRecord, PAGE_BYTES, Registry, }; struct Memory { pages: [[u8; PAGE_BYTES]; 4], used: [bool; 4], partial_zero: bool, fail_release: bool, fail_zero: bool, fail_read: bool, } impl Memory { fn new() -> Self { Self { pages: [[0xa5; PAGE_BYTES]; 4], used: [false; 4], partial_zero: false, fail_release: false, fail_zero: false, fail_read: false, } } fn slot(&self, address: u64) -> Result { let offset = address.checked_sub(0x4000_0000).ok_or(Error::Backend)?; if offset % PAGE_BYTES as u64 != 0 || offset / PAGE_BYTES as u64 >= 4 { return Err(Error::Backend); } let slot = (offset / PAGE_BYTES as u64) as usize; if !self.used[slot] { return Err(Error::Backend); } Ok(slot) } } impl Backend for Memory { fn allocate(&mut self) -> Result { let slot = self .used .iter() .position(|used| !used) .ok_or(Error::NoMemory)?; self.used[slot] = true; Ok(0x4000_0000 + slot as u64 * PAGE_BYTES as u64) } fn release(&mut self, address: u64) -> Result<(), Error> { if self.fail_release { return Err(Error::Backend); } let slot = self.slot(address)?; // Model a previous owner's private bytes being present on reuse. self.pages[slot].fill(0xa5); self.used[slot] = false; Ok(()) } fn zero_page(&mut self, address: u64) -> Result<(), Error> { let slot = self.slot(address)?; if self.fail_zero { return Err(Error::Backend); } if self.partial_zero { self.pages[slot][..64].fill(0); } else { self.pages[slot].fill(0); } Ok(()) } fn read_byte(&self, address: u64, offset: usize) -> Result { if self.fail_read { return Err(Error::Backend); } self.pages[self.slot(address)?] .get(offset) .copied() .ok_or(Error::Backend) } fn available_pages(&self) -> u64 { self.used.iter().filter(|used| !**used).count() as u64 } } #[test] fn os_frame_metadata_owner_positive() { let mut registry = Registry::<_, 2>::new(1, Memory::new()).unwrap(); let old = registry.allocate_zeroed(17).unwrap(); registry.release(old, 17).unwrap(); let new = registry.allocate_zeroed(18).unwrap(); assert_eq!(old.address(), new.address()); assert!(new.generation() > old.generation()); assert_eq!(registry.snapshot().live_pages, 1); } #[test] fn os_frame_metadata_owner_negative() { let mut registry = Registry::<_, 2>::new(1, Memory::new()).unwrap(); let old = registry.allocate_zeroed(17).unwrap(); registry.release(old, 17).unwrap(); let new = registry.allocate_zeroed(17).unwrap(); let before = registry.snapshot(); assert_eq!(registry.release(old, 17), Err(Error::Stale)); assert_eq!(registry.snapshot(), before); assert_eq!(registry.read_byte(new, 17, PAGE_BYTES - 1), Ok(0)); registry.release(new, 17).unwrap(); } #[test] fn os_frame_metadata_zero_positive() { let mut registry = Registry::<_, 1>::new(1, Memory::new()).unwrap(); let first = registry.allocate_zeroed(17).unwrap(); registry.release(first, 17).unwrap(); let next = registry.allocate_zeroed(18).unwrap(); for offset in 0..PAGE_BYTES { assert_eq!(registry.read_byte(next, 18, offset), Ok(0)); } } #[test] fn os_frame_metadata_zero_negative() { let mut memory = Memory::new(); memory.partial_zero = true; let mut registry = Registry::<_, 1>::new(1, memory).unwrap(); let before = registry.snapshot(); assert_eq!(registry.allocate_zeroed(17), Err(Error::Erasure)); assert_eq!(registry.snapshot(), before); } #[test] fn os_frame_metadata_pins_positive() { let mut registry = Registry::<_, 1>::new(1, Memory::new()).unwrap(); let handle = registry.allocate_zeroed(17).unwrap(); let mut first = registry.pin(handle, 17).unwrap(); let mut second = registry.pin(handle, 17).unwrap(); assert_eq!(registry.release(handle, 17), Err(Error::Pinned)); registry.unpin(&mut first, 17).unwrap(); assert_eq!(registry.release(handle, 17), Err(Error::Pinned)); registry.unpin(&mut second, 17).unwrap(); registry.release(handle, 17).unwrap(); assert_eq!(registry.snapshot().backend_free_pages, 4); assert_eq!(registry.release(handle, 17), Err(Error::Stale)); } #[test] fn os_frame_metadata_pins_negative() { let mut registry = Registry::<_, 1>::new(1, Memory::new()).unwrap(); let handle = registry.allocate_zeroed(17).unwrap(); let mut pins = (0..MAX_PINS) .map(|_| registry.pin(handle, 17).unwrap()) .collect::>(); let before = registry.snapshot(); assert!(matches!(registry.pin(handle, 17), Err(Error::PinCapacity))); assert_eq!(registry.snapshot(), before); registry.unpin(&mut pins[0], 17).unwrap(); let before = registry.snapshot(); assert_eq!(registry.unpin(&mut pins[0], 17), Err(Error::PinConsumed)); assert_eq!(registry.snapshot(), before); } #[test] fn os_frame_metadata_reclaim_positive() { let mut registry = Registry::<_, 2>::new(1, Memory::new()).unwrap(); let initial = registry.snapshot(); let a = registry.allocate_zeroed(1).unwrap(); let b = registry.allocate_zeroed(2).unwrap(); assert_ne!(a.address(), b.address()); assert_eq!(registry.snapshot().free_metadata, 0); registry.release(b, 2).unwrap(); registry.release(a, 1).unwrap(); assert_eq!(registry.snapshot(), initial); } #[test] fn os_frame_metadata_reclaim_negative() { let mut registry = Registry::<_, 1>::new(1, Memory::new()).unwrap(); let live = registry.allocate_zeroed(1).unwrap(); let before = registry.snapshot(); assert_eq!(registry.allocate_zeroed(2), Err(Error::Capacity)); assert_eq!(registry.snapshot(), before); assert_eq!(registry.read_byte(live, 1, 4095), Ok(0)); } #[test] fn foreign_authority_and_owner_cannot_read_or_free() { let mut first = Registry::<_, 1>::new(1, Memory::new()).unwrap(); let mut second = Registry::<_, 1>::new(2, Memory::new()).unwrap(); let handle = first.allocate_zeroed(17).unwrap(); assert_eq!(first.release(handle, 18), Err(Error::Foreign)); assert_eq!(first.read_byte(handle, 18, 0), Err(Error::Foreign)); let before = second.snapshot(); assert_eq!(second.release(handle, 17), Err(Error::Foreign)); assert_eq!(second.snapshot(), before); } #[test] fn failed_zero_or_read_does_not_publish_a_lease() { for read_failure in [false, true] { let mut memory = Memory::new(); memory.fail_zero = !read_failure; memory.fail_read = read_failure; let mut registry = Registry::<_, 1>::new(1, memory).unwrap(); let before = registry.snapshot(); assert_eq!(registry.allocate_zeroed(17), Err(Error::Backend)); assert_eq!(registry.snapshot(), before); } } #[test] fn failed_rollback_quarantines_backing_and_retains_metadata() { let mut memory = Memory::new(); memory.fail_zero = true; memory.fail_release = true; let mut registry = Registry::<_, 1>::new(1, memory).unwrap(); assert_eq!(registry.allocate_zeroed(17), Err(Error::Backend)); let snapshot = registry.snapshot(); assert_eq!(snapshot.live_pages, 0); assert_eq!(snapshot.quarantined_pages, 1); assert_eq!(snapshot.free_metadata, 0); assert_eq!(snapshot.backend_free_pages, 3); assert_eq!(registry.allocate_zeroed(18), Err(Error::Capacity)); assert_eq!(registry.snapshot(), snapshot); } #[test] fn failed_live_release_retains_the_valid_handle() { let mut memory = Memory::new(); memory.fail_release = true; let mut registry = Registry::<_, 1>::new(1, memory).unwrap(); let handle = registry.allocate_zeroed(17).unwrap(); let before = registry.snapshot(); assert_eq!(registry.release(handle, 17), Err(Error::Backend)); assert_eq!(registry.snapshot(), before); assert_eq!(registry.read_byte(handle, 17, 4095), Ok(0)); } #[test] fn invalid_owner_empty_metadata_and_out_of_bounds_access_are_closed() { assert!(matches!( Registry::<_, 1>::new(0, Memory::new()), Err(Error::Owner) )); let mut empty = Registry::<_, 0>::new(1, Memory::new()).unwrap(); let before = empty.snapshot(); assert_eq!(empty.allocate_zeroed(17), Err(Error::Capacity)); assert_eq!(empty.snapshot(), before); let mut registry = Registry::<_, 1>::new(1, Memory::new()).unwrap(); assert_eq!(registry.allocate_zeroed(0), Err(Error::Owner)); let handle = registry.allocate_zeroed(17).unwrap(); assert_eq!( registry.read_byte(handle, 17, PAGE_BYTES), Err(Error::BadAddress) ); } #[test] fn ownership_audit_contains_exact_live_and_quarantined_pages() { let mut registry = Registry::<_, 2>::new(1, Memory::new()).unwrap(); let a = registry.allocate_zeroed(17).unwrap(); let b = registry.allocate_zeroed(18).unwrap(); let mut pin = registry.pin(a, 17).unwrap(); let expected = [ OwnedRecord { address: a.address(), owner: 17, generation: a.generation(), pins: 1, quarantined: false, }, OwnedRecord { address: b.address(), owner: 18, generation: b.generation(), pins: 0, quarantined: false, }, ]; assert_eq!(registry.owned_records().collect::>(), expected); registry.unpin(&mut pin, 17).unwrap(); registry.release(a, 17).unwrap(); registry.release(b, 18).unwrap(); assert_eq!(registry.owned_records().count(), 0); let mut memory = Memory::new(); memory.fail_zero = true; memory.fail_release = true; let mut registry = Registry::<_, 1>::new(2, memory).unwrap(); assert_eq!(registry.allocate_zeroed(19), Err(Error::Backend)); assert_eq!( registry.owned_records().collect::>(), [OwnedRecord { address: 0x4000_0000, owner: 19, generation: 1, pins: 0, quarantined: true, }] ); }