Path: blob/main/crates/fiber/src/stackswitch/aarch64.rs
3068 views
// A WORD OF CAUTION1//2// This entire file basically needs to be kept in sync with itself. It's not3// really possible to modify just one bit of this file without understanding4// all the other bits. Documentation tries to reference various bits here and5// there but try to make sure to read over everything before tweaking things!6//7// Also at this time this file is heavily based off the x86_64 file, so you'll8// probably want to read that one as well.9//10// Finally, control flow integrity hardening has been applied to the code using11// the Pointer Authentication (PAuth) and Branch Target Identification (BTI)12// technologies from the Arm instruction set architecture:13// * All callable functions start with either the `BTI c` or `PACIASP`/`PACIBSP`14// instructions15// * Return addresses are signed and authenticated using the stack pointer16// value as a modifier (similarly to the salt in a HMAC operation); the17// `DW_CFA_AARCH64_negate_ra_state` DWARF operation (aliased with the18// `.cfi_window_save` assembler directive) informs an unwinder about this1920use core::arch::naked_asm;2122cfg_if::cfg_if! {23if #[cfg(target_vendor = "apple")] {24macro_rules! paci1716 { () => ("pacib1716\n"); }25macro_rules! pacisp { () => ("pacibsp\n"); }26macro_rules! autisp { () => ("autibsp\n"); }27} else {28macro_rules! paci1716 { () => ("pacia1716\n"); }29macro_rules! pacisp { () => ("paciasp\n"); }30macro_rules! autisp { () => ("autiasp\n"); }31}32}3334#[inline(never)] // FIXME(rust-lang/rust#148307)35pub(crate) unsafe extern "C" fn wasmtime_fiber_switch(top_of_stack: *mut u8) {36unsafe { wasmtime_fiber_switch_(top_of_stack) }37}3839#[unsafe(naked)]40unsafe extern "C" fn wasmtime_fiber_switch_(top_of_stack: *mut u8 /* x0 */) {41naked_asm!(concat!(42"43.cfi_startproc44",45pacisp!(),46"47.cfi_window_save48// Save all callee-saved registers on the stack since we're49// assuming they're clobbered as a result of the stack switch.50stp x29, x30, [sp, -16]!51stp x27, x28, [sp, -16]!52stp x25, x26, [sp, -16]!53stp x23, x24, [sp, -16]!54stp x21, x22, [sp, -16]!55stp x19, x20, [sp, -16]!56stp d14, d15, [sp, -16]!57stp d12, d13, [sp, -16]!58stp d10, d11, [sp, -16]!59stp d8, d9, [sp, -16]!6061// Load our previously saved stack pointer to resume to, and save62// off our current stack pointer on where to come back to63// eventually.64ldr x8, [x0, -0x10]65mov x9, sp66str x9, [x0, -0x10]6768// Switch to the new stack and restore all our callee-saved69// registers after the switch and return to our new stack.70mov sp, x871ldp d8, d9, [sp], 1672ldp d10, d11, [sp], 1673ldp d12, d13, [sp], 1674ldp d14, d15, [sp], 167576ldp x19, x20, [sp], 1677ldp x21, x22, [sp], 1678ldp x23, x24, [sp], 1679ldp x25, x26, [sp], 1680ldp x27, x28, [sp], 1681ldp x29, x30, [sp], 1682",83autisp!(),84"85.cfi_window_save86ret87.cfi_endproc88",89));90}9192pub(crate) unsafe fn wasmtime_fiber_init(93top_of_stack: *mut u8,94entry_point: extern "C" fn(*mut u8, *mut u8),95entry_arg0: *mut u8, // x296) {97#[repr(C)]98#[derive(Default)]99struct InitialStack {100d8: u64,101d9: u64,102d10: u64,103d11: u64,104d12: u64,105d13: u64,106d14: u64,107d15: u64,108109x19: *mut u8,110x20: *mut u8,111x21: *mut u8,112x22: *mut u8,113x23: *mut u8,114x24: *mut u8,115x25: *mut u8,116x26: *mut u8,117x27: *mut u8,118x28: *mut u8,119120fp: *mut u8,121lr: *mut u8,122123// unix.rs reserved space124last_sp: *mut u8,125run_result: *mut u8,126}127128unsafe {129let initial_stack = top_of_stack.cast::<InitialStack>().sub(1);130initial_stack.write(InitialStack {131x19: top_of_stack,132x20: entry_point as *mut u8,133x21: entry_arg0,134135// We set up the newly initialized fiber, so that it resumes136// execution from wasmtime_fiber_start(). As a result, we need a137// signed address of this function because `wasmtime_fiber_switch`138// ends with a `auti{a,b}sp` instruction. There are 2 requirements:139// * We would like to use an instruction that is executed as a no-op140// by processors that do not support PAuth, so that the code is141// backward-compatible and there is no duplication; `PACIA1716` is142// a suitable one.143// * The fiber stack pointer value that is used by the signing144// operation must match the value when the pointer is145// authenticated inside wasmtime_fiber_switch(), which is 16 bytes146// below the `top_of_stack` which will be `sp` at the time of the147// `auti{a,b}sp`.148//149// TODO: Use the PACGA instruction to authenticate the saved register150// state, which avoids creating signed pointers to151// wasmtime_fiber_start(), and provides wider coverage.152lr: paci1716(wasmtime_fiber_start as *mut u8, top_of_stack.sub(16)),153154last_sp: initial_stack.cast(),155..InitialStack::default()156});157}158}159160/// Signs `r17` with the value in `r16` using either `paci{a,b}1716` depending161/// on the platform.162fn paci1716(mut r17: *mut u8, r16: *mut u8) -> *mut u8 {163unsafe {164core::arch::asm!(165paci1716!(),166inout("x17") r17,167in("x16") r16,168);169r17170}171}172173// See the x86_64 file for more commentary on what these CFI directives are174// doing. Like over there note that the relative offsets to registers here175// match the frame layout in `wasmtime_fiber_switch`.176#[unsafe(naked)]177unsafe extern "C" fn wasmtime_fiber_start() -> ! {178naked_asm!(179"180.cfi_startproc simple181.cfi_def_cfa_offset 0182.cfi_escape 0x0f, /* DW_CFA_def_cfa_expression */ \1835, /* the byte length of this expression */ \1840x6f, /* DW_OP_reg31(%sp) */ \1850x06, /* DW_OP_deref */ \1860x23, 0xa0, 0x1 /* DW_OP_plus_uconst 0xa0 */187.cfi_rel_offset x30, -0x08188.cfi_rel_offset x29, -0x10189.cfi_window_save190.cfi_rel_offset x28, -0x18191.cfi_rel_offset x27, -0x20192.cfi_rel_offset x26, -0x28193.cfi_rel_offset x25, -0x30194.cfi_rel_offset x24, -0x38195.cfi_rel_offset x23, -0x40196.cfi_rel_offset x22, -0x48197.cfi_rel_offset x21, -0x50198.cfi_rel_offset x20, -0x58199.cfi_rel_offset x19, -0x60200201// Load our two arguments from the stack, where x1 is our start202// procedure and x0 is its first argument. This also blows away the203// stack space used by those two arguments.204mov x0, x21205mov x1, x19206207// ... and then we call the function! Note that this is a function call208// so our frame stays on the stack to backtrace through.209blr x20210// Unreachable, here for safety. This should help catch unexpected211// behaviors. Use a noticeable payload so one can grep for it in the212// codebase.213brk 0xf1b3214.cfi_endproc215",216);217}218219220