use std::cell::Cell; use turkos_os_exceptions::exceptions::*; const LOW: u64 = 0x4000_0000; const HIGH: u64 = LOW + 65536; const ADDRESS: u64 = HIGH - 16 - FRAME_BYTES as u64; const USER: User = User { owner: 7, generation: 2, root: 0x5000_0000, asid: 3, }; fn stack() -> Stack { Stack::new(LOW, HIGH).unwrap() } fn frame(slot: u64, address: u64) -> Frame { let mut frame = Frame::EMPTY; frame.vector_slot = slot; frame.interrupted_sp = address + FRAME_BYTES as u64; frame.elr = if slot < 8 { 0x4008_0000 } else { 0x8000_0040 }; frame.spsr = match slot / 4 { 0 => 0x3c4, 1 => { if slot % 4 == 1 { 0x345 } else { 0x3c5 } } 2 => 0x3c0, _ => 0x3d0, }; frame.sp_el0 = if slot < 4 { LOW + 8192 } else { 0x8000_4000 }; frame } #[derive(Default)] struct Authority { owner_calls: Cell, entry_calls: Cell, stack_calls: Cell, fail_owner: bool, fail_entry: bool, fail_stack: bool, } impl ReturnAuthority for Authority { fn owner_root(&self, user: User) -> Result<(), Error> { self.owner_calls.set(self.owner_calls.get() + 1); if user != USER || self.fail_owner { Err(Error::Owner) } else { Ok(()) } } fn executable(&self, user: User, entry: u64) -> Result<(), Error> { self.entry_calls.set(self.entry_calls.get() + 1); if user != USER || self.fail_entry || !(0x80000000..0x80001000).contains(&entry) { Err(Error::Entry) } else { Ok(()) } } fn writable_stack(&self, user: User, sp: u64) -> Result<(), Error> { self.stack_calls.set(self.stack_calls.get() + 1); if user != USER || self.fail_stack || !(0x80003010..=0x80004000).contains(&sp) { Err(Error::Stack) } else { Ok(()) } } } #[test] fn all_sixteen_slots_have_exact_origin_kind_offset_and_base() { let origins = [ Origin::CurrentSp0, Origin::CurrentSpx, Origin::LowerAarch64, Origin::LowerAarch32, ]; let kinds = [Kind::Sync, Kind::Irq, Kind::Fiq, Kind::Serror]; for slot in 0..16 { let v = Vector::new(slot).unwrap(); assert_eq!(v.slot(), slot as u8); assert_eq!(v.origin(), origins[(slot / 4) as usize]); assert_eq!(v.kind(), kinds[(slot % 4) as usize]); assert_eq!(v.offset(), slot * 128); assert_eq!(v.address(0x40080000), Ok(0x40080000 + slot * 128)); } assert_eq!(Vector::new(16), Err(Error::Vector)); assert_eq!(Vector::new(u64::MAX), Err(Error::Vector)); for base in [0, 0x40080001, u64::MAX - 127] { assert_eq!(Vector::new(8).unwrap().address(base), Err(Error::Vector)); } } #[test] fn every_vector_source_mode_and_current_level_are_checked() { for slot in 0..12 { let good = frame(slot, ADDRESS); assert!(good.validate_header().is_ok()); let mut bad = good; bad.current_el = 8; assert_eq!(bad.validate_header(), Err(Error::WrongLevel)); bad = good; bad.spsr = (bad.spsr & !31) | if slot < 8 { 0 } else { 5 }; assert_eq!(bad.validate_header(), Err(Error::WrongOrigin)); } for slot in 12..16 { let good = frame(slot, ADDRESS); assert_eq!(good.validate_header(), Err(Error::UnsupportedOrigin)); let mut wrong = good; wrong.spsr = 0x3d3; assert_eq!(wrong.validate_header(), Err(Error::WrongOrigin)); } } #[test] fn abi_covers_all_gp_simd_status_and_canary_fields() { assert_eq!(FRAME_BYTES, 848); assert_eq!(FRAME_ALIGNMENT, 16); assert_eq!( [ FRAME_X3_OFFSET, FRAME_SP_OFFSET, FRAME_SP_EL0_OFFSET, FRAME_ELR_OFFSET, FRAME_SPSR_OFFSET, FRAME_ESR_OFFSET, FRAME_FAR_OFFSET, FRAME_SLOT_OFFSET, FRAME_Q_OFFSET, FRAME_FPCR_OFFSET, FRAME_FPSR_OFFSET, FRAME_CURRENT_EL_OFFSET, FRAME_CANARY_OFFSET, FRAME_PADDING_OFFSET ], [ 24, 248, 256, 264, 272, 280, 288, 296, 304, 816, 820, 824, 832, 840 ] ); let mut original = frame(8, ADDRESS); for (i, x) in original.x.iter_mut().enumerate() { *x = 0x1234_0000 + i as u64; } for (i, q) in original.q.iter_mut().enumerate() { *q = [i as u64, !(i as u64)]; } original.fpcr = 0x123; original.fpsr = 0x456; let snapshot = original; original.x.fill(0); original.q.fill([0; 2]); original = snapshot; assert_eq!((original.x[0], original.x[30]), (0x12340000, 0x1234001e)); assert_eq!((original.q[0], original.q[31]), ([0, u64::MAX], [31, !31])); assert_eq!((original.fpcr, original.fpsr), (0x123, 0x456)); } #[test] fn raw_frame_bounds_are_checked_before_any_reference() { let good = stack().frame_address(ADDRESS).unwrap(); assert_eq!(good.address(), ADDRESS); for value in [ 0, LOW, LOW + 16, ADDRESS + 1, HIGH - 16, HIGH, u64::MAX - 15, ] { assert_eq!(stack().frame_address(value), Err(Error::Frame)); } for (low, high) in [ (0, HIGH), (LOW + 1, HIGH), (LOW, LOW + 848), (HIGH, LOW), (LOW, HIGH + 1), ] { assert_eq!(Stack::new(low, high), Err(Error::Stack)); } } #[test] fn all_ingress_sources_use_their_correct_stack_pointer() { for slot in 0..12 { let mut good = frame(slot, ADDRESS); assert!(validate_ingress(&good, ADDRESS, stack()).is_ok()); good.interrupted_sp -= 16; assert_eq!(validate_ingress(&good, ADDRESS, stack()), Err(Error::Stack)); } let mut lower = frame(8, ADDRESS); lower.sp_el0 += 8; assert_eq!( validate_ingress(&lower, ADDRESS, stack()), Err(Error::Stack) ); let mut sp0 = frame(0, ADDRESS); sp0.sp_el0 = 0x80004000; assert_eq!(validate_ingress(&sp0, ADDRESS, stack()), Err(Error::Stack)); } #[test] fn frame_canary_padding_and_bad_slot_refuse_ingress() { let good = frame(8, ADDRESS); let mut bad = good; bad.canary ^= 1; assert_eq!(validate_ingress(&bad, ADDRESS, stack()), Err(Error::Canary)); bad = good; bad.padding = 1; assert_eq!(validate_ingress(&bad, ADDRESS, stack()), Err(Error::Frame)); bad = good; bad.vector_slot = 16; assert_eq!(validate_ingress(&bad, ADDRESS, stack()), Err(Error::Vector)); } #[test] fn user_return_requires_trusted_owner_rx_entry_and_rw_stack() { let authority = Authority::default(); let good = frame(8, ADDRESS); let receipt = authorize_user_return(&good, ADDRESS, stack(), USER, &authority).unwrap(); assert_eq!( ( receipt.frame_address(), receipt.user(), receipt.entry(), receipt.stack_pointer() ), (ADDRESS, USER, 0x80000040, 0x80004000) ); assert_eq!( ( authority.owner_calls.get(), authority.entry_calls.get(), authority.stack_calls.get() ), (1, 1, 1) ); } #[test] fn forged_user_el1_status_never_reaches_return_authority() { let authority = Authority::default(); let mut bad = frame(8, ADDRESS); bad.spsr |= 5; assert_eq!( authorize_user_return(&bad, ADDRESS, stack(), USER, &authority), Err(Error::WrongOrigin) ); assert_eq!( ( authority.owner_calls.get(), authority.entry_calls.get(), authority.stack_calls.get() ), (0, 0, 0) ); assert_eq!( authorize_kernel_irq_return(&bad, ADDRESS, stack(), 0x40080000, 0x40090000), Err(Error::WrongOrigin) ); } #[test] fn optional_reserved_status_and_aarch32_returns_are_refused() { let authority = Authority::default(); for bit in [ 1 << 20, 1 << 21, 1 << 22, 1 << 23, 1 << 24, 1 << 25, 1u64 << 63, ] { let mut bad = frame(8, ADDRESS); bad.spsr |= bit; assert_eq!( authorize_user_return(&bad, ADDRESS, stack(), USER, &authority), Err(Error::Status) ); } let mut bad = frame(8, ADDRESS); bad.spsr |= 16; assert_eq!( authorize_user_return(&bad, ADDRESS, stack(), USER, &authority), Err(Error::WrongOrigin) ); assert_eq!(authority.owner_calls.get(), 0); } #[test] fn invalid_user_sp_and_pc_refuse_before_owner_lookup() { for sp in [0, 0x80004008] { let authority = Authority::default(); let mut bad = frame(8, ADDRESS); bad.sp_el0 = sp; assert_eq!( authorize_user_return(&bad, ADDRESS, stack(), USER, &authority), Err(Error::Stack) ); assert_eq!(authority.owner_calls.get(), 0); } for pc in [0, 0x80000042] { let authority = Authority::default(); let mut bad = frame(8, ADDRESS); bad.elr = pc; assert_eq!( authorize_user_return(&bad, ADDRESS, stack(), USER, &authority), Err(Error::Entry) ); assert_eq!(authority.owner_calls.get(), 0); } } #[test] fn zero_owner_generation_and_malformed_root_never_lookup_backing() { for user in [ User { owner: 0, ..USER }, User { generation: 0, ..USER }, ] { let authority = Authority::default(); assert_eq!( authorize_user_return(&frame(8, ADDRESS), ADDRESS, stack(), user, &authority), Err(Error::Owner) ); assert_eq!(authority.owner_calls.get(), 0); } for user in [ User { root: 0, ..USER }, User { root: USER.root + 1, ..USER }, User { root: 1 << 48, ..USER }, User { asid: 0, ..USER }, ] { let authority = Authority::default(); assert_eq!( authorize_user_return(&frame(8, ADDRESS), ADDRESS, stack(), user, &authority), Err(Error::Root) ); assert_eq!(authority.owner_calls.get(), 0); } } #[test] fn stale_owner_or_root_stops_before_page_walks() { let authority = Authority::default(); for user in [ User { owner: USER.owner + 1, ..USER }, User { generation: USER.generation + 1, ..USER }, User { root: USER.root + 4096, ..USER }, User { asid: USER.asid + 1, ..USER }, ] { assert_eq!( authorize_user_return(&frame(8, ADDRESS), ADDRESS, stack(), user, &authority), Err(Error::Owner) ); } assert_eq!( (authority.entry_calls.get(), authority.stack_calls.get()), (0, 0) ); } #[test] fn executable_and_stack_backing_failures_are_distinct() { let no_entry = Authority { fail_entry: true, ..Authority::default() }; assert_eq!( authorize_user_return(&frame(8, ADDRESS), ADDRESS, stack(), USER, &no_entry), Err(Error::Entry) ); assert_eq!(no_entry.stack_calls.get(), 0); let no_stack = Authority { fail_stack: true, ..Authority::default() }; assert_eq!( authorize_user_return(&frame(8, ADDRESS), ADDRESS, stack(), USER, &no_stack), Err(Error::Stack) ); assert_eq!(no_stack.stack_calls.get(), 1); let no_owner = Authority { fail_owner: true, ..Authority::default() }; assert_eq!( authorize_user_return(&frame(8, ADDRESS), ADDRESS, stack(), USER, &no_owner), Err(Error::Owner) ); assert_eq!(no_owner.entry_calls.get(), 0); } #[test] fn current_el_irq_has_separate_kernel_return_authority() { let good = frame(5, ADDRESS); let receipt = authorize_kernel_irq_return(&good, ADDRESS, stack(), 0x40080000, 0x40090000).unwrap(); assert_eq!( (receipt.entry(), receipt.stack_pointer()), (good.elr, good.interrupted_sp) ); for slot in [4, 8, 9] { assert_eq!( authorize_kernel_irq_return( &frame(slot, ADDRESS), ADDRESS, stack(), 0x40080000, 0x40090000 ), Err(Error::WrongOrigin) ); } let mut bad = good; bad.spsr |= 128; assert_eq!( authorize_kernel_irq_return(&bad, ADDRESS, stack(), 0x40080000, 0x40090000), Err(Error::Status) ); bad = good; bad.elr = 0x80000000; assert_eq!( authorize_kernel_irq_return(&bad, ADDRESS, stack(), 0x40080000, 0x40090000), Err(Error::Entry) ); } #[test] fn nested_syscall_and_irq_frames_are_disjoint_and_lifo() { let mut controller = Controller::<2>::new(1, stack()).unwrap(); let mut svc = controller.enter(&frame(8, ADDRESS), ADDRESS).unwrap(); let inner = ADDRESS - 4096; let mut irq = controller.enter(&frame(5, inner), inner).unwrap(); assert_eq!( (svc.depth(), irq.depth(), irq.frame_address()), (1, 2, inner) ); let before = controller.snapshot(); assert_eq!(controller.leave(&mut svc), Err(Error::Order)); assert_eq!(controller.snapshot(), before); controller.leave(&mut irq).unwrap(); assert_eq!(controller.snapshot().depth, 1); controller.leave(&mut svc).unwrap(); assert_eq!( controller.snapshot(), NestingSnapshot { depth: 0, issued: 2, completed: 2, high_water: 2, fatal_entries: 0 } ); } #[test] fn nesting_capacity_refusal_changes_neither_tokens_nor_snapshot() { let mut controller = Controller::<2>::new(1, stack()).unwrap(); let mut first = controller.enter(&frame(8, ADDRESS), ADDRESS).unwrap(); let mut second = controller .enter(&frame(5, ADDRESS - 4096), ADDRESS - 4096) .unwrap(); let before = controller.snapshot(); assert_eq!( controller.enter(&frame(4, ADDRESS - 8192), ADDRESS - 8192), Err(Error::Nesting) ); assert_eq!(controller.snapshot(), before); controller.leave(&mut second).unwrap(); controller.leave(&mut first).unwrap(); } #[test] fn overlapping_live_frames_are_rejected_before_issue() { let mut controller = Controller::<2>::new(1, stack()).unwrap(); let mut first = controller.enter(&frame(8, ADDRESS), ADDRESS).unwrap(); let before = controller.snapshot(); let overlap = ADDRESS - 16; assert_eq!( controller.enter(&frame(5, overlap), overlap), Err(Error::Frame) ); assert_eq!(controller.snapshot(), before); controller.leave(&mut first).unwrap(); } #[test] fn foreign_and_consumed_entry_tokens_never_unwind_another_frame() { let mut a = Controller::<2>::new(1, stack()).unwrap(); let mut b = Controller::<2>::new(2, stack()).unwrap(); let mut token = a.enter(&frame(8, ADDRESS), ADDRESS).unwrap(); let mut other = b.enter(&frame(8, ADDRESS), ADDRESS).unwrap(); let before = b.snapshot(); assert_eq!(b.leave(&mut token), Err(Error::Foreign)); assert_eq!(b.snapshot(), before); a.leave(&mut token).unwrap(); assert_eq!(a.leave(&mut token), Err(Error::Consumed)); b.leave(&mut other).unwrap(); } #[test] fn invalid_frame_never_registers_nesting() { let mut controller = Controller::<2>::new(1, stack()).unwrap(); let before = controller.snapshot(); let mut bad = frame(8, ADDRESS); bad.canary = 0; assert_eq!(controller.enter(&bad, ADDRESS), Err(Error::Canary)); assert_eq!(controller.snapshot(), before); bad = frame(8, ADDRESS); bad.spsr |= 5; assert_eq!(controller.enter(&bad, ADDRESS), Err(Error::WrongOrigin)); assert_eq!(controller.snapshot(), before); } #[test] fn fatal_irq_reentry_stops_once_then_parks_silently() { let mut controller = Controller::<2>::new(1, stack()).unwrap(); let mut outer = controller.enter(&frame(8, ADDRESS), ADDRESS).unwrap(); let mut irq = controller .enter(&frame(5, ADDRESS - 4096), ADDRESS - 4096) .unwrap(); assert_eq!( controller.begin_fatal(usize::MAX), FatalAction::Report { polls: 4096 } ); assert_eq!(controller.begin_fatal(4096), FatalAction::StopOnce); assert_eq!(controller.begin_fatal(4096), FatalAction::SilentPark); assert_eq!(controller.snapshot().fatal_entries, 2); assert_eq!(controller.snapshot().depth, 2); assert_eq!(controller.leave(&mut irq), Err(Error::Fatal)); assert_eq!(controller.leave(&mut outer), Err(Error::Fatal)); assert_eq!( controller.enter(&frame(4, ADDRESS - 8192), ADDRESS - 8192), Err(Error::Fatal) ); } #[derive(Default)] struct Sink { calls: usize, accept: bool, bytes: Vec, } impl FatalSink for Sink { fn try_byte(&mut self, byte: u8) -> bool { self.calls += 1; if self.accept { self.bytes.push(byte); } self.accept } } #[test] fn unavailable_fatal_transport_stops_at_the_real_poll_bound() { let mut sink = Sink::default(); assert_eq!( emit_bounded(&mut sink, b"fatal", usize::MAX), Emission { bytes: 0, polls: 4096, complete: false } ); assert_eq!(sink.calls, 4096); assert!(sink.bytes.is_empty()); let mut zero = Sink::default(); assert_eq!( emit_bounded(&mut zero, b"fatal", 0), Emission { bytes: 0, polls: 0, complete: false } ); assert_eq!(zero.calls, 0); } #[test] fn bounded_transport_reports_exact_partial_and_complete_bytes() { let mut sink = Sink { accept: true, ..Sink::default() }; assert_eq!( emit_bounded(&mut sink, b"fatal", 3), Emission { bytes: 3, polls: 3, complete: false } ); assert_eq!(sink.bytes, b"fat"); let mut sink = Sink { accept: true, ..Sink::default() }; assert_eq!( emit_bounded(&mut sink, b"fatal", 5), Emission { bytes: 5, polls: 5, complete: true } ); assert_eq!(sink.bytes, b"fatal"); } #[test] fn invalid_controller_capacity_and_namespace_are_refused() { assert!(matches!( Controller::<0>::new(1, stack()), Err(Error::Configuration) )); assert!(matches!( Controller::<5>::new(1, stack()), Err(Error::Configuration) )); assert!(matches!( Controller::<2>::new(0, stack()), Err(Error::Configuration) )); }