refactor(proofs): split into per-topic modules with shared helpers across types, domain, and kernel

This commit is contained in:
2026-10-18 01:08:05 +02:00
parent e5fd6372b3
commit 71518a93a4
16 changed files with 554 additions and 607 deletions

View File

@@ -0,0 +1,37 @@
use crate::capability::CapabilityTable;
use crate::resource::Resource;
use super::{arbitrary_endpoint_cap, insert_cap};
use types::permission::PermissionSet;
#[kani::proof]
fn cap_table_insert_get_roundtrip() {
let bits: u8 = kani::any();
let ep_id: u16 = kani::any();
kani::assume(ep_id < 256);
let perms = PermissionSet::from_bits(bits);
let cap = arbitrary_endpoint_cap(bits, ep_id);
let mut table = CapabilityTable::new();
let slot = insert_cap(&mut table, cap);
let retrieved = table.get(slot).unwrap();
assert!(*retrieved.permissions() == perms);
}
#[kani::proof]
fn cap_table_remove_then_get_fails() {
let bits: u8 = kani::any();
let cap = crate::capability::Capability::new(
Resource::ProcessCreate,
PermissionSet::from_bits(bits),
types::generation::Generation::initial(),
);
let mut table = CapabilityTable::new();
let slot = insert_cap(&mut table, cap);
table.remove(slot).unwrap();
assert!(table.get(slot).is_err());
}

View File

@@ -0,0 +1,66 @@
use crate::capability::{Capability, CapabilityTable};
use crate::resource::Resource;
use super::{arbitrary_endpoint_cap, insert_cap};
use types::generation::Generation;
use types::id::EndpointId;
use types::permission::{Permission, PermissionSet};
#[kani::proof]
fn delegation_via_cap_table_never_escalates() {
let source_bits: u8 = kani::any();
let restrict_bits: u8 = kani::any();
let ep_id: u16 = kani::any();
kani::assume(ep_id < 256);
let source_perms = PermissionSet::from_bits(source_bits);
kani::assume(source_perms.allows(Permission::Delegate));
let mut source_table = CapabilityTable::new();
let slot = insert_cap(&mut source_table, arbitrary_endpoint_cap(source_bits, ep_id));
let cap = source_table.get(slot).unwrap();
let original_perms = *cap.permissions();
let delegated_perms = original_perms.restrict_to(&PermissionSet::from_bits(restrict_bits));
let delegated_cap = Capability::new(
Resource::Endpoint(EndpointId::from_raw(ep_id)),
delegated_perms,
Generation::initial(),
);
let mut target_table = CapabilityTable::new();
let target_slot = insert_cap(&mut target_table, delegated_cap);
let result = target_table.get(target_slot).unwrap();
assert!(result.permissions().is_subset_of(&original_perms));
}
#[kani::proof]
fn delegation_cannot_add_permissions_not_in_source() {
let source_bits: u8 = kani::any();
let restrict_bits: u8 = kani::any();
let source = PermissionSet::from_bits(source_bits);
let result = source.restrict_to(&PermissionSet::from_bits(restrict_bits));
if !source.allows(Permission::Read) { assert!(!result.allows(Permission::Read)); }
if !source.allows(Permission::Write) { assert!(!result.allows(Permission::Write)); }
if !source.allows(Permission::Execute) { assert!(!result.allows(Permission::Execute)); }
if !source.allows(Permission::Delegate) { assert!(!result.allows(Permission::Delegate)); }
if !source.allows(Permission::Revoke) { assert!(!result.allows(Permission::Revoke)); }
}
#[kani::proof]
fn chained_delegation_never_escalates() {
let original_bits: u8 = kani::any();
let restrict1_bits: u8 = kani::any();
let restrict2_bits: u8 = kani::any();
let original = PermissionSet::from_bits(original_bits);
let after_first = original.restrict_to(&PermissionSet::from_bits(restrict1_bits));
let after_second = after_first.restrict_to(&PermissionSet::from_bits(restrict2_bits));
assert!(after_first.is_subset_of(&original));
assert!(after_second.is_subset_of(&after_first));
assert!(after_second.is_subset_of(&original));
}

View File

@@ -0,0 +1,23 @@
mod capability;
mod delegation;
mod thread;
use crate::capability::{Capability, CapabilityTable};
use crate::resource::Resource;
use types::generation::Generation;
use types::id::EndpointId;
use types::permission::PermissionSet;
fn arbitrary_endpoint_cap(bits: u8, ep_id: u16) -> Capability {
Capability::new(
Resource::Endpoint(EndpointId::from_raw(ep_id)),
PermissionSet::from_bits(bits),
Generation::initial(),
)
}
fn insert_cap(table: &mut CapabilityTable, cap: Capability) -> types::bounded::CapSlot {
let slot = table.insert(cap);
kani::assume(slot.is_ok());
slot.unwrap()
}

View File

@@ -0,0 +1,50 @@
use crate::thread::{BlockReason, Thread, ThreadState};
use types::bounded::Priority;
use types::id::{CoreId, ProcessId, ThreadId};
use types::time::TimeSlice;
fn test_thread(prio: Priority) -> Thread {
Thread::new(
ThreadId::from_raw(0),
ProcessId::from_raw(0),
prio,
TimeSlice::new(100),
None,
)
}
#[kani::proof]
fn dead_thread_rejects_all_transitions() {
let mut thread = test_thread(Priority::new(16).unwrap());
thread.set_running(CoreId::from_raw(0)).unwrap();
thread.exit().unwrap();
assert!(matches!(thread.state(), ThreadState::Dead));
assert!(thread.set_running(CoreId::from_raw(0)).is_err());
assert!(thread.block(BlockReason::WaitingAny).is_err());
assert!(thread.unblock().is_err());
assert!(thread.preempt().is_err());
assert!(thread.exit().is_err());
}
#[kani::proof]
fn only_running_thread_can_block() {
let prio_val: u8 = kani::any();
kani::assume(prio_val <= 31);
let mut thread = test_thread(Priority::new(prio_val).unwrap());
assert!(matches!(thread.state(), ThreadState::Ready));
assert!(thread.block(BlockReason::WaitingAny).is_err());
}
#[kani::proof]
fn blocked_thread_cannot_run_directly() {
let mut thread = test_thread(Priority::new(16).unwrap());
thread.set_running(CoreId::from_raw(0)).unwrap();
thread.block(BlockReason::WaitingAny).unwrap();
assert!(thread.set_running(CoreId::from_raw(0)).is_err());
}