//! The user's 5 October K501–K600 contract has its own test namespace. //! Older historical K numbers are not silently relabelled as this contract. use crate::{bootinfo, memory}; use bootinfo::BootInfoV1; use memory::{FrameAllocator, FrameRequestError, FreeMap, PAGE_SIZE}; fn descriptor(kind: u32, start: u64, pages: u64) -> [u8; 48] { let mut raw = [0; 48]; raw[..4].copy_from_slice(&kind.to_le_bytes()); raw[8..16].copy_from_slice(&start.to_le_bytes()); raw[24..32].copy_from_slice(&pages.to_le_bytes()); raw } fn payload() -> BootInfoV1 { let mut info = BootInfoV1::empty(); info.memory_map_ptr = 0x4100_0000; info.memory_map_bytes = 96; info.descriptor_bytes = 48; info.descriptor_version = 1; info.kernel_phys_start = 0x4008_0000; info.kernel_file_bytes = 0x10000; info.dtb_phys_addr = 0x4200_0000; info } fn admit(info: &BootInfoV1, address: u64) -> Result<(), bootinfo::BootInfoHandoffError> { info.validate_handoff( address, 0x4000_0000, 256 * 1024 * 1024, 0x4008_0000, 0x20000, 0x4200_0000, 0x10000, ) } #[test] fn p09_k501_abi_offsets_are_exact() { assert_eq!(core::mem::size_of::(), 72); assert_eq!(core::mem::align_of::(), 8); assert_eq!(core::mem::offset_of!(BootInfoV1, memory_map_ptr), 16); assert_eq!(core::mem::offset_of!(BootInfoV1, dtb_phys_addr), 56); assert_eq!(core::mem::offset_of!(BootInfoV1, flags), 64); } #[test] fn p09_k502_payload_allocations_are_disjoint() { let info = payload(); assert_eq!(admit(&info, 0x4300_0000), Ok(())); assert!(admit(&info, info.memory_map_ptr).is_err()); assert!(admit(&info, info.kernel_phys_start).is_err()); } #[test] fn p09_k503_unaligned_pointer_is_rejected() { assert!(bootinfo::validate_uefi_boot_info_pointer(0x4300_0001).is_err()); assert!(bootinfo::validate_uefi_boot_info_pointer(0).is_err()); assert!(bootinfo::validate_uefi_boot_info_pointer(u64::MAX - 7).is_err()); assert!(bootinfo::validate_uefi_boot_info_pointer(0x4300_0000).is_ok()); } #[test] fn p09_k504_bad_header_never_admits_payload() { for field in 0..3 { let mut info = payload(); match field { 0 => info.magic[0] ^= 1, 1 => info.version += 1, _ => info.struct_bytes -= 8, } assert!(admit(&info, 0x4300_0000).is_err()); } } #[test] fn p09_k505_owned_copy_survives_source_mutation() { let mut source = descriptor(7, 0x4000_0000, 16); let mut owned = [0xa5; 96]; assert_eq!( turkos_bootinfo::copy_owned_memory_map(&source, &mut owned, 48), Ok(48) ); let captured = owned; source.fill(0xff); assert_eq!(owned, captured); assert!(owned[48..].iter().all(|b| *b == 0)); assert_eq!( bootinfo::parse_memory_descriptor(&owned[..48]) .unwrap() .kind, 7 ); } #[test] fn p09_k506_copy_length_stride_and_capacity_fail_closed() { let source = descriptor(7, 0x4000_0000, 16); for stride in [0, 16, 41, 56] { let mut owned = [0xa5; 96]; assert!(turkos_bootinfo::copy_owned_memory_map(&source, &mut owned, stride).is_err()); assert_eq!(owned, [0xa5; 96]); } let mut short = [0xa5; 40]; assert!(turkos_bootinfo::copy_owned_memory_map(&source, &mut short, 48).is_err()); assert_eq!(short, [0xa5; 40]); } #[test] fn p09_k507_dtb_pointer_is_bound_to_observed_dtb() { let mut info = payload(); assert_eq!(admit(&info, 0x4300_0000), Ok(())); info.dtb_phys_addr += PAGE_SIZE; assert!(admit(&info, 0x4300_0000).is_err()); } #[test] fn p09_k508_image_payload_overlap_is_rejected() { let mut info = payload(); info.memory_map_ptr = info.kernel_phys_start; assert!(admit(&info, 0x4300_0000).is_err()); info = payload(); info.memory_map_ptr = info.dtb_phys_addr; assert!(admit(&info, 0x4300_0000).is_err()); } #[test] fn p09_k511_all_firmware_types_have_conservative_authority() { for kind in 0..=16 { let raw = descriptor(kind, 0x4000_0000, 16); let result = FreeMap::from_uefi_conventional(0x4000_0000, 16 * PAGE_SIZE, &raw, 48); if kind == 7 { assert_eq!(result.unwrap().total_pages(), 16); } else { assert!(result.is_err(), "type {kind} became free RAM"); } } } #[test] fn p09_k512_unknown_type_never_becomes_allocatable() { let mut raw = [0; 96]; raw[..48].copy_from_slice(&descriptor(7, 0x4000_0000, 1)); raw[48..].copy_from_slice(&descriptor(u32::MAX, 0x4000_1000, 15)); let mut alloc = FrameAllocator::new( FreeMap::from_uefi_conventional(0x4000_0000, 16 * PAGE_SIZE, &raw, 48).unwrap(), ); assert_eq!(alloc.allocate(), Some(0x4000_0000)); assert_eq!(alloc.allocate(), None); assert_eq!( alloc.allocate_at(0x4000_1000), Err(FrameRequestError::OutsideAuthority) ); assert_eq!(alloc.remaining_pages(), 0); } #[test] fn p09_k513_descriptor_overflow_and_overlap_are_rejected() { assert!(bootinfo::parse_memory_descriptor(&descriptor(7, u64::MAX - 4095, 1)).is_err()); assert!(bootinfo::parse_memory_descriptor(&descriptor(7, 0, u64::MAX)).is_err()); let mut raw = [0; 96]; raw[..48].copy_from_slice(&descriptor(2, 0x4100_0000, 16)); raw[48..].copy_from_slice(&descriptor(7, 0x4100_1000, 1)); assert_eq!( bootinfo::inspect_uefi_memory_map(&payload(), 0x4100_0000, &raw), Err(bootinfo::MapError::Overlap) ); } #[test] fn p09_k515_reserved_pages_are_never_requested_or_returned() { let mut free = FreeMap::from_ram(0x4000_0000, 256 * PAGE_SIZE).unwrap(); for (address, bytes) in [ (0x4000_0000, PAGE_SIZE), (0x4008_0000, 2 * PAGE_SIZE), (0x400a_0000, 16 * PAGE_SIZE), ] { free.reserve(address, bytes).unwrap(); } let mut alloc = FrameAllocator::new(free); let before = alloc.remaining_pages(); for address in [0x4000_0000, 0x4008_0000, 0x400a_0000, 0x400a_f000] { assert_eq!( alloc.allocate_at(address), Err(FrameRequestError::OutsideAuthority) ); assert!(alloc.release(address).is_err()); assert_eq!(alloc.remaining_pages(), before); } } #[test] fn p09_k516_exact_first_and_last_page_preserve_accounting() { let mut alloc = FrameAllocator::new(FreeMap::from_ram(0x1000, 3 * PAGE_SIZE).unwrap()); assert_eq!(alloc.allocate_at(0x1000), Ok(0x1000)); assert_eq!(alloc.allocate_at(0x3000), Ok(0x3000)); assert_eq!(alloc.remaining_pages(), 1); alloc.release_many(&[0x1000, 0x3000]).unwrap(); assert_eq!(alloc.remaining_pages(), 3); } #[test] fn p09_k517_foreign_busy_unaligned_and_double_return_fail() { let mut alloc = FrameAllocator::new(FreeMap::from_ram(0x1000, 3 * PAGE_SIZE).unwrap()); assert_eq!( alloc.allocate_at(0), Err(FrameRequestError::OutsideAuthority) ); assert_eq!( alloc.allocate_at(0x1001), Err(FrameRequestError::InvalidAddress) ); assert_eq!( alloc.allocate_at(u64::MAX - 4095), Err(FrameRequestError::InvalidAddress) ); assert_eq!(alloc.allocate_at(0x2000), Ok(0x2000)); assert_eq!(alloc.allocate_at(0x2000), Err(FrameRequestError::NotFree)); assert_eq!(alloc.remaining_pages(), 2); alloc.release(0x2000).unwrap(); assert!(alloc.release(0x2000).is_err()); assert_eq!(alloc.remaining_pages(), 3); } /// K586 proposed patch: replay the reduced inputs of four real kernel/fuzz DTB /// findings and one BootInfo finding (decoded exactly as kernel/fuzz does). #[test] fn p15_k586_open_fuzz_findings_regression() { extern crate std; use sha2::{Digest, Sha256}; use std::{format, string::String, vec::Vec}; let corpus = include_str!("../tests/corpus/fuzz-k586-open-v1.txt"); let mut count = 0; let mut mismatches = Vec::new(); for line in corpus .lines() .filter(|l| !l.is_empty() && !l.starts_with('#')) { let f: Vec<&str> = line.split('|').collect(); assert_eq!(f.len(), 6); let bytes: Vec = f[4] .as_bytes() .chunks_exact(2) .map(|p| u8::from_str_radix(core::str::from_utf8(p).unwrap(), 16).unwrap()) .collect(); let digest: String = Sha256::digest(&bytes) .iter() .map(|b| format!("{b:02x}")) .collect(); assert_eq!(digest, f[3], "{}", f[0]); let observed = match f[1] { "dtb" => { let (walker, _) = f[5].split_once('=').unwrap(); let result = match walker { "discover" => crate::dtb::discover(&bytes).map(|_| ()), "discover_interrupts" => crate::dtb::discover_interrupts(&bytes).map(|_| ()), "discover_virtio_mmio" => crate::dtb::discover_virtio_mmio(&bytes).map(|_| ()), other => panic!("unknown walker {other}"), }; format!( "{walker}={}", result.map_or_else(|e| format!("{e:?}"), |_| String::from("Ok")) ) } "bootinfo" => { let at = |i: usize| bytes.get(i).copied().unwrap_or(0); let le = |start: usize, n: usize| { (0..n).fold(0u64, |v, i| v | (u64::from(at(start + i)) << (8 * i))) }; let mut info = BootInfoV1::empty(); for (index, byte) in info.magic.iter_mut().enumerate() { *byte = at(index) ^ bootinfo::BootInfoV1::empty().magic[index]; } info.version = le(8, 4) as u32 ^ 1; info.struct_bytes = le(12, 4) as u32 ^ 72; info.memory_map_ptr = le(16, 8); info.memory_map_bytes = le(24, 8); info.descriptor_bytes = le(32, 4) as u32; info.descriptor_version = le(36, 4) as u32; info.kernel_phys_start = le(40, 8); info.kernel_file_bytes = le(48, 8); info.dtb_phys_addr = le(56, 8); info.flags = le(64, 8); let v: Vec = (0..7).map(|i| le(72 + i * 8, 8)).collect(); let result = info.validate_handoff(v[0], v[1], v[2], v[3], v[4], v[5], v[6]); format!( "validate_handoff={}", result.map_or_else(|e| format!("{e:?}"), |_| String::from("Ok")) ) } other => panic!("unknown target {other}"), }; std::eprintln!("K586-OPEN {} {} {}", f[0], observed, f[3]); if observed != f[5] { mismatches.push(format!("{}: {observed} != {}", f[0], f[5])); } count += 1; } assert_eq!(count, 5); assert!( mismatches.is_empty(), "accepted fuzz findings: {mismatches:?}" ); }