From 704f4e05f02e70ec39be1e9bb19d7eff50210eee Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Sat, 28 Feb 2026 10:32:05 -0800 Subject: [PATCH 1/5] segfault properly --- src/arch/arm64/memory/fault.rs | 10 ++------ src/arch/arm64/proc/signal.rs | 3 +++ src/sched/current.rs | 7 ++++- usertest/src/main.rs | 47 ++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/arch/arm64/memory/fault.rs b/src/arch/arm64/memory/fault.rs index b462502..99628c7 100644 --- a/src/arch/arm64/memory/fault.rs +++ b/src/arch/arm64/memory/fault.rs @@ -10,6 +10,7 @@ use crate::{ memory::uaccess::UAccessResult, }, memory::fault::{FaultResolution, handle_demand_fault, handle_protection_fault}, + process::thread_group::signal::SigId, sched::{current::current_task, spawn_kernel_work}, }; use alloc::boxed::Box; @@ -119,15 +120,8 @@ pub fn handle_kernel_mem_fault(exception: Exception, info: AbortIss, state: &mut pub fn handle_mem_fault(exception: Exception, info: AbortIss) { match run_mem_fault_handler(exception, info) { Ok(FaultResolution::Resolved) => {} - // TODO: Implement proc signals. Ok(FaultResolution::Denied) => { - let task = current_task(); - panic!( - "SIGSEGV on process {} {:?} PC: {:x}", - task.process.tgid, - exception, - task.ctx.user().elr_el1 - ) + current_task().process.deliver_signal(SigId::SIGSEGV); } // If the page fault involves sleepy kernel work, we can // spawn that work on the process, since there is no other diff --git a/src/arch/arm64/proc/signal.rs b/src/arch/arm64/proc/signal.rs index dcc9a33..26f26d7 100644 --- a/src/arch/arm64/proc/signal.rs +++ b/src/arch/arm64/proc/signal.rs @@ -56,6 +56,9 @@ pub async fn do_signal(id: SigId, sa: UserspaceSigAction) -> Result, pub(super) borrowed: Cell, + pub location: Cell>>, } unsafe impl Send for CurrentTaskPtr {} @@ -51,15 +52,19 @@ impl CurrentTaskPtr { Self { ptr: Cell::new(ptr::null_mut()), borrowed: Cell::new(false), + location: Cell::new(None), } } + #[track_caller] pub fn current(&self) -> CurrentTaskGuard<'static> { if self.borrowed.get() { - panic!("Double mutable borrow of current task!"); + let other = self.location.take(); + panic!("Double mutable borrow of current task! Borrowed from: {other:?}"); } self.borrowed.set(true); + self.location.set(Some(*core::panic::Location::caller())); unsafe { let ptr = self.ptr.get(); diff --git a/usertest/src/main.rs b/usertest/src/main.rs index a13581b..4b97f1f 100644 --- a/usertest/src/main.rs +++ b/usertest/src/main.rs @@ -194,6 +194,53 @@ fn test_mincore() { register_test!(test_mincore); +fn segfault_child(inner: impl FnOnce()) { + unsafe { + let pid = libc::fork(); + if pid < 0 { + panic!("fork failed"); + } else if pid == 0 { + // Child process + inner() + } else { + // Parent process + let mut status = 0; + let rusage = std::ptr::null_mut(); + libc::wait4(pid, &mut status, 0, rusage); + + assert!(libc::WIFSIGNALED(status)); + assert_eq!(libc::WTERMSIG(status), libc::SIGSEGV); + } + } +} + +fn test_segfault_read() { + segfault_child(|| { + let addr: *const u8 = std::hint::black_box(std::ptr::null()); + let _ = unsafe { std::ptr::read(addr) }; + }); + segfault_child(|| { + // Ensure reading from kernel stack fails + let addr = 0xffff_ba00_0000_0000 as *const u8; + let _ = unsafe { std::ptr::read(addr) }; + }); +} + +register_test!(test_segfault_read); + +fn test_segfault_write() { + segfault_child(|| { + let addr: *mut u8 = std::hint::black_box(std::ptr::null_mut()); + unsafe { std::ptr::write(addr, 42) }; + }); + segfault_child(|| { + let addr = 0xffff_ba00_0000_0000 as *mut u8; + unsafe { std::ptr::write(addr, 42) }; + }); +} + +register_test!(test_segfault_write); + fn run_test(test_fn: fn()) -> Result<(), i32> { // Fork a new process to run the test unsafe { From 6a7c40c54554b0c3159be16c62daf819dfb0cbf0 Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Sat, 28 Feb 2026 11:53:08 -0800 Subject: [PATCH 2/5] un-register rust's SIGSEV handler in test --- usertest/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/usertest/src/main.rs b/usertest/src/main.rs index 4b97f1f..048ffd8 100644 --- a/usertest/src/main.rs +++ b/usertest/src/main.rs @@ -201,6 +201,8 @@ fn segfault_child(inner: impl FnOnce()) { panic!("fork failed"); } else if pid == 0 { // Child process + // Reset rust's SIGSEGV stack overflow signal handler to default + libc::signal(libc::SIGSEGV, libc::SIG_DFL); inner() } else { // Parent process From c180f1ca243b3081e1fa43263ef5c41c22332cf3 Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Sun, 1 Mar 2026 03:59:38 -0800 Subject: [PATCH 3/5] stack overflow test --- usertest/src/main.rs | 49 --------------------------------- usertest/src/signals.rs | 61 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/usertest/src/main.rs b/usertest/src/main.rs index 048ffd8..a13581b 100644 --- a/usertest/src/main.rs +++ b/usertest/src/main.rs @@ -194,55 +194,6 @@ fn test_mincore() { register_test!(test_mincore); -fn segfault_child(inner: impl FnOnce()) { - unsafe { - let pid = libc::fork(); - if pid < 0 { - panic!("fork failed"); - } else if pid == 0 { - // Child process - // Reset rust's SIGSEGV stack overflow signal handler to default - libc::signal(libc::SIGSEGV, libc::SIG_DFL); - inner() - } else { - // Parent process - let mut status = 0; - let rusage = std::ptr::null_mut(); - libc::wait4(pid, &mut status, 0, rusage); - - assert!(libc::WIFSIGNALED(status)); - assert_eq!(libc::WTERMSIG(status), libc::SIGSEGV); - } - } -} - -fn test_segfault_read() { - segfault_child(|| { - let addr: *const u8 = std::hint::black_box(std::ptr::null()); - let _ = unsafe { std::ptr::read(addr) }; - }); - segfault_child(|| { - // Ensure reading from kernel stack fails - let addr = 0xffff_ba00_0000_0000 as *const u8; - let _ = unsafe { std::ptr::read(addr) }; - }); -} - -register_test!(test_segfault_read); - -fn test_segfault_write() { - segfault_child(|| { - let addr: *mut u8 = std::hint::black_box(std::ptr::null_mut()); - unsafe { std::ptr::write(addr, 42) }; - }); - segfault_child(|| { - let addr = 0xffff_ba00_0000_0000 as *mut u8; - unsafe { std::ptr::write(addr, 42) }; - }); -} - -register_test!(test_segfault_write); - fn run_test(test_fn: fn()) -> Result<(), i32> { // Fork a new process to run the test unsafe { diff --git a/usertest/src/signals.rs b/usertest/src/signals.rs index db167b1..b18a1f2 100644 --- a/usertest/src/signals.rs +++ b/usertest/src/signals.rs @@ -157,3 +157,64 @@ fn test_interruptible_waitpid() { } register_test!(test_interruptible_waitpid); + +fn segfault_child(inner: impl FnOnce()) { + unsafe { + let pid = libc::fork(); + if pid < 0 { + panic!("fork failed"); + } else if pid == 0 { + // Child process + // Reset rust's SIGSEGV stack overflow signal handler to default + libc::signal(libc::SIGSEGV, libc::SIG_DFL); + inner() + } else { + // Parent process + let mut status = 0; + let rusage = std::ptr::null_mut(); + libc::wait4(pid, &mut status, 0, rusage); + + assert!(libc::WIFSIGNALED(status)); + assert_eq!(libc::WTERMSIG(status), libc::SIGSEGV); + } + } +} + +fn test_segfault_read() { + segfault_child(|| { + let addr: *const u8 = std::hint::black_box(std::ptr::null()); + let _ = unsafe { std::ptr::read(addr) }; + }); + segfault_child(|| { + // Ensure reading from kernel stack fails + let addr = 0xffff_ba00_0000_0000 as *const u8; + let _ = unsafe { std::ptr::read(addr) }; + }); +} + +register_test!(test_segfault_read); + +fn test_segfault_write() { + segfault_child(|| { + let addr: *mut u8 = std::hint::black_box(std::ptr::null_mut()); + unsafe { std::ptr::write(addr, 42) }; + }); + segfault_child(|| { + let addr = 0xffff_ba00_0000_0000 as *mut u8; + unsafe { std::ptr::write(addr, 42) }; + }); +} + +register_test!(test_segfault_write); + +fn rust_stack_overflow() { + segfault_child(|| { + fn recurse(n: usize) -> usize { + let m = std::hint::black_box(n) + 1; + recurse(m) + } + recurse(0); + }); +} + +register_test!(rust_stack_overflow); From 5cdeb82dfa6abcd2150b3bbc63c06ee86e4dbc7a Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Sun, 1 Mar 2026 04:23:47 -0800 Subject: [PATCH 4/5] clippy --- src/sched/current.rs | 4 +++- usertest/src/signals.rs | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sched/current.rs b/src/sched/current.rs index 9a4a10b..d919641 100644 --- a/src/sched/current.rs +++ b/src/sched/current.rs @@ -43,7 +43,9 @@ impl DerefMut for CurrentTaskGuard<'_> { impl<'a> Drop for CurrentTaskGuard<'a> { fn drop(&mut self) { - CUR_TASK_PTR.borrow().borrowed.set(false); + let current = CUR_TASK_PTR.borrow(); + current.borrowed.set(false); + current.location.set(None); } } diff --git a/usertest/src/signals.rs b/usertest/src/signals.rs index b18a1f2..09dc8b6 100644 --- a/usertest/src/signals.rs +++ b/usertest/src/signals.rs @@ -165,8 +165,6 @@ fn segfault_child(inner: impl FnOnce()) { panic!("fork failed"); } else if pid == 0 { // Child process - // Reset rust's SIGSEGV stack overflow signal handler to default - libc::signal(libc::SIGSEGV, libc::SIG_DFL); inner() } else { // Parent process @@ -209,6 +207,7 @@ register_test!(test_segfault_write); fn rust_stack_overflow() { segfault_child(|| { + #[allow(unconditional_recursion)] fn recurse(n: usize) -> usize { let m = std::hint::black_box(n) + 1; recurse(m) From 15605b11ef76677444db6e2da2524396ad9cb703 Mon Sep 17 00:00:00 2001 From: Troy Neubauer Date: Sun, 1 Mar 2026 04:26:49 -0800 Subject: [PATCH 5/5] no pub on location --- src/sched/current.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sched/current.rs b/src/sched/current.rs index d919641..3682938 100644 --- a/src/sched/current.rs +++ b/src/sched/current.rs @@ -17,7 +17,7 @@ per_cpu_private! { pub(super) struct CurrentTaskPtr { pub(super) ptr: Cell<*mut OwnedTask>, pub(super) borrowed: Cell, - pub location: Cell>>, + location: Cell>>, } unsafe impl Send for CurrentTaskPtr {}