diff --git a/crates/domain/src/syscall.rs b/crates/domain/src/syscall.rs index 2e6469d..1a541e0 100644 --- a/crates/domain/src/syscall.rs +++ b/crates/domain/src/syscall.rs @@ -67,6 +67,9 @@ pub enum Syscall { Kill { process: CapSlot, }, + Revoke { + slot: CapSlot, + }, SpawnThread { entry: EntryPoint, stack: StackPointer, diff --git a/crates/kernel/src/syscall/capability.rs b/crates/kernel/src/syscall/capability.rs index 8b1672b..3a7e9cd 100644 --- a/crates/kernel/src/syscall/capability.rs +++ b/crates/kernel/src/syscall/capability.rs @@ -44,4 +44,22 @@ impl Kernel

{ Err(e) => SyscallOutcome::Complete(SyscallResult::Error(e)), } } + + pub(crate) fn handle_revoke( + &mut self, + caller_pid: types::id::ProcessId, + slot: types::bounded::CapSlot, + ) -> SyscallOutcome { + let resource = match self.check_cap( + caller_pid, + slot, + types::permission::Permission::Revoke, + ) { + Ok(cap) => cap.resource().clone(), + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + self.revoke_resource(&resource); + SyscallOutcome::Complete(SyscallResult::Ok) + } } diff --git a/crates/kernel/src/syscall/mod.rs b/crates/kernel/src/syscall/mod.rs index 004e111..c6d78ce 100644 --- a/crates/kernel/src/syscall/mod.rs +++ b/crates/kernel/src/syscall/mod.rs @@ -44,6 +44,7 @@ impl Kernel

{ Syscall::SpawnThread { entry, stack } => self.handle_spawn_thread(pid, entry, stack), Syscall::CreateProcess { create_cap, image } => self.handle_create_process(pid, create_cap, image), Syscall::Kill { process } => self.handle_kill(pid, process), + Syscall::Revoke { slot } => self.handle_revoke(pid, slot), Syscall::Delegate { slot, to_endpoint, permissions } => { self.handle_delegate(pid, slot, to_endpoint, permissions) } diff --git a/crates/kernel/src/tests/capability.rs b/crates/kernel/src/tests/capability.rs index 474d091..e62dc1b 100644 --- a/crates/kernel/src/tests/capability.rs +++ b/crates/kernel/src/tests/capability.rs @@ -143,3 +143,46 @@ fn revoke_does_not_affect_other_resources() { 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(_)) + )); +}