diff --git a/src/arch/x86_64/interrupt/handler.rs b/src/arch/x86_64/interrupt/handler.rs index 0a2cfa3..70f3b13 100644 --- a/src/arch/x86_64/interrupt/handler.rs +++ b/src/arch/x86_64/interrupt/handler.rs @@ -207,13 +207,16 @@ macro_rules! intel_asm { } #[macro_export] macro_rules! function { - ($name:expr => { $($body:expr,)+ }) => { + ($name:ident => { $($body:expr,)+ }) => { intel_asm!( - ".global ", $name, "\n", - $name, ":\n", + ".global ", stringify!($name), "\n", + stringify!($name), ":\n", $($body,)+ ); - } + extern "C" { + pub fn $name(); + } + }; } #[macro_export] @@ -249,8 +252,11 @@ macro_rules! push_fs { push fs // Load kernel tls - mov rax, 0x18 - mov fs, ax // can't load value directly into `fs` + // We can't load the value directly into `fs`. We also can't use `rax` + // as the temporary value, as during errors that's already used for the + // error code. + mov rbx, 0x18 + mov fs, bx " }; } @@ -294,15 +300,11 @@ macro_rules! interrupt_stack { ($name:ident, |$stack:ident| $code:block) => { paste::item! { #[no_mangle] - unsafe extern "C" fn [<__interrupt_ $name>]($stack: *mut $crate::arch::x86_64::interrupt::InterruptStack) { - unsafe fn [<__interrupt_inner_ $name>]($stack: &mut $crate::arch::x86_64::interrupt::InterruptStack) { - $code - } - - [<__interrupt_inner_ $name>](&mut *$stack); + unsafe extern "C" fn [<__interrupt_ $name>]($stack: &mut $crate::arch::x86_64::interrupt::InterruptStack) { + $code } - function!(stringify!($name) => { + function!($name => { // Backup all userspace registers to stack "push rax\n", push_scratch!(), @@ -326,10 +328,6 @@ macro_rules! interrupt_stack { "iretq\n", }); - - extern "C" { - pub fn $name(); - } } }; } @@ -340,14 +338,10 @@ macro_rules! interrupt { paste::item! { #[no_mangle] unsafe extern "C" fn [<__interrupt_ $name>]() { - unsafe fn [<__interrupt_inner_ $name>]() { - $code - } - - [<__interrupt_inner_ $name>](); + $code } - function!(stringify!($name) => { + function!($name => { // Backup all userspace registers to stack "push rax\n", push_scratch!(), @@ -368,10 +362,6 @@ macro_rules! interrupt { "iretq\n", }); - - extern "C" { - pub fn $name(); - } } }; } @@ -381,15 +371,11 @@ macro_rules! interrupt_error { ($name:ident, |$stack:ident| $code:block) => { paste::item! { #[no_mangle] - unsafe extern "C" fn [<__interrupt_ $name>]($stack: *mut $crate::arch::x86_64::interrupt::handler::InterruptErrorStack) { - unsafe fn [<__interrupt_inner_ $name>]($stack: &mut $crate::arch::x86_64::interrupt::handler::InterruptErrorStack) { - $code - } - - [<__interrupt_inner_ $name>](&mut *$stack); + unsafe extern "C" fn [<__interrupt_ $name>]($stack: &mut $crate::arch::x86_64::interrupt::handler::InterruptErrorStack) { + $code } - function!(stringify!($name) => { + function!($name => { // Move rax into code's place, put code in last instead (to be // compatible with InterruptStack) "xchg [rsp], rax\n", @@ -422,10 +408,6 @@ macro_rules! interrupt_error { "iretq\n", }); - - extern "C" { - pub fn $name(); - } } }; } diff --git a/src/arch/x86_64/interrupt/syscall.rs b/src/arch/x86_64/interrupt/syscall.rs index 56d3ac6..4ec6b9d 100644 --- a/src/arch/x86_64/interrupt/syscall.rs +++ b/src/arch/x86_64/interrupt/syscall.rs @@ -1,5 +1,5 @@ use crate::arch::interrupt::InterruptStack; -use crate::arch::{gdt, pti}; +use crate::arch::gdt; use crate::syscall::flag::{PTRACE_FLAG_IGNORE, PTRACE_STOP_PRE_SYSCALL, PTRACE_STOP_POST_SYSCALL}; use crate::{ptrace, syscall}; use x86::msr; @@ -14,46 +14,53 @@ pub unsafe fn init() { msr::wrmsr(msr::IA32_EFER, efer | 1); } -// Not a function pointer because it somehow messes up the returning -// from clone() (via clone_ret()). Not sure what the problem is. macro_rules! with_interrupt_stack { - (unsafe fn $wrapped:ident($stack:ident) -> usize $code:block) => { - #[inline(never)] - unsafe fn $wrapped(stack: *mut InterruptStack) { - let _guard = ptrace::set_process_regs(stack); + ($name:ident |$stack:ident| $code:block) => {{ + let _guard = ptrace::set_process_regs($stack); - let allowed = ptrace::breakpoint_callback(PTRACE_STOP_PRE_SYSCALL, None) - .and_then(|_| ptrace::next_breakpoint().map(|f| !f.contains(PTRACE_FLAG_IGNORE))); + let allowed = ptrace::breakpoint_callback(PTRACE_STOP_PRE_SYSCALL, None) + .and_then(|_| ptrace::next_breakpoint().map(|f| !f.contains(PTRACE_FLAG_IGNORE))); - if allowed.unwrap_or(true) { - // If syscall not ignored - let $stack = &mut *stack; - $stack.scratch.rax = $code; + if allowed.unwrap_or(true) { + paste::expr! { + #[no_mangle] + unsafe extern "C" fn [<__inner_stack_ $name>]($stack: &mut InterruptStack) -> usize { + $code + } + + // A normal function call like inner(stack) is sadly not guaranteed + // to map to a `call` instruction - it can also be a `jmp`. We want + // this to definitely be its own call stack, to make `clone_ret` + // work as expected. + let ret; + asm!( + concat!("call __inner_stack_", stringify!($name)) + : "={rax}"(ret) + : "{rdi}"(&mut *$stack) + : /* no clobbers */ + : "volatile", "intel" + ); + + (*$stack).scratch.rax = ret; } - - ptrace::breakpoint_callback(PTRACE_STOP_POST_SYSCALL, None); } - } + + ptrace::breakpoint_callback(PTRACE_STOP_POST_SYSCALL, None); + }} } #[no_mangle] pub unsafe extern "C" fn __inner_syscall_instruction(stack: *mut InterruptStack) { - with_interrupt_stack! { - unsafe fn inner(stack) -> usize { - let rbp; - asm!("" : "={rbp}"(rbp) : : : "intel", "volatile"); + with_interrupt_stack!(syscall_instruction |stack| { + let rbp; + asm!("" : "={rbp}"(rbp) : : : "intel", "volatile"); - let scratch = &stack.scratch; - syscall::syscall(scratch.rax, scratch.rdi, scratch.rsi, scratch.rdx, scratch.r10, scratch.r8, rbp, stack) - } - } - - pti::map(); - inner(stack); - pti::unmap(); + let scratch = &stack.scratch; + syscall::syscall(scratch.rax, scratch.rdi, scratch.rsi, scratch.rdx, scratch.r10, scratch.r8, rbp, stack) + }); } -function!("syscall_instruction" => { +function!(syscall_instruction => { // Yes, this is magic. No, you don't need to understand " swapgs // Set gs segment to TSS @@ -74,10 +81,16 @@ function!("syscall_instruction" => { push_preserved!(), push_fs!(), + // TODO: Map PTI + // $crate::arch::x86_64::pti::map(); + // Call inner funtion "mov rdi, rsp\n", "call __inner_syscall_instruction\n", + // TODO: Unmap PTI + // $crate::arch::x86_64::pti::unmap(); + // Pop context registers pop_fs!(), pop_preserved!(), @@ -87,34 +100,27 @@ function!("syscall_instruction" => { "iretq\n", }); -extern "C" { - pub fn syscall_instruction(); -} - interrupt_stack!(syscall, |stack| { - with_interrupt_stack! { - unsafe fn inner(stack) -> usize { - let rbp; - asm!("" : "={rbp}"(rbp) : : : "intel", "volatile"); + with_interrupt_stack!(syscall |stack| { + let rbp; + asm!("" : "={rbp}"(rbp) : : : "intel", "volatile"); - let scratch = &stack.scratch; - syscall::syscall(scratch.rax, stack.preserved.rbx, scratch.rcx, scratch.rdx, scratch.rsi, scratch.rdi, rbp, stack) - } - } - inner(stack); + let scratch = &stack.scratch; + syscall::syscall(scratch.rax, stack.preserved.rbx, scratch.rcx, scratch.rdx, scratch.rsi, scratch.rdi, rbp, stack) + }) }); -#[naked] -pub unsafe extern "C" fn clone_ret() { - // The C x86_64 ABI specifies that rbp is pushed to save the old - // call frame. Popping rbp means we're using the parent's call - // frame and thus will not only return from this function but also - // from the function above this one. - // When this is called, the stack should have been - // interrupt->inner->syscall->clone - // then changed to - // interrupt->inner->clone_ret->clone - // so this will return from "inner". +function!(clone_ret => { + // The C x86_64 ABI specifies that rbp is pushed to save the old call frame. + // Popping rbp means we're using the parent's call frame and thus will not + // only return from this function but also from the function above this one. - asm!("pop rbp" : : : : "intel", "volatile"); -} + // When this is called, the stack should have been + // interrupt->inner->syscall->clone->clone_ret + // then popped to + // interrupt->inner->syscall + // so this will return from "syscall". + "pop rbx\n", + "xor rax, rax\n", + "ret\n", +}); diff --git a/src/syscall/process.rs b/src/syscall/process.rs index 161896d..b0f3f38 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -138,18 +138,16 @@ pub fn clone(flags: CloneFlags, stack_base: usize) -> Result { unsafe { if let Some(regs) = ptrace::rebase_regs_ptr_mut(context.regs, Some(&mut new_stack)) { - // We'll need to tell the clone that it should - // return 0, but that's it. We don't actually - // clone the registers, because it will then - // become None and be exempt from all kinds of - // ptracing until the current syscall has - // completed. + // We'll need to tell the clone that it should return 0, + // but that's it. We don't actually clone the registers + // and put them on the child, because it will then + // instead become None and be exempt from all kinds of + // ptracing until the current syscall has completed. (*regs).scratch.rax = 0; } - // Change the return address of the child - // (previously syscall) to the arch-specific - // clone_ret callback + // Change the return address of the child (previously + // syscall) to the arch-specific clone_ret callback let func_ptr = new_stack.as_mut_ptr().add(offset); *(func_ptr as *mut usize) = interrupt::syscall::clone_ret as usize; }