From da8a0f21b65d9c3c0c0475bc03063fb8129ea1b3 Mon Sep 17 00:00:00 2001 From: Haibib Kerim Date: Mon, 31 Aug 2026 15:11:17 -0700 Subject: [PATCH 1/5] Fix sys_compat_mmap arithmatic bug --- kernel/src/types/function/syscall.rs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/kernel/src/types/function/syscall.rs b/kernel/src/types/function/syscall.rs index baa2370c..c269ff47 100644 --- a/kernel/src/types/function/syscall.rs +++ b/kernel/src/types/function/syscall.rs @@ -460,7 +460,9 @@ pub fn sys_compat_mmap(args: [u64; 6], arca: &mut LoadedArca) -> Result { let mut p = addr; while p < addr + len { - if p.is_multiple_of(Page1GB::SIZE) && len >= Page1GB::SIZE { + // Remaining number of bytes to mmap + let remaining = addr + len - p; + if p.is_multiple_of(Page1GB::SIZE) && remaining >= Page1GB::SIZE { if mode == arcane::__MODE_none { let entry = Entry::Null(Page1GB::SIZE); arca.cpu().map(p, entry).unwrap(); @@ -473,7 +475,7 @@ pub fn sys_compat_mmap(args: [u64; 6], arca: &mut LoadedArca) -> Result { p += Page1GB::SIZE; continue; } - if p.is_multiple_of(Page2MB::SIZE) && len >= Page2MB::SIZE { + if p.is_multiple_of(Page2MB::SIZE) && remaining >= Page2MB::SIZE { if mode == arcane::__MODE_none { let entry = Entry::Null(Page2MB::SIZE); arca.cpu().map(p, entry).unwrap(); @@ -673,3 +675,24 @@ impl From for SyscallError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + // Verifies memory mapping rounds up to the nearest 4KB page without over allocation + #[test] + fn test_mmap_fits_request() { + let len: usize = Page2MB::SIZE + 1; + let mut cpu = CPU.borrow_mut(); + let mut loaded_arca = Arca::new().load(&mut cpu); + let args = [0, len as u64, arcane::__MODE_read_write as u64, 0, 0, 0]; + sys_compat_mmap(args, &mut loaded_arca).expect("mmap failed"); + let mut arca = loaded_arca.unload(); + let mappings = arca.mappings_mut(); + + assert_eq!(mappings.unmap(0).unwrap().len(), Page2MB::SIZE); + assert_eq!(mappings.unmap(Page2MB::SIZE).unwrap().len(), Page4KB::SIZE); + assert!(matches!(mappings.unmap(len), Some(Entry::Null(_)))); + } +} From 10c805eda86d2e1ab8b69ef0daf2e1705aa6d914 Mon Sep 17 00:00:00 2001 From: Haibib Kerim Date: Thu, 3 Sep 2026 15:31:13 -0700 Subject: [PATCH 2/5] Add debug assert check --- kernel/src/types/function/syscall.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/src/types/function/syscall.rs b/kernel/src/types/function/syscall.rs index c269ff47..e5f2ef06 100644 --- a/kernel/src/types/function/syscall.rs +++ b/kernel/src/types/function/syscall.rs @@ -502,6 +502,13 @@ pub fn sys_compat_mmap(args: [u64; 6], arca: &mut LoadedArca) -> Result { } panic!("unaligned mmap or bad size: {p:#x}+{len:#x}"); } + + assert_eq!( + p, + (addr + len).next_multiple_of(Page4KB::SIZE), + "sys_compat_mmap over allocated mapping from {addr:#x} to {:#x}", + addr + len + ); Ok(p - addr) } From 68c56a024a6e965fc41006ae9d2494dda3061ce5 Mon Sep 17 00:00:00 2001 From: Haibib Kerim Date: Fri, 4 Sep 2026 12:40:35 -0700 Subject: [PATCH 3/5] add checks and explicit panics for unexpected kernel behavior --- arca/src/table.rs | 3 ++- fix/shell/src/lib.rs | 18 ++++++++++++++++++ fix/shell/src/rt.rs | 12 ++++++------ fix/shell/src/shell.rs | 7 +++---- kernel/src/types/function/syscall.rs | 2 +- 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/arca/src/table.rs b/arca/src/table.rs index fed4ead3..7b2799c3 100644 --- a/arca/src/table.rs +++ b/arca/src/table.rs @@ -49,7 +49,8 @@ impl Table { let mut smaller = match self.set(index, Entry::Null(0))? { Entry::ROTable(table) => table, Entry::RWTable(table) => table, - _ => R::create_table(self.len() / 512), + Entry::Null(_) => R::create_table(self.len() / 512), + page => panic!("cannot remap {} byte page at {address:#x}", page.len()), }; assert!(self.len() > smaller.len()); smaller.map(offset, entry)?; diff --git a/fix/shell/src/lib.rs b/fix/shell/src/lib.rs index 9d589a33..f36053bd 100644 --- a/fix/shell/src/lib.rs +++ b/fix/shell/src/lib.rs @@ -117,3 +117,21 @@ pub fn main() -> ! { }; os::exit(&result[..]); } + +const KERNEL_PAGE_SIZE: usize = 4096; + +/// Maps `length` bytes at `address` with the given `mode`. +/// Panics if the kernel returns an error code or maps a different length than request +/// +/// # Safety +/// +/// [address] must refer to an unused region of memory at least `length` bytes long; +/// there must be no Rust references pointing into that region. +pub unsafe fn mmap(address: *mut c_void, length: usize, mode: u32) { + let expected = length.next_multiple_of(KERNEL_PAGE_SIZE); + let mapped = unsafe { arcane::arca_compat_mmap(address, length, mode) }; + assert_eq!( + mapped, expected as i64, + "mmap of {length} bytes at {address:?} unexpectedly returned {mapped}" + ); +} diff --git a/fix/shell/src/rt.rs b/fix/shell/src/rt.rs index 9dbcfc05..d7325367 100644 --- a/fix/shell/src/rt.rs +++ b/fix/shell/src/rt.rs @@ -4,7 +4,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; -use arcane::{__MODE_read_write, arca_compat_mmap}; +use arcane::__MODE_read_write; use user::error; include!(concat!(env!("OUT_DIR"), "/wasm_rt.rs")); @@ -50,7 +50,7 @@ pub extern "C" fn wasm_rt_allocate_memory( assert!(max_pages <= (1u64 << 32) / PAGE_SIZE as u64); let data = ((1 << 32) * idx) as *mut u8; let size = initial_pages * PAGE_SIZE as u64; - arca_compat_mmap(data as *mut _, size as usize, __MODE_read_write); + crate::mmap(data as *mut _, size as usize, __MODE_read_write); memory.write(wasm_rt_memory_t { data, pages: initial_pages, @@ -86,7 +86,7 @@ pub extern "C" fn wasm_rt_grow_memory(memory: *mut wasm_rt_memory_t, pages: u64) let start = unsafe { memory.data.byte_add(current as usize * PAGE_SIZE as usize) }; let size = pages * PAGE_SIZE as u64; unsafe { - arca_compat_mmap(start as *mut _, size as usize, __MODE_read_write); + crate::mmap(start as *mut _, size as usize, __MODE_read_write); memory.pages += pages; memory.size += size; } @@ -113,7 +113,7 @@ pub extern "C" fn wasm_rt_allocate_externref_table( max_elements = 1 << (32 - 5); } let data = ((1 << 32) * (64 + idx)) as *mut u8; - arca_compat_mmap(data as *mut _, (elements * 32) as usize, __MODE_read_write); + crate::mmap(data as *mut _, (elements * 32) as usize, __MODE_read_write); table.write(wasm_rt_externref_table_t { data: data as *mut _, size: elements, @@ -137,7 +137,7 @@ pub extern "C" fn wasm_rt_grow_externref_table( let start = unsafe { table.data.byte_add(current as usize * 32) }; let size = delta * 32; unsafe { - arca_compat_mmap(start as *mut _, size as usize, __MODE_read_write); + crate::mmap(start as *mut _, size as usize, __MODE_read_write); table.size += delta; } current @@ -163,7 +163,7 @@ pub extern "C" fn wasm_rt_allocate_funcref_table( max_elements = 1 << (32 - 5); } let data = ((1 << 32) * (64 + 32 + idx)) as *mut u8; - arca_compat_mmap( + crate::mmap( data as *mut _, (elements as usize * core::mem::size_of::()) as usize, __MODE_read_write, diff --git a/fix/shell/src/shell.rs b/fix/shell/src/shell.rs index a22f94c5..2fde0da7 100644 --- a/fix/shell/src/shell.rs +++ b/fix/shell/src/shell.rs @@ -3,8 +3,7 @@ use arca::{Blob, Function, Table, Word}; use arca::{Runtime as _, Tuple}; use arcane::{ __MODE_read_only, __MODE_read_write, __NR_length, __TYPE_table, arca_argument, - arca_blob_create, arca_blob_read, arca_compat_mmap, arca_entry, arca_mmap, arca_table_map, - arcad, + arca_blob_create, arca_blob_read, arca_entry, arca_mmap, arca_table_map, arcad, }; use core::arch::x86_64::*; @@ -52,7 +51,7 @@ pub unsafe fn fixpoint_attach_blob(addr: *mut c_void, handle: [u8; 32]) -> usize let len = fixpoint_len(handle); unsafe { - arca_compat_mmap(addr, len, __MODE_read_write); + crate::mmap(addr, len, __MODE_read_write); blob.read(0, core::slice::from_raw_parts_mut(addr as *mut u8, len)); }; // user::error::log_int("attached memory", len as u64); @@ -86,7 +85,7 @@ pub unsafe fn fixpoint_attach_tree(addr: *mut c_void, handle: [u8; 32]) -> usize // user::error::log_int("attached tree", len as u64); unsafe { - arca_compat_mmap(addr, len * 32, __MODE_read_write); + crate::mmap(addr, len * 32, __MODE_read_write); let slice = core::slice::from_raw_parts_mut(addr as *mut u8, len * 32); tree.read(0, slice) }; diff --git a/kernel/src/types/function/syscall.rs b/kernel/src/types/function/syscall.rs index e5f2ef06..8c751a63 100644 --- a/kernel/src/types/function/syscall.rs +++ b/kernel/src/types/function/syscall.rs @@ -690,7 +690,7 @@ mod tests { // Verifies memory mapping rounds up to the nearest 4KB page without over allocation #[test] fn test_mmap_fits_request() { - let len: usize = Page2MB::SIZE + 1; + let len: usize = Page2MB::SIZE + Page4KB::SIZE; let mut cpu = CPU.borrow_mut(); let mut loaded_arca = Arca::new().load(&mut cpu); let args = [0, len as u64, arcane::__MODE_read_write as u64, 0, 0, 0]; From 125b98f31c0b09f5ada180b049cab0c2c8c58500 Mon Sep 17 00:00:00 2001 From: Keith Winstein Date: Fri, 4 Sep 2026 18:45:25 -0700 Subject: [PATCH 4/5] assert_eq! -> debug_assert_eq! --- kernel/src/types/function/syscall.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/kernel/src/types/function/syscall.rs b/kernel/src/types/function/syscall.rs index 8c751a63..986cbe2a 100644 --- a/kernel/src/types/function/syscall.rs +++ b/kernel/src/types/function/syscall.rs @@ -503,13 +503,9 @@ pub fn sys_compat_mmap(args: [u64; 6], arca: &mut LoadedArca) -> Result { panic!("unaligned mmap or bad size: {p:#x}+{len:#x}"); } - assert_eq!( - p, - (addr + len).next_multiple_of(Page4KB::SIZE), - "sys_compat_mmap over allocated mapping from {addr:#x} to {:#x}", - addr + len - ); - Ok(p - addr) + let size_mapped = p - addr; + debug_assert_eq!(size_mapped, len.next_multiple_of(Page4KB::SIZE)); + Ok(size_mapped) } pub fn sys_call_with_current_continuation( From 9ab8ea5360c122a086ed09aef1be4a5f53a3063e Mon Sep 17 00:00:00 2001 From: Keith Winstein Date: Fri, 4 Sep 2026 23:42:27 -0700 Subject: [PATCH 5/5] Table::map: return error instead of panic on overlapping map request --- arca/src/table.rs | 22 ++++++++++++++++------ kernel/src/cpu.rs | 5 ++++- kernel/src/types/function/syscall.rs | 1 + kernel/src/types/runtime.rs | 1 + 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/arca/src/table.rs b/arca/src/table.rs index 7b2799c3..d0b84e50 100644 --- a/arca/src/table.rs +++ b/arca/src/table.rs @@ -1,5 +1,10 @@ use super::prelude::*; +pub enum MapError { + Runtime(R::Error), + MapExists, +} + impl Table { pub fn new(len: usize) -> Self { R::create_table(len) @@ -25,7 +30,11 @@ impl Table { Ok(()) } - pub fn map(&mut self, address: usize, entry: Entry) -> Result, R::Error> { + // map behaves somewhat like Linux's MAP_FIXED_NOREPLACE (rejects an overlapping map with MapExists). + // Given the heterogeneity of page sizes, handling overlapping map requests involves + // a bunch of cases (e.g. attempt to map a 4 KiB page in the middle of an existing 2 MiB page, etc.) + pub fn map(&mut self, address: usize, entry: Entry) -> Result, MapError> { + use MapError::*; if entry.is_empty() { return Ok(entry); } @@ -34,27 +43,28 @@ impl Table { let mut embiggened = R::create_table(this.len() * 512); embiggened.set(0, Entry::RWTable(this))?; Ok(embiggened) - })?; + }) + .map_err(Runtime)?; self.map(address, entry)? } else if entry.len() == self.len() / 512 { let shift = entry.len().ilog2(); let index = address >> shift; assert!(index < 512); - self.set(index, entry)? + self.set(index, entry).map_err(Runtime)? } else { let shift = (self.len() / 512).ilog2(); let index = (address >> shift) & 0x1ff; let offset = address & !(0x1ff << shift); - let mut smaller = match self.set(index, Entry::Null(0))? { + let mut smaller = match self.set(index, Entry::Null(0)).map_err(Runtime)? { Entry::ROTable(table) => table, Entry::RWTable(table) => table, Entry::Null(_) => R::create_table(self.len() / 512), - page => panic!("cannot remap {} byte page at {address:#x}", page.len()), + Entry::ROPage(_) | Entry::RWPage(_) => return Err(MapExists), }; assert!(self.len() > smaller.len()); smaller.map(offset, entry)?; - self.set(index, Entry::RWTable(smaller))? + self.set(index, Entry::RWTable(smaller)).map_err(Runtime)? }; Ok(result) } diff --git a/kernel/src/cpu.rs b/kernel/src/cpu.rs index ea3919d8..87548482 100644 --- a/kernel/src/cpu.rs +++ b/kernel/src/cpu.rs @@ -309,7 +309,10 @@ impl Cpu { }; let table = Table::from(CowPage::Unique(pdpt)); let mut table = arca::Table::from_inner(table); - let result = table.map(address, entry)?; + let result = table.map(address, entry).map_err(|e| match e { + arca::table::MapError::Runtime(e) => e, + arca::table::MapError::MapExists => crate::types::Error::MapError, + })?; match table.into_inner() { Table::Table512GB(page) => pml4.entry_mut(i_512gb).chain_unique(page.unique()), _ => todo!(), diff --git a/kernel/src/types/function/syscall.rs b/kernel/src/types/function/syscall.rs index 986cbe2a..fb56a84f 100644 --- a/kernel/src/types/function/syscall.rs +++ b/kernel/src/types/function/syscall.rs @@ -675,6 +675,7 @@ impl From for SyscallError { crate::types::Error::InvalidTableEntry(_) => SyscallError::BadArgument, crate::types::Error::InvalidIndex(_) => SyscallError::BadIndex, crate::types::Error::InvalidValue => SyscallError::BadArgument, + crate::types::Error::MapError => SyscallError::BadArgument, } } } diff --git a/kernel/src/types/runtime.rs b/kernel/src/types/runtime.rs index 6d197837..982ad7ce 100644 --- a/kernel/src/types/runtime.rs +++ b/kernel/src/types/runtime.rs @@ -8,6 +8,7 @@ pub enum Error { InvalidTableEntry(super::Entry), InvalidIndex(usize), InvalidValue, + MapError, } impl arca::Runtime for Runtime {