WIP: Ensure clone_ret correctness
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user