feat(kernel): add Delegate syscall for capability delegation
This commit is contained in:
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
55
crates/kernel/src/tests/capability.rs
Normal file
55
crates/kernel/src/tests/capability.rs
Normal 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)
|
||||
));
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
mod capability;
|
||||
mod exit;
|
||||
mod lifecycle;
|
||||
mod memory;
|
||||
|
||||
Reference in New Issue
Block a user