feat(kernel): derivation-tree revocation, region caps

- caps record parent (mint/delegate/ipc), CapRef w/ pid+slot gens
- Revoke = delete subtree, keep self; drop/death re-parents children
- CreateRegion/MapRegion; mapping lives only as long as its cap
- drop Permission::Revoke + object-wide revoke
- mint any cap (badge only ep/ntfn), process caps delegable
- kani: descendants == subtree; model-based fuzz vs spec
This commit is contained in:
2026-10-26 20:41:03 +01:00
parent 76f10ff1f1
commit 154599e3af
32 changed files with 1302 additions and 239 deletions

View File

@@ -1,10 +1,9 @@
use super::*;
use crate::{SyscallOutcome, SyscallResult};
use domain::resource::Resource;
use domain::syscall::Syscall;
use types::address::DataRef;
use types::bounded::CapSlot;
use types::permission::{Permission, PermissionSet};
use types::permission::PermissionSet;
#[test]
fn delegate_without_delegate_permission_fails() {
@@ -57,147 +56,6 @@ fn delegate_with_valid_cap_succeeds() {
// --- Generation-based revocation ---
#[test]
fn revoke_resource_invalidates_all_caps() {
let (mut k, pid) = kernel_with_process();
let ep_id = k.create_endpoint_for_test();
let cap_slot = k.grant_cap_for_test(
pid,
Resource::Endpoint(ep_id),
PermissionSet::empty().with(Permission::Read),
);
// Revoke the endpoint
k.revoke_resource(&Resource::Endpoint(ep_id));
// Cap is now invalid — check_cap should reject with RevokedCapability
let outcome = k.handle_syscall(Syscall::Receive { endpoint: cap_slot });
assert!(matches!(
outcome,
SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::RevokedCapability(_)
))
));
}
#[test]
fn new_cap_after_revoke_uses_current_generation() {
let (mut k, pid) = kernel_with_process();
let ep_id = k.create_endpoint_for_test();
// Grant, revoke, grant again
let old_slot = k.grant_cap_for_test(
pid,
Resource::Endpoint(ep_id),
PermissionSet::empty().with(Permission::Read),
);
k.revoke_resource(&Resource::Endpoint(ep_id));
// Old cap is dead
let outcome = k.handle_syscall(Syscall::Receive { endpoint: old_slot });
assert!(matches!(
outcome,
SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::RevokedCapability(_)
))
));
// New cap with current generation works
let new_slot = k.grant_cap_for_test(
pid,
Resource::Endpoint(ep_id),
PermissionSet::empty().with(Permission::Read),
);
let outcome = k.handle_syscall(Syscall::Receive { endpoint: new_slot });
// Should block (no sender), not error — proving the cap is valid
assert!(matches!(outcome, SyscallOutcome::BlockCaller(_)));
}
#[test]
fn revoke_does_not_affect_other_resources() {
let (mut k, pid) = kernel_with_process();
let ep1 = k.create_endpoint_for_test();
let ep2 = k.create_endpoint_for_test();
let slot1 = k.grant_cap_for_test(
pid,
Resource::Endpoint(ep1),
PermissionSet::empty().with(Permission::Read),
);
let slot2 = k.grant_cap_for_test(
pid,
Resource::Endpoint(ep2),
PermissionSet::empty().with(Permission::Read),
);
// Revoke only ep1
k.revoke_resource(&Resource::Endpoint(ep1));
// ep1 cap is revoked
let outcome = k.handle_syscall(Syscall::Receive { endpoint: slot1 });
assert!(matches!(
outcome,
SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::RevokedCapability(_)
))
));
// ep2 cap still works
let outcome = k.handle_syscall(Syscall::Receive { endpoint: slot2 });
assert!(matches!(outcome, SyscallOutcome::BlockCaller(_)));
}
#[test]
fn revoke_syscall_invalidates_via_generation() {
let (mut k, pid) = kernel_with_process();
let ep_id = k.create_endpoint_for_test();
let cap_slot = k.grant_cap_for_test(
pid,
Resource::Endpoint(ep_id),
PermissionSet::empty()
.with(Permission::Read)
.with(Permission::Revoke),
);
// Revoke via syscall
let outcome = k.handle_syscall(Syscall::Revoke { slot: cap_slot });
assert!(matches!(
outcome,
SyscallOutcome::Complete(SyscallResult::Ok)
));
// The cap itself is now revoked (generation mismatch)
let outcome = k.handle_syscall(Syscall::Receive { endpoint: cap_slot });
assert!(matches!(
outcome,
SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::RevokedCapability(_)
))
));
}
#[test]
fn revoke_syscall_without_revoke_permission_fails() {
let (mut k, pid) = kernel_with_process();
let ep_id = k.create_endpoint_for_test();
let cap_slot = k.grant_cap_for_test(
pid,
Resource::Endpoint(ep_id),
PermissionSet::empty().with(Permission::Read),
);
let outcome = k.handle_syscall(Syscall::Revoke { slot: cap_slot });
assert!(matches!(
outcome,
SyscallOutcome::Complete(SyscallResult::Error(_))
));
}
#[test]
fn every_representable_slot_is_rejected_not_panicking() {
let (mut k, _) = kernel_with_process();