ptrace: Allow stopping on fexec

This commit is contained in:
jD91mZM2
2020-06-21 17:54:09 +02:00
parent e18a877995
commit 7fc49eed74
5 changed files with 111 additions and 46 deletions

View File

@@ -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]

View File

@@ -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!();
}
};

View File

@@ -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!();
}

View File

@@ -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> {

Submodule syscall updated: 9ecdc11d73...c23d36e892