feat(kernel): add Delegate syscall for capability delegation

This commit is contained in:
2026-10-18 00:22:37 +02:00
parent 07534e52d9
commit ae6023081a
4 changed files with 115 additions and 0 deletions

View File

@@ -193,4 +193,19 @@ impl<P: Platform> Kernel<P> {
}
self.scheduler.remove(tid);
}
#[cfg(test)]
pub fn create_endpoint_for_test(&mut self) -> types::id::EndpointId {
self.endpoints.create().expect("create endpoint")
}
#[cfg(test)]
pub fn grant_cap_for_test(
&mut self,
pid: ProcessId,
resource: Resource,
permissions: PermissionSet,
) -> CapSlot {
self.grant_cap(pid, resource, permissions).expect("grant cap")
}
}

View File

@@ -21,6 +21,9 @@ impl<P: Platform> Kernel<P> {
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::Delegate { slot, to_endpoint, permissions } => {
self.handle_delegate(pid, slot, to_endpoint, permissions)
}
_ => SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::WouldBlock,
)),
@@ -321,4 +324,45 @@ impl<P: Platform> Kernel<P> {
self.process_memory[target_pid.raw() as usize] = None;
SyscallOutcome::Complete(SyscallResult::Ok)
}
fn handle_delegate(
&mut self,
caller_pid: types::id::ProcessId,
slot: types::bounded::CapSlot,
to_endpoint: types::bounded::CapSlot,
restrict: types::permission::PermissionSet,
) -> SyscallOutcome {
let (resource, permissions) = match self.check_cap(
caller_pid,
slot,
types::permission::Permission::Delegate,
) {
Ok(cap) => (cap.resource().clone(), *cap.permissions()),
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let restricted = permissions.restrict_to(&restrict);
let target_pid = match self.check_cap(
caller_pid,
to_endpoint,
types::permission::Permission::Execute,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Process(pid) => *pid,
_ => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::PermissionDenied {
slot: to_endpoint,
needed: types::permission::Permission::Execute,
},
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
match self.grant_cap(target_pid, resource, restricted) {
Ok(_) => SyscallOutcome::Complete(SyscallResult::Ok),
Err(e) => SyscallOutcome::Complete(SyscallResult::Error(e)),
}
}
}

View File

@@ -0,0 +1,55 @@
use super::*;
use crate::{SyscallOutcome, SyscallResult};
use domain::syscall::Syscall;
use types::bounded::CapSlot;
use types::permission::PermissionSet;
use types::address::DataRef;
#[test]
fn delegate_without_delegate_permission_fails() {
let (mut k, _) = kernel_with_process();
// slot 0 has ProcessCreate cap — no Delegate permission
let outcome = k.handle_syscall(Syscall::Delegate {
slot: CapSlot::new(0).unwrap(),
to_endpoint: CapSlot::new(1).unwrap(),
permissions: PermissionSet::empty(),
});
assert!(matches!(
outcome,
SyscallOutcome::Complete(SyscallResult::Error(_))
));
}
#[test]
fn delegate_with_valid_cap_succeeds() {
let (mut k, pid) = kernel_with_process();
// Create a second process to delegate to
let target_slot = match k.handle_syscall(Syscall::CreateProcess {
create_cap: CapSlot::new(0).unwrap(),
image: DataRef::empty(),
}) {
SyscallOutcome::Complete(SyscallResult::Slot(s)) => s,
_ => panic!("expected slot"),
};
// Grant a delegatable cap to the caller (endpoint cap with Delegate perm)
let ep_id = k.create_endpoint_for_test();
let source_slot = k.grant_cap_for_test(
pid,
domain::resource::Resource::Endpoint(ep_id),
PermissionSet::empty()
.with(types::permission::Permission::Read)
.with(types::permission::Permission::Delegate),
);
let outcome = k.handle_syscall(Syscall::Delegate {
slot: source_slot,
to_endpoint: target_slot, // using process cap as target identifier
permissions: PermissionSet::empty().with(types::permission::Permission::Read),
});
assert!(matches!(
outcome,
SyscallOutcome::Complete(SyscallResult::Ok)
));
}

View File

@@ -1,3 +1,4 @@
mod capability;
mod exit;
mod lifecycle;
mod memory;