From 01ec650b1eddd3e1a62ad0f69a463004d8d0b50c Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Mon, 10 Nov 2025 08:54:06 -0700 Subject: [PATCH] Add support for userspace stack traces using usercopy --- src/arch/aarch64/interrupt/exception.rs | 13 ++--- src/arch/aarch64/interrupt/handler.rs | 15 +++++- src/arch/riscv64/interrupt/exception.rs | 7 ++- src/arch/riscv64/interrupt/handler.rs | 17 +++++-- src/arch/x86/interrupt/handler.rs | 22 ++++++++- src/arch/x86_64/interrupt/handler.rs | 15 +++++- src/arch/x86_shared/interrupt/exception.rs | 56 ++++++++-------------- src/panic.rs | 56 ++++++++++++++++++++-- 8 files changed, 142 insertions(+), 59 deletions(-) diff --git a/src/arch/aarch64/interrupt/exception.rs b/src/arch/aarch64/interrupt/exception.rs index ef093b13d7..c4f9dec8c5 100644 --- a/src/arch/aarch64/interrupt/exception.rs +++ b/src/arch/aarch64/interrupt/exception.rs @@ -5,7 +5,6 @@ use crate::{ context::signal::excp_handler, exception_stack, memory::{ArchIntCtx, GenericPfFlags}, - panic::stack_trace, sync::CleanLockToken, syscall::{self, flag::*}, }; @@ -14,8 +13,7 @@ use super::InterruptStack; exception_stack!(synchronous_exception_at_el1_with_sp0, |stack| { println!("Synchronous exception at EL1 with SP0"); - stack.dump(); - stack_trace(); + stack.trace(); loop {} }); @@ -162,8 +160,7 @@ exception_stack!(synchronous_exception_at_el1_with_spx, |stack| { let far_el1 = far_el1(); println!("USER FAR_EL1 = 0x{:08x}", far_el1); } - stack.dump(); - stack_trace(); + stack.trace(); loop {} } }); @@ -205,8 +202,7 @@ exception_stack!(synchronous_exception_at_el0, |stack| { ); println!("FAR_EL1: {:#0x}", far_el1()); //crate::debugger::debugger(None); - stack.dump(); - stack_trace(); + stack.trace(); excp_handler(Exception { kind: 0, // TODO }); @@ -217,8 +213,7 @@ exception_stack!(synchronous_exception_at_el0, |stack| { exception_stack!(unhandled_exception, |stack| { println!("Unhandled exception"); - stack.dump(); - stack_trace(); + stack.trace(); loop {} }); diff --git a/src/arch/aarch64/interrupt/handler.rs b/src/arch/aarch64/interrupt/handler.rs index 03c26fdec4..6c7a0359e3 100644 --- a/src/arch/aarch64/interrupt/handler.rs +++ b/src/arch/aarch64/interrupt/handler.rs @@ -1,4 +1,4 @@ -use crate::syscall::IntRegisters; +use crate::{panic, syscall::IntRegisters}; #[derive(Default)] #[repr(C, packed)] @@ -120,6 +120,12 @@ pub struct InterruptStack { impl InterruptStack { pub fn init(&mut self) {} + pub fn frame_pointer(&self) -> usize { + self.preserved.x29 + } + pub fn stack_pointer(&self) -> usize { + self.iret.sp_el0 + } pub fn set_stack_pointer(&mut self, sp: usize) { self.iret.sp_el0 = sp; } @@ -137,6 +143,13 @@ impl InterruptStack { self.scratch.dump(); self.preserved.dump(); } + pub fn trace(&self) { + self.dump(); + unsafe { + panic::user_stack_trace(&self); + panic::stack_trace(); + } + } /// Saves all registers to a struct used by the proc: /// scheme to read/write registers. diff --git a/src/arch/riscv64/interrupt/exception.rs b/src/arch/riscv64/interrupt/exception.rs index e321d03124..ac8d81f935 100644 --- a/src/arch/riscv64/interrupt/exception.rs +++ b/src/arch/riscv64/interrupt/exception.rs @@ -132,15 +132,14 @@ unsafe fn handle_system_exception(scause: usize, regs: &InterruptStack) { "S-mode exception! scause={:#016x}, stval={:#016x}", scause, stval ); - regs.dump(); if tp == 0 { // Early failure - before misc::init and potentially before RMM init // Do not attempt to trace stack because it would probably trap again - loop {} + regs.dump(); + } else { + regs.trace(); } - - stack_trace(); loop {} } } diff --git a/src/arch/riscv64/interrupt/handler.rs b/src/arch/riscv64/interrupt/handler.rs index 066dd2b362..070a4932c7 100644 --- a/src/arch/riscv64/interrupt/handler.rs +++ b/src/arch/riscv64/interrupt/handler.rs @@ -1,4 +1,4 @@ -use crate::{memory::ArchIntCtx, syscall::IntRegisters}; +use crate::{memory::ArchIntCtx, panic, syscall::IntRegisters}; use core::mem::size_of; #[derive(Default)] @@ -99,12 +99,15 @@ impl InterruptStack { assert!(32 * 8 == size_of::()); } } - pub fn set_stack_pointer(&mut self, sp: usize) { - self.registers.x2 = sp; + pub fn frame_pointer(&self) -> usize { + self.registers.x8 } pub fn stack_pointer(&self) -> usize { self.registers.x2 } + pub fn set_stack_pointer(&mut self, sp: usize) { + self.registers.x2 = sp; + } pub fn set_instr_pointer(&mut self, ip: usize) { self.iret.sepc = ip; } @@ -124,6 +127,14 @@ impl InterruptStack { self.registers.dump(); } + pub fn trace(&self) { + self.dump(); + unsafe { + panic::user_stack_trace(&self); + panic::stack_trace(); + } + } + /// Saves all registers to a struct used by the proc: /// scheme to read/write registers. pub fn save(&self, all: &mut IntRegisters) { diff --git a/src/arch/x86/interrupt/handler.rs b/src/arch/x86/interrupt/handler.rs index c3a756be39..cbf5051ade 100644 --- a/src/arch/x86/interrupt/handler.rs +++ b/src/arch/x86/interrupt/handler.rs @@ -1,6 +1,6 @@ use core::mem; -use crate::{memory::ArchIntCtx, syscall::IntRegisters}; +use crate::{memory::ArchIntCtx, panic, syscall::IntRegisters}; use super::super::flags::*; @@ -88,6 +88,13 @@ impl InterruptStack { self.scratch.dump(); self.preserved.dump(); } + pub fn trace(&self) { + self.dump(); + unsafe { + panic::user_stack_trace(&self); + panic::stack_trace(); + } + } /// Saves all registers to a struct used by the proc: /// scheme to read/write registers. pub fn save(&self, all: &mut IntRegisters) { @@ -124,6 +131,12 @@ impl InterruptStack { all.ss = self.iret.ss; } } + pub fn frame_pointer(&self) -> usize { + self.preserved.ebp + } + pub fn stack_pointer(&self) -> usize { + self.iret.esp + } pub fn set_stack_pointer(&mut self, esp: usize) { self.iret.esp = esp; } @@ -188,6 +201,13 @@ impl InterruptErrorStack { println!("CODE: {:08x}", { self.code }); self.inner.dump(); } + pub fn trace(&self) { + self.dump(); + unsafe { + panic::user_stack_trace(&self.inner); + panic::stack_trace(); + } + } } #[macro_export] diff --git a/src/arch/x86_64/interrupt/handler.rs b/src/arch/x86_64/interrupt/handler.rs index 8f99e0bc35..1cd82dac05 100644 --- a/src/arch/x86_64/interrupt/handler.rs +++ b/src/arch/x86_64/interrupt/handler.rs @@ -1,4 +1,4 @@ -use crate::{memory::ArchIntCtx, syscall::IntRegisters}; +use crate::{memory::ArchIntCtx, panic, syscall::IntRegisters}; use super::super::flags::*; @@ -102,6 +102,12 @@ impl InterruptStack { self.iret.cs = (crate::gdt::GDT_USER_CODE << 3) | 3; self.iret.ss = (crate::gdt::GDT_USER_DATA << 3) | 3; } + pub fn frame_pointer(&self) -> usize { + self.preserved.rbp + } + pub fn stack_pointer(&self) -> usize { + self.iret.rsp + } pub fn set_stack_pointer(&mut self, rsp: usize) { self.iret.rsp = rsp; } @@ -120,6 +126,13 @@ impl InterruptStack { self.scratch.dump(); self.preserved.dump(); } + pub fn trace(&self) { + self.dump(); + unsafe { + panic::user_stack_trace(&self); + panic::stack_trace(); + } + } /// Saves all registers to a struct used by the proc: /// scheme to read/write registers. pub fn save(&self, all: &mut IntRegisters) { diff --git a/src/arch/x86_shared/interrupt/exception.rs b/src/arch/x86_shared/interrupt/exception.rs index 7aea910638..ff3de91448 100644 --- a/src/arch/x86_shared/interrupt/exception.rs +++ b/src/arch/x86_shared/interrupt/exception.rs @@ -3,13 +3,12 @@ use x86::irq::PageFaultError; use crate::{ arch::x86_shared::interrupt, context::signal::excp_handler, memory::GenericPfFlags, - paging::VirtualAddress, panic::stack_trace, ptrace, sync::CleanLockToken, syscall::flag::*, + paging::VirtualAddress, ptrace, sync::CleanLockToken, syscall::flag::*, }; interrupt_stack!(divide_by_zero, |stack| { println!("Divide by zero"); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 0, ..Default::default() @@ -92,8 +91,7 @@ interrupt_stack!(breakpoint, |stack| { interrupt_stack!(overflow, |stack| { println!("Overflow trap"); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 4, ..Default::default() @@ -102,8 +100,7 @@ interrupt_stack!(overflow, |stack| { interrupt_stack!(bound_range, |stack| { println!("Bound range exceeded fault"); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 5, ..Default::default() @@ -112,8 +109,7 @@ interrupt_stack!(bound_range, |stack| { interrupt_stack!(invalid_opcode, |stack| { println!("Invalid opcode fault"); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 6, ..Default::default() @@ -122,8 +118,7 @@ interrupt_stack!(invalid_opcode, |stack| { interrupt_stack!(device_not_available, |stack| { println!("Device not available fault"); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 7, ..Default::default() @@ -132,9 +127,8 @@ interrupt_stack!(device_not_available, |stack| { interrupt_error!(double_fault, |stack, _code| { println!("Double fault"); - stack.dump(); + stack.trace(); unsafe { - stack_trace(); loop { interrupt::disable(); interrupt::halt(); @@ -144,8 +138,7 @@ interrupt_error!(double_fault, |stack, _code| { interrupt_error!(invalid_tss, |stack, code| { println!("Invalid TSS fault"); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 10, code, @@ -155,8 +148,7 @@ interrupt_error!(invalid_tss, |stack, code| { interrupt_error!(segment_not_present, |stack, code| { println!("Segment not present fault"); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 11, code, @@ -166,8 +158,7 @@ interrupt_error!(segment_not_present, |stack, code| { interrupt_error!(stack_segment, |stack, code| { println!("Stack segment fault"); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 12, code, @@ -177,8 +168,7 @@ interrupt_error!(stack_segment, |stack, code| { interrupt_error!(protection, |stack, code| { println!("Protection fault code={:#0x}", code); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 13, code, @@ -215,8 +205,7 @@ interrupt_error!(page, |stack, code| { #[cfg(target_arch = "x86")] if crate::memory::page_fault_handler(&mut stack.inner, generic_flags, cr2).is_err() { println!("Page fault: {:>08X} {:#?}", cr2.data(), arch_flags); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 14, code, @@ -227,8 +216,7 @@ interrupt_error!(page, |stack, code| { #[cfg(target_arch = "x86_64")] if crate::memory::page_fault_handler(stack, generic_flags, cr2).is_err() { println!("Page fault: {:>016X} {:#?}", cr2.data(), arch_flags); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 14, code, @@ -239,8 +227,7 @@ interrupt_error!(page, |stack, code| { interrupt_stack!(fpu_fault, |stack| { println!("FPU floating point fault"); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 16, ..Default::default() @@ -249,8 +236,7 @@ interrupt_stack!(fpu_fault, |stack| { interrupt_error!(alignment_check, |stack, code| { println!("Alignment check fault"); - stack.dump(); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 17, code, @@ -260,9 +246,8 @@ interrupt_error!(alignment_check, |stack, code| { interrupt_stack!(machine_check, @paranoid, |stack| { println!("Machine check fault"); - stack.dump(); + stack.trace(); unsafe { - stack_trace(); loop { interrupt::disable(); interrupt::halt(); @@ -272,11 +257,10 @@ interrupt_stack!(machine_check, @paranoid, |stack| { interrupt_stack!(simd, |stack| { println!("SIMD floating point fault"); - stack.dump(); let mut mxcsr = 0_usize; unsafe { core::arch::asm!("stmxcsr [{}]", in(reg) core::ptr::addr_of_mut!(mxcsr)) }; println!("MXCSR {:#0x}", mxcsr); - unsafe { stack_trace() }; + stack.trace(); excp_handler(Exception { kind: 19, ..Default::default() @@ -285,9 +269,8 @@ interrupt_stack!(simd, |stack| { interrupt_stack!(virtualization, |stack| { println!("Virtualization fault"); - stack.dump(); + stack.trace(); unsafe { - stack_trace(); loop { interrupt::disable(); interrupt::halt(); @@ -297,9 +280,8 @@ interrupt_stack!(virtualization, |stack| { interrupt_error!(security, |stack, _code| { println!("Security exception"); - stack.dump(); + stack.trace(); unsafe { - stack_trace(); loop { interrupt::disable(); interrupt::halt(); diff --git a/src/panic.rs b/src/panic.rs index d667b02161..df8512d69d 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -1,6 +1,6 @@ //! Intrinsics for panic handling -use core::{panic::PanicInfo, slice}; +use core::{mem, panic::PanicInfo, slice}; #[cfg(target_pointer_width = "32")] use object::elf::FileHeader32 as FileHeader; @@ -16,10 +16,11 @@ use rustc_demangle::demangle; use crate::{ arch::{consts::USER_END_OFFSET, interrupt::trace::StackTrace}, - context, cpu_id, interrupt, + context, cpu_id, + interrupt::{self, InterruptStack}, memory::KernelMapper, sync::CleanLockToken, - syscall, + syscall::{self, usercopy::UserSliceRo}, }; /// Required to handle panics @@ -147,3 +148,52 @@ pub unsafe fn stack_trace() { } } } + +/// Get a user stack trace +#[inline(never)] +pub unsafe fn user_stack_trace(stack: &InterruptStack) { + let mut fp = stack.frame_pointer(); + let sp = stack.stack_pointer(); + + if fp < sp { + println!(" "); + return; + } + if fp >= crate::USER_END_OFFSET { + return; + } + + for _ in 0..64 { + if fp == 0 || fp >= crate::USER_END_OFFSET { + break; + } + let rip_addr = fp + size_of::(); + let rip = match UserSliceRo::new(rip_addr, mem::size_of::()) + .and_then(|x| x.read_usize()) + { + Ok(val) => val, + Err(err) => { + println!(" 016x}>: {}", fp, err); + break; + } + }; + println!(" FP {:>016x}: PC {:>016x}", fp, rip); + if rip == 0 { + break; + } + + let next_fp = + match UserSliceRo::new(fp, mem::size_of::()).and_then(|x| x.read_usize()) { + Ok(val) => val, + Err(_err) => break, + }; + if next_fp <= fp { + println!( + " 016x}; stack walk ended>", + next_fp + ); + break; + } + fp = next_fp; + } +}