WIP: Ensure clone_ret correctness

This commit is contained in:
jD91mZM2
2020-07-14 16:32:51 +02:00
parent ca3ddcdeca
commit 3bb234009a
3 changed files with 88 additions and 102 deletions

View File

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

View File

@@ -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",
});

View File

@@ -138,18 +138,16 @@ pub fn clone(flags: CloneFlags, stack_base: usize) -> Result<ContextId> {
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;
}