fix: critical privilege escalation + resource leak hardening

CRITICAL — MapPage privilege escalation:
  handle_map_page allowed mapping kernel memory (VA >= 0x80000000)
  into userspace. Any process with a Process capability could read/
  write kernel data structures. Fixed: reject src_va >= USER_VA_LIMIT.

HIGH — resource leaks:
  handle_kill: leaked pages (used cleanup_threads directly instead
    of destroy_process). Now uses destroy_process for full cleanup.
  handle_create_process: orphaned process on cap grant failure.
    Now destroys process on rollback.
  handle_create_endpoint: orphaned endpoint on cap grant failure.
    Now destroys endpoint on rollback.
  Thread ID array: hardcoded [None; 4] replaced with
    [None; MAX_THREADS_PER_PROCESS] slice. cleanup_threads now
    takes &[Option<ThreadId>] slice.

MEDIUM — quality fixes:
  handle_request_pages: replaced unreachable!() with proper error
    return + page pool rollback.
This commit is contained in:
2026-10-18 22:39:16 +02:00
parent d009818caf
commit 06863c84fa
4 changed files with 41 additions and 39 deletions

View File

@@ -1,3 +1,4 @@
use domain::process::MAX_THREADS_PER_PROCESS;
use platform::Platform;
use super::{SyscallOutcome, SyscallResult};
@@ -14,7 +15,7 @@ impl<P: Platform> Kernel<P> {
use domain::thread::SavedContext;
let priority = types::bounded::Priority::DEFAULT;
let time_slice = types::time::TimeSlice::DEFAULT;
let time_slice = types::time::TimeSlice::new(100);
let context = SavedContext::with_entry_and_stack(entry, stack);
let tid = match self.threads.spawn_with_context(pid, priority, time_slice, None, context) {
@@ -38,8 +39,6 @@ impl<P: Platform> Kernel<P> {
create_cap: types::bounded::CapSlot,
_image: types::address::DataRef,
) -> SyscallOutcome {
use domain::scheduler::Scheduler;
if let Err(e) = self.check_cap(caller_pid, create_cap, types::permission::Permission::Execute) {
return SyscallOutcome::Complete(SyscallResult::Error(e));
}
@@ -55,18 +54,21 @@ impl<P: Platform> Kernel<P> {
self.process_memory[new_pid.raw() as usize] =
Some(domain::memory::process_memory::ProcessMemory::new(new_pid, 1024));
let slot = match self.grant_cap(
match self.grant_cap(
caller_pid,
domain::resource::Resource::Process(new_pid),
types::permission::PermissionSet::empty()
.with(types::permission::Permission::Execute)
.with(types::permission::Permission::Read),
) {
Ok(s) => s,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
SyscallOutcome::Complete(SyscallResult::Slot(slot))
Ok(slot) => SyscallOutcome::Complete(SyscallResult::Slot(slot)),
Err(e) => {
// Rollback: destroy orphaned process
let _ = self.processes.destroy(new_pid);
self.process_memory[new_pid.raw() as usize] = None;
SyscallOutcome::Complete(SyscallResult::Error(e))
}
}
}
pub(crate) fn handle_spawn_thread_in(
@@ -97,7 +99,7 @@ impl<P: Platform> Kernel<P> {
};
let priority = types::bounded::Priority::DEFAULT;
let time_slice = types::time::TimeSlice::DEFAULT;
let time_slice = types::time::TimeSlice::new(100);
let context = SavedContext::with_entry_and_stack(entry, stack);
let tid = match self.threads.spawn_with_context(target_pid, priority, time_slice, None, context) {
@@ -133,23 +135,7 @@ impl<P: Platform> Kernel<P> {
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let thread_ids: [Option<types::id::ThreadId>; 4] = {
match self.processes.get(target_pid) {
Ok(p) => {
let mut ids = [None; 4];
for (i, tid) in p.thread_ids().enumerate() {
if i < 4 { ids[i] = Some(tid); }
}
ids
}
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
}
};
self.cleanup_threads(&thread_ids);
let _ = self.processes.destroy(target_pid);
self.process_memory[target_pid.raw() as usize] = None;
self.destroy_process(target_pid);
SyscallOutcome::Complete(SyscallResult::Ok)
}
}