diff --git a/src/arch/x86_64/interrupt/syscall.rs b/src/arch/x86_64/interrupt/syscall.rs index 9ddd3c79aef35b745b7113c531d4208467c676b8..9f5ed74b253032f23342867e5e5bb6126220c6f1 100644 --- a/src/arch/x86_64/interrupt/syscall.rs +++ b/src/arch/x86_64/interrupt/syscall.rs @@ -67,12 +67,7 @@ pub unsafe extern fn syscall_instruction() { : "intel", "volatile"); // Push scratch registers - scratch_push!(); - preserved_push!(); - asm!("push fs - mov r11, 0x18 - mov fs, r11" - : : : : "intel", "volatile"); + interrupt_push!(); // Get reference to stack variables let rsp: usize; @@ -87,10 +82,8 @@ pub unsafe extern fn syscall_instruction() { pti::unmap(); // Interrupt return - asm!("pop fs" : : : : "intel", "volatile"); - preserved_pop!(); - scratch_pop!(); - asm!("iretq" : : : : "intel", "volatile"); + interrupt_pop!(); + iret!() } #[naked] @@ -106,12 +99,7 @@ pub unsafe extern fn syscall() { } // Push scratch registers - scratch_push!(); - preserved_push!(); - asm!("push fs - mov r11, 0x18 - mov fs, r11" - : : : : "intel", "volatile"); + interrupt_push!(); // Get reference to stack variables let rsp: usize; @@ -126,10 +114,8 @@ pub unsafe extern fn syscall() { pti::unmap(); // Interrupt return - asm!("pop fs" : : : : "intel", "volatile"); - preserved_pop!(); - scratch_pop!(); - asm!("iretq" : : : : "intel", "volatile"); + interrupt_pop!(); + iret!(); } #[naked] diff --git a/src/arch/x86_64/macros.rs b/src/arch/x86_64/macros.rs index 84021f6b6c755f403f476b23d2f91d8a13166584..22fe5a3cf649ade69d1cb04180acdb1d5738cd5d 100644 --- a/src/arch/x86_64/macros.rs +++ b/src/arch/x86_64/macros.rs @@ -1,5 +1,6 @@ use core::mem; use syscall::data::IntRegisters; +use super::gdt; /// Print to console #[macro_export] @@ -19,6 +20,7 @@ macro_rules! println { } #[allow(dead_code)] +#[derive(Default)] #[repr(packed)] pub struct ScratchRegisters { pub r11: usize, @@ -77,6 +79,7 @@ macro_rules! scratch_pop { } #[allow(dead_code)] +#[derive(Default)] #[repr(packed)] pub struct PreservedRegisters { pub r15: usize, @@ -124,9 +127,13 @@ macro_rules! preserved_pop { macro_rules! fs_push { () => (asm!( - "push fs + " + push fs + + // Load kernel tls mov rax, 0x18 - mov fs, ax" + mov fs, ax // can't load value directly into `fs` + " : : : : "intel", "volatile" )); } @@ -139,6 +146,7 @@ macro_rules! fs_pop { } #[allow(dead_code)] +#[derive(Default)] #[repr(packed)] pub struct IretRegisters { pub rip: usize, @@ -198,6 +206,7 @@ macro_rules! interrupt { } #[allow(dead_code)] +#[derive(Default)] #[repr(packed)] pub struct InterruptStack { pub fs: usize, @@ -207,6 +216,24 @@ pub struct InterruptStack { } impl InterruptStack { + pub fn new_usermode(ip: usize, sp: usize, arg: usize) -> Self { + // See which registers are set in start.rs, function `usermode` + Self { + fs: gdt::GDT_USER_TLS << 3 | 3, + preserved: PreservedRegisters::default(), + scratch: ScratchRegisters { + rdi: arg, + ..ScratchRegisters::default() + }, + iret: IretRegisters { + rip: ip, + cs: gdt::GDT_USER_CODE << 3 | 3, + rflags: 1 << 9, + rsp: sp, + ss: gdt::GDT_USER_DATA << 3 | 3, + }, + } + } pub fn dump(&self) { self.iret.dump(); self.scratch.dump(); @@ -298,9 +325,24 @@ impl InterruptStack { } } +macro_rules! interrupt_push { + () => { + scratch_push!(); + preserved_push!(); + fs_push!(); + }; +} +macro_rules! interrupt_pop { + () => { + fs_pop!(); + preserved_pop!(); + scratch_pop!(); + }; +} + #[macro_export] macro_rules! interrupt_stack { - ($name:ident, $stack: ident, $func:block) => { + ($name:ident, $stack:ident, $func:block) => { #[naked] pub unsafe extern fn $name () { #[inline(never)] @@ -309,9 +351,7 @@ macro_rules! interrupt_stack { } // Push scratch registers - scratch_push!(); - preserved_push!(); - fs_push!(); + interrupt_push!(); // Get reference to stack variables let rsp: usize; @@ -327,15 +367,14 @@ macro_rules! interrupt_stack { $crate::arch::x86_64::pti::unmap(); // Pop scratch registers and return - fs_pop!(); - preserved_pop!(); - scratch_pop!(); + interrupt_pop!(); iret!(); } }; } #[allow(dead_code)] +#[derive(Default)] #[repr(packed)] pub struct InterruptErrorStack { pub fs: usize, @@ -366,9 +405,7 @@ macro_rules! interrupt_error { } // Push scratch registers - scratch_push!(); - preserved_push!(); - fs_push!(); + interrupt_push!(); // Get reference to stack variables let rsp: usize; @@ -384,10 +421,8 @@ macro_rules! interrupt_error { $crate::arch::x86_64::pti::unmap(); // Pop scratch registers, error code, and return - fs_pop!(); - preserved_pop!(); - scratch_pop!(); - asm!("add rsp, 8" : : : : "intel", "volatile"); + interrupt_pop!(); + asm!("add rsp, 8" : : : : "intel", "volatile"); // pop error code iret!(); } }; diff --git a/src/arch/x86_64/start.rs b/src/arch/x86_64/start.rs index ab7fc39629f4a7b2f69a4a2d92adcb227a4104b7..b2bc4e5fc1efc093626ed521fe285b5239b7117a 100644 --- a/src/arch/x86_64/start.rs +++ b/src/arch/x86_64/start.rs @@ -3,7 +3,7 @@ /// It must create the IDT with the correct entries, those entries are /// defined in other files inside of the `arch` module -use core::slice; +use core::{slice, mem, ptr}; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use crate::allocator; @@ -17,6 +17,7 @@ use crate::gdt; use crate::idt; use crate::interrupt; use crate::log; +use crate::macros::InterruptStack; use crate::memory; use crate::paging; @@ -283,3 +284,31 @@ pub unsafe fn usermode(ip: usize, sp: usize, arg: usize) -> ! { : "intel", "volatile"); unreachable!(); } + +#[naked] +pub unsafe fn usermode_interrupt_stack(stack: InterruptStack) -> ! { + // Push fake stack to the actual stack + let rsp: usize; + asm!("sub rsp, $1" : "={rsp}"(rsp) : "r"(mem::size_of::<InterruptStack>()) : : "intel", "volatile"); + ptr::write(rsp as *mut InterruptStack, stack); + + // Unmap kernel + pti::unmap(); + + // Set up floating point and TLS + asm!("mov ds, r14d + mov es, r14d + mov fs, r15d + mov gs, r14d + fninit" + : // No output because it never returns + : "{r14}"(gdt::GDT_USER_DATA << 3 | 3), // Data segment + "{r15}"(gdt::GDT_USER_TLS << 3 | 3) // TLS segment + : "ds", "es", "fs", "gs" + : "intel", "volatile"); + + // Go to usermode + interrupt_pop!(); + iret!(); + unreachable!(); +} diff --git a/src/syscall/process.rs b/src/syscall/process.rs index 18de7f27ada9cb30b08e8151e55ce65f0fc38022..e27576fd3e248ec542b0b40df67034f97ec23686 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -6,6 +6,7 @@ use core::{intrinsics, mem}; use core::ops::DerefMut; use spin::Mutex; +use crate::arch::macros::InterruptStack; use crate::context::file::FileDescriptor; use crate::context::{ContextId, WaitpidKey}; use crate::context; @@ -20,14 +21,14 @@ use crate::paging::temporary_page::TemporaryPage; use crate::paging::{ActivePageTable, InactivePageTable, Page, VirtualAddress, PAGE_SIZE}; use crate::{ptrace, syscall}; use crate::scheme::FileHandle; -use crate::start::usermode; +use crate::start::{usermode, usermode_interrupt_stack}; use crate::syscall::data::{SigAction, Stat}; use crate::syscall::error::*; -use crate::syscall::flag::{CloneFlags, CLONE_VFORK, CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND, - CLONE_STACK, MapFlags, PROT_EXEC, PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE, - PTRACE_STOP_EXIT, SigActionFlags, SIG_DFL, SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK, - SIGCONT, SIGTERM, WaitFlags, WCONTINUED, WNOHANG, WUNTRACED, wifcontinued, - wifstopped}; +use crate::syscall::flag::{CloneFlags, CLONE_FILES, CLONE_FS, CLONE_SIGHAND, + CLONE_STACK, CLONE_VFORK, CLONE_VM, MapFlags, PtraceFlags, PROT_EXEC, + PROT_READ, PROT_WRITE, PTRACE_EVENT_CLONE, PTRACE_STOP_EXIT, SigActionFlags, + SIG_BLOCK, SIG_DFL, SIG_SETMASK, SIG_UNBLOCK, SIGCONT, SIGTERM, WaitFlags, + WCONTINUED, WNOHANG,WUNTRACED, wifcontinued, wifstopped}; use crate::syscall::ptrace_event; use crate::syscall::validate::{validate_slice, validate_slice_mut}; @@ -899,8 +900,22 @@ fn fexec_noreturn( } } - // Go to usermode - unsafe { usermode(entry, sp, 0); } + // Create dummy stack for ptrace to read from + let mut regs = InterruptStack::new_usermode(entry, sp, 0); + + // ptrace breakpoint + let was_traced = { + let _guard = ptrace::set_process_regs(&mut regs); + ptrace::breakpoint_callback(PtraceFlags::PTRACE_STOP_EXEC, None).is_some() + }; + + if !was_traced { + // Go to usermode, fast route + unsafe { usermode(entry, sp, 0) } + } else { + // Go to usermode, take ptrace-modified stack into account + unsafe { usermode_interrupt_stack(regs) } + } } pub fn fexec_kernel(fd: FileHandle, args: Box<[Box<[u8]>]>, vars: Box<[Box<[u8]>]>, name_override_opt: Option<Box<[u8]>>) -> Result<usize> { diff --git a/syscall b/syscall index 9ecdc11d73677477b37567a47af1633478093cbb..c23d36e8923b8087039c26fcde3c359b5e0accc7 160000 --- a/syscall +++ b/syscall @@ -1 +1 @@ -Subproject commit 9ecdc11d73677477b37567a47af1633478093cbb +Subproject commit c23d36e8923b8087039c26fcde3c359b5e0accc7