feat(kernel): object lifetimes via slot generations
- SlotGenerations per table, never wraps, retires exhausted slots - caps to dead/reused process, endpoint, region rejected - endpoints owned, die w/ owner, waiters woken w/ error - DropCap syscall + libkos wrapper - irq table bounds-checked, routes dropped w/ thread - self-kill terminates caller, exit_thread full cleanup - sched run_now: switch to named thread, not queue head - lifetime tests, fuzz kills/recreates, kani gen proofs
This commit is contained in:
@@ -205,8 +205,13 @@ impl<P: Platform> Kernel<P> {
|
||||
Some(tid)
|
||||
}
|
||||
|
||||
pub fn create_endpoint(&mut self) -> Result<types::id::EndpointId, KernelError> {
|
||||
self.endpoints.create().ok_or(KernelError::NoFreeSlot)
|
||||
/// Creates an endpoint owned by `owner`; it is destroyed with `owner`.
|
||||
pub fn create_endpoint(
|
||||
&mut self,
|
||||
owner: ProcessId,
|
||||
) -> Result<types::id::EndpointId, KernelError> {
|
||||
self.processes.get(owner)?;
|
||||
self.endpoints.create(owner).ok_or(KernelError::NoFreeSlot)
|
||||
}
|
||||
|
||||
pub fn grant_capability(
|
||||
@@ -237,8 +242,8 @@ impl<P: Platform> Kernel<P> {
|
||||
|
||||
if let Ok(t) = self.threads.get(tid)
|
||||
&& matches!(t.state(), ThreadState::Ready)
|
||||
&& self.scheduler.run_now(core, tid)
|
||||
{
|
||||
self.scheduler.next(core);
|
||||
let _ = self.threads.get_mut(tid).map(|t| t.set_running(core));
|
||||
}
|
||||
}
|
||||
@@ -274,16 +279,9 @@ impl<P: Platform> Kernel<P> {
|
||||
};
|
||||
|
||||
self.cleanup_threads(&thread_ids);
|
||||
|
||||
self.destroy_endpoints_owned_by(pid);
|
||||
self.teardown_memory(pid);
|
||||
|
||||
// Unblock threads in other processes that were blocked waiting
|
||||
// on endpoints where this process's threads were the other side.
|
||||
// The cleanup_threads call above already removed this process's
|
||||
// threads from all endpoint wait queues. Threads blocked waiting
|
||||
// for this process will eventually time out or be rescheduled
|
||||
// when the endpoint is revoked.
|
||||
|
||||
let _ = self.processes.destroy(pid);
|
||||
}
|
||||
|
||||
@@ -292,6 +290,7 @@ impl<P: Platform> Kernel<P> {
|
||||
for tid in thread_ids.iter().flatten() {
|
||||
self.endpoints.remove_thread_from_all(*tid);
|
||||
self.cleanup_wait_any(*tid);
|
||||
self.irqs.unregister_thread(*tid);
|
||||
self.scheduler.remove(*tid);
|
||||
self.pending_messages[tid.raw() as usize] = None;
|
||||
self.pending_results[tid.raw() as usize] = None;
|
||||
@@ -299,6 +298,41 @@ impl<P: Platform> Kernel<P> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroys every endpoint `owner` created, waking anyone blocked on
|
||||
/// them with an error instead of leaving them blocked forever.
|
||||
fn destroy_endpoints_owned_by(&mut self, owner: ProcessId) {
|
||||
let mut owned = [None; domain::endpoint::MAX_ENDPOINTS];
|
||||
for (slot, id) in owned.iter_mut().zip(self.endpoints.ids_owned_by(owner)) {
|
||||
*slot = Some(id);
|
||||
}
|
||||
for id in owned.into_iter().flatten() {
|
||||
let Some(mut ep) = self.endpoints.destroy(id) else {
|
||||
continue;
|
||||
};
|
||||
while let Some(tid) = ep.dequeue_sender().or_else(|| ep.dequeue_receiver()) {
|
||||
self.wake_with_error(tid, KernelError::InvalidEndpoint(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a blocked thread runnable with `error` as its syscall result.
|
||||
fn wake_with_error(&mut self, tid: ThreadId, error: KernelError) {
|
||||
self.cleanup_wait_any(tid);
|
||||
self.pending_messages[tid.raw() as usize] = None;
|
||||
let Ok(thread) = self.threads.get_mut(tid) else {
|
||||
return;
|
||||
};
|
||||
if !matches!(thread.state(), ThreadState::Blocked(_)) {
|
||||
return;
|
||||
}
|
||||
let priority = thread.priority();
|
||||
if thread.unblock().is_ok() {
|
||||
self.pending_results[tid.raw() as usize] = Some(SyscallResult::Error(error));
|
||||
let core = self.platform.core_id();
|
||||
self.scheduler.add(tid, priority, core);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cleanup_wait_any(&mut self, tid: ThreadId) {
|
||||
if self.wait_sets[tid.raw() as usize].take().is_some() {
|
||||
self.endpoints.remove_thread_from_all(tid);
|
||||
@@ -316,41 +350,27 @@ impl<P: Platform> Kernel<P> {
|
||||
if !cap.permissions().allows(needed) {
|
||||
return Err(KernelError::PermissionDenied { slot, needed });
|
||||
}
|
||||
let current_gen = self.resource_generation(cap.resource());
|
||||
if !cap.generation().is_valid_against(¤t_gen) {
|
||||
return Err(KernelError::RevokedCapability(slot));
|
||||
match self.resource_generation(cap.resource()) {
|
||||
Some(current) if cap.generation().is_valid_against(¤t) => Ok(cap),
|
||||
_ => Err(KernelError::RevokedCapability(slot)),
|
||||
}
|
||||
Ok(cap)
|
||||
}
|
||||
|
||||
fn resource_generation(&self, resource: &Resource) -> Generation {
|
||||
/// The generation a capability to `resource` must carry, or `None` if
|
||||
/// the object no longer exists.
|
||||
fn resource_generation(&self, resource: &Resource) -> Option<Generation> {
|
||||
match resource {
|
||||
Resource::Endpoint(id) => self
|
||||
.endpoints
|
||||
.get(*id)
|
||||
.map(|ep| *ep.generation())
|
||||
.unwrap_or_else(Generation::initial),
|
||||
Resource::Memory(id) => self
|
||||
.regions
|
||||
.get(*id)
|
||||
.map(|r| *r.generation())
|
||||
.unwrap_or_else(Generation::initial),
|
||||
_ => Generation::initial(),
|
||||
Resource::Endpoint(id) => self.endpoints.generation(*id),
|
||||
Resource::Memory(id) => self.regions.generation(*id),
|
||||
Resource::Process(pid) => self.processes.generation(*pid),
|
||||
Resource::Interrupt(_) | Resource::ProcessCreate => Some(Generation::initial()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn revoke_resource(&mut self, resource: &Resource) {
|
||||
match resource {
|
||||
Resource::Endpoint(id) => {
|
||||
if let Some(ep) = self.endpoints.get_mut(*id) {
|
||||
ep.revoke();
|
||||
}
|
||||
}
|
||||
Resource::Memory(id) => {
|
||||
if let Some(r) = self.regions.get_mut(*id) {
|
||||
r.revoke();
|
||||
}
|
||||
}
|
||||
Resource::Endpoint(id) => self.endpoints.revoke(*id),
|
||||
Resource::Memory(id) => self.regions.revoke(*id),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -361,7 +381,9 @@ impl<P: Platform> Kernel<P> {
|
||||
resource: Resource,
|
||||
permissions: PermissionSet,
|
||||
) -> Result<CapSlot, KernelError> {
|
||||
let generation = self.resource_generation(&resource);
|
||||
let generation = self
|
||||
.resource_generation(&resource)
|
||||
.ok_or(KernelError::NoSuchObject)?;
|
||||
let cap = Capability::new(resource, permissions, generation);
|
||||
self.processes.get_mut(to)?.capabilities_mut().insert(cap)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user