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);
|
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::SpawnThread { entry, stack } => self.handle_spawn_thread(pid, entry, stack),
|
||||||
Syscall::CreateProcess { create_cap, image } => self.handle_create_process(pid, create_cap, image),
|
Syscall::CreateProcess { create_cap, image } => self.handle_create_process(pid, create_cap, image),
|
||||||
Syscall::Kill { process } => self.handle_kill(pid, process),
|
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(
|
_ => SyscallOutcome::Complete(SyscallResult::Error(
|
||||||
domain::error::KernelError::WouldBlock,
|
domain::error::KernelError::WouldBlock,
|
||||||
)),
|
)),
|
||||||
@@ -321,4 +324,45 @@ impl<P: Platform> Kernel<P> {
|
|||||||
self.process_memory[target_pid.raw() as usize] = None;
|
self.process_memory[target_pid.raw() as usize] = None;
|
||||||
SyscallOutcome::Complete(SyscallResult::Ok)
|
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 exit;
|
||||||
mod lifecycle;
|
mod lifecycle;
|
||||||
mod memory;
|
mod memory;
|
||||||
|
|||||||
Reference in New Issue
Block a user