feat: kernel W^X (per-section perms, firmware unmapped, stack guard, boot check), halt on kernel faults, region W^X across processes

This commit is contained in:
2026-10-27 00:13:00 +01:00
parent bafc18a5f8
commit 55758ae5b2
12 changed files with 285 additions and 17 deletions

View File

@@ -15,6 +15,7 @@ use platform::Platform;
use types::address::DataRef;
use types::bounded::CapSlot;
use types::id::ThreadId;
use types::mapping::PagePerms;
use types::syscall_abi::*;
impl Kernel<MockPlatform> {
@@ -96,6 +97,10 @@ impl Kernel<MockPlatform> {
}
}
for (_, region) in self.regions.iter() {
let has = |want| region.mappings().any(|m| m.perms == want);
if has(PagePerms::ReadWrite) && has(PagePerms::ReadExecute) {
return Err(String::from("region mapped both writable and executable"));
}
for m in region.mappings() {
if let Some(via) = m.via
&& (!self.resolves(via) || via.process != m.process)
@@ -308,6 +313,19 @@ fn new_round(k: &mut Kernel<MockPlatform>, init: ProcessId, init_thread: ThreadI
}
}
/// The slots of every region capability `pid` holds.
fn region_slots(k: &Kernel<MockPlatform>, pid: Option<ProcessId>) -> std::vec::Vec<CapSlot> {
let Some(process) = pid.and_then(|p| k.processes.get(p).ok()) else {
return std::vec::Vec::new();
};
process
.capabilities()
.iter()
.filter(|(_, c)| matches!(c.resource(), domain::resource::Resource::Memory(_)))
.map(|(s, _)| s)
.collect()
}
#[test]
fn the_kernel_survives_a_syscall_storm() {
let mut total = [0usize; 36 + 1];
@@ -357,7 +375,20 @@ fn the_kernel_survives_a_syscall_storm() {
if is_init && (nr == SYS_EXIT || nr == SYS_EXIT_THREAD) {
continue;
}
let args: [usize; 6] = core::array::from_fn(|_| rng.arg());
let mut args: [usize; 6] = core::array::from_fn(|_| rng.arg());
// Random arguments almost never name a region cap, a free
// address and valid perms at once; half the time, pick one of
// the caller's region caps (often the hub's shared region) so
// regions really are mapped into several processes.
if nr == SYS_MAP_REGION && rng.below(2) == 0 {
let regions = region_slots(&k, k.thread_process(tid));
if !regions.is_empty() {
args[0] = regions[rng.below(regions.len())].value() as usize;
args[1] = 0x10000 + rng.below(32) * 4096;
// Read-write or read-execute: the pairs W^X forbids.
args[2] = [PERMS_READ_WRITE, PERMS_READ_EXECUTE][rng.below(2)];
}
}
// init keeps the capability it creates processes with.
if is_init && nr == SYS_DROP_CAP && args[0] == 0 {
continue;