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:
@@ -42,10 +42,8 @@ impl<P: Platform> Kernel<P> {
|
||||
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
|
||||
};
|
||||
|
||||
let inserted = self
|
||||
.processes
|
||||
.get_mut(target_pid)
|
||||
.and_then(|p| p.capabilities_mut().insert(copy));
|
||||
let parent = self.cap_ref(caller_pid, slot);
|
||||
let inserted = self.insert_derived(target_pid, copy, parent);
|
||||
if let Err(e) = inserted {
|
||||
return SyscallOutcome::Complete(SyscallResult::Error(e));
|
||||
}
|
||||
@@ -58,14 +56,10 @@ impl<P: Platform> Kernel<P> {
|
||||
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)
|
||||
SyscallOutcome::Complete(match self.revoke_subtree(caller_pid, slot) {
|
||||
Ok(()) => SyscallResult::Ok,
|
||||
Err(e) => SyscallResult::Error(e),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn handle_drop_cap(
|
||||
@@ -73,11 +67,7 @@ impl<P: Platform> Kernel<P> {
|
||||
caller_pid: types::id::ProcessId,
|
||||
slot: types::bounded::CapSlot,
|
||||
) -> SyscallOutcome {
|
||||
let result = self
|
||||
.processes
|
||||
.get_mut(caller_pid)
|
||||
.and_then(|p| p.capabilities_mut().remove(slot));
|
||||
SyscallOutcome::Complete(match result {
|
||||
SyscallOutcome::Complete(match self.delete_cap(caller_pid, slot) {
|
||||
Ok(()) => SyscallResult::Ok,
|
||||
Err(e) => SyscallResult::Error(e),
|
||||
})
|
||||
|
||||
@@ -150,12 +150,8 @@ impl<P: Platform> Kernel<P> {
|
||||
.check_cap(from, slot, Permission::Delegate)
|
||||
.ok()?
|
||||
.clone();
|
||||
self.processes
|
||||
.get_mut(to)
|
||||
.ok()?
|
||||
.capabilities_mut()
|
||||
.insert(copy)
|
||||
.ok()
|
||||
let parent = self.cap_ref(from, slot);
|
||||
self.insert_derived(to, copy, parent).ok()
|
||||
}
|
||||
|
||||
/// Hands `out` from `sender` to `receiver`. For a call, the receiver's
|
||||
@@ -361,13 +357,13 @@ impl<P: Platform> Kernel<P> {
|
||||
permissions: PermissionSet,
|
||||
) -> Result<CapSlot, KernelError> {
|
||||
let source = self.check_cap(pid, slot, Permission::Delegate)?;
|
||||
if !matches!(
|
||||
let badgeable = matches!(
|
||||
source.resource(),
|
||||
Resource::Endpoint(_) | Resource::Notification(_)
|
||||
) {
|
||||
return Err(KernelError::WrongObject);
|
||||
}
|
||||
if !source.badge().is_none() {
|
||||
);
|
||||
// Any capability can be copied with narrower rights; only those a
|
||||
// receiver can observe the badge of take one, and only once.
|
||||
if (!badgeable && !badge.is_none()) || !source.badge().is_none() {
|
||||
return Err(KernelError::InvalidArgument);
|
||||
}
|
||||
let minted = Capability::new(
|
||||
@@ -376,10 +372,8 @@ impl<P: Platform> Kernel<P> {
|
||||
*source.generation(),
|
||||
)
|
||||
.with_badge(badge);
|
||||
self.processes
|
||||
.get_mut(pid)?
|
||||
.capabilities_mut()
|
||||
.insert(minted)
|
||||
let parent = self.cap_ref(pid, slot);
|
||||
self.insert_derived(pid, minted, parent)
|
||||
}
|
||||
|
||||
pub(crate) fn handle_create_endpoint(&mut self, pid: ProcessId) -> SyscallOutcome {
|
||||
|
||||
@@ -65,9 +65,12 @@ impl<P: Platform> Kernel<P> {
|
||||
match self.grant_cap(
|
||||
caller_pid,
|
||||
domain::resource::Resource::Process(new_pid),
|
||||
// The creator has full authority over what it creates,
|
||||
// including handing that authority on.
|
||||
types::permission::PermissionSet::empty()
|
||||
.with(types::permission::Permission::Execute)
|
||||
.with(types::permission::Permission::Read),
|
||||
.with(types::permission::Permission::Read)
|
||||
.with(types::permission::Permission::Delegate),
|
||||
) {
|
||||
Ok(slot) => SyscallOutcome::Complete(SyscallResult::Slot(slot)),
|
||||
Err(e) => {
|
||||
|
||||
@@ -75,4 +75,44 @@ impl<P: Platform> Kernel<P> {
|
||||
.map(|()| SyscallResult::Ok),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn create_region_cap(
|
||||
&mut self,
|
||||
pid: ProcessId,
|
||||
pages: PageCount,
|
||||
) -> Result<CapSlot, KernelError> {
|
||||
let id = self.create_pool_region(pid, pages)?;
|
||||
let all = types::permission::PermissionSet::empty()
|
||||
.with(Permission::Read)
|
||||
.with(Permission::Write)
|
||||
.with(Permission::Execute)
|
||||
.with(Permission::Delegate);
|
||||
self.grant_cap(pid, Resource::Memory(id), all)
|
||||
}
|
||||
|
||||
/// Maps the region behind `slot` into the caller. The capability must
|
||||
/// allow every access `perms` grants; the mapping lasts as long as
|
||||
/// the capability.
|
||||
pub(crate) fn map_region_cap(
|
||||
&mut self,
|
||||
pid: ProcessId,
|
||||
slot: CapSlot,
|
||||
va: VirtualAddress,
|
||||
perms: PagePerms,
|
||||
) -> Result<(), KernelError> {
|
||||
let cap = self.check_cap(pid, slot, Permission::Read)?;
|
||||
let needed = match perms {
|
||||
PagePerms::ReadOnly => Permission::Read,
|
||||
PagePerms::ReadWrite => Permission::Write,
|
||||
PagePerms::ReadExecute => Permission::Execute,
|
||||
};
|
||||
if !cap.permissions().allows(needed) {
|
||||
return Err(KernelError::PermissionDenied { slot, needed });
|
||||
}
|
||||
let Resource::Memory(id) = *cap.resource() else {
|
||||
return Err(KernelError::WrongObject);
|
||||
};
|
||||
let via = self.cap_ref(pid, slot);
|
||||
self.map_region_via(id, pid, va, perms, via)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +116,13 @@ impl<P: Platform> Kernel<P> {
|
||||
Syscall::Kill { process } => self.handle_kill(pid, process),
|
||||
Syscall::Revoke { slot } => self.handle_revoke(pid, slot),
|
||||
Syscall::DropCap { slot } => self.handle_drop_cap(pid, slot),
|
||||
Syscall::CreateRegion { pages } => {
|
||||
complete(self.create_region_cap(pid, pages).map(SyscallResult::Slot))
|
||||
}
|
||||
Syscall::MapRegion { region, va, perms } => complete(
|
||||
self.map_region_cap(pid, region, va, perms)
|
||||
.map(|()| SyscallResult::Ok),
|
||||
),
|
||||
Syscall::Delegate {
|
||||
slot,
|
||||
to_endpoint,
|
||||
|
||||
Reference in New Issue
Block a user