fix: kernel hardening pass 2 — page tracking, queue overflow, region tracking

Page lifecycle:
- ProcessMemory now tracks (PhysicalAddress, count) pairs for each
  allocation (up to 16). destroy_process iterates and frees all
  pages back to the pool. No more permanent page leaks on exit/fault.
- RequestPages re-adds MemoryRegion entries so ReleasePages works.
  Release test un-ignored, passes.
- RequestPages: VA allocation moved after pool allocation succeeds,
  preventing VA leak on OOM.

IPC hardening:
- WaitQueue overflow (>16 waiters): returns WouldBlock error instead
  of silently dropping the thread. Thread is not blocked if enqueue
  fails.
- debug_assert on pending_messages[sender].take() — invariant that
  a sender in wait queue must have a pending message.

Scheduler:
- remove() now clears current thread if the removed thread was
  current on its core.
- add() asserts push succeeded; bitmap bit only set if thread was
  actually added to RunList.

Cleanup:
- Priority::DEFAULT replaces Priority::new(16).unwrap() in
  spawn_thread and spawn_thread_in.

36 kernel tests pass, 0 ignored.
This commit is contained in:
2026-10-18 22:31:16 +02:00
parent ea2ac5d1d9
commit d009818caf
8 changed files with 78 additions and 34 deletions

View File

@@ -1,16 +1,19 @@
use types::address::VirtualAddress;
use types::address::{PhysicalAddress, VirtualAddress};
use types::id::ProcessId;
use crate::error::KernelError;
const USER_HEAP_START: usize = 0x40000;
const PAGE_SIZE: usize = 4096;
const MAX_ALLOCATIONS: usize = 16;
pub struct ProcessMemory {
process: ProcessId,
page_count: usize,
limit: usize,
next_heap_va: usize,
allocations: [(PhysicalAddress, usize); MAX_ALLOCATIONS],
alloc_count: usize,
}
impl ProcessMemory {
@@ -20,6 +23,8 @@ impl ProcessMemory {
page_count: 0,
limit,
next_heap_va: USER_HEAP_START,
allocations: [(PhysicalAddress::from_raw(0), 0); MAX_ALLOCATIONS],
alloc_count: 0,
}
}
@@ -29,6 +34,17 @@ impl ProcessMemory {
VirtualAddress::from_raw(va)
}
pub fn record_allocation(&mut self, pa: PhysicalAddress, count: usize) {
if self.alloc_count < MAX_ALLOCATIONS {
self.allocations[self.alloc_count] = (pa, count);
self.alloc_count += 1;
}
}
pub fn allocations(&self) -> &[(PhysicalAddress, usize)] {
&self.allocations[..self.alloc_count]
}
pub fn process(&self) -> ProcessId {
self.process
}

View File

@@ -68,7 +68,11 @@ impl Scheduler for MultiCoreScheduler {
fn remove(&mut self, thread: ThreadId) {
let idx = thread.raw() as usize;
if let Some(placement) = self.placements[idx].take() {
self.cores[placement.core.raw() as usize].remove(thread, placement.priority);
let core_idx = placement.core.raw() as usize;
self.cores[core_idx].remove(thread, placement.priority);
if self.cores[core_idx].current() == Some(thread) {
self.cores[core_idx].clear_current();
}
}
}

View File

@@ -77,8 +77,11 @@ impl PriorityQueue {
pub fn add(&mut self, thread: ThreadId, priority: Priority) {
let idx = priority.value() as usize;
self.queues[idx].push(thread);
self.bitmap |= 1 << idx;
let added = self.queues[idx].push(thread);
debug_assert!(added, "scheduler run list full for priority {}", idx);
if added {
self.bitmap |= 1 << idx;
}
}
pub fn remove(&mut self, thread: ThreadId, priority: Priority) {