Skip to content

Commit

Permalink
Mach ports continued + support aarch64-apple unwinding (#2723)
Browse files Browse the repository at this point in the history
* Switch macOS to using mach ports for trap handling

This commit moves macOS to using mach ports instead of signals for
handling traps. The motivation for this is listed in #2456, namely that
once mach ports are used in a process that means traditional UNIX signal
handlers won't get used. This means that if Wasmtime is integrated with
Breakpad, for example, then Wasmtime's trap handler never fires and
traps don't work.

The `traphandlers` module is refactored as part of this commit to split
the platform-specific bits into their own files (it was growing quite a
lot for one inline `cfg_if!`). The `unix.rs` and `windows.rs` files
remain the same as they were before with a few minor tweaks for some
refactored interfaces. The `macos.rs` file is brand new and lifts almost
its entire implementation from SpiderMonkey, adapted for Wasmtime
though.

The main gotcha with mach ports is that a separate thread is what
services the exception. Some unsafe magic allows this separate thread to
read non-`Send` and temporary state from other threads, but is hoped to
be safe in this context. The unfortunate downside is that calling wasm
on macOS now involves taking a global lock and modifying a global hash
map twice-per-call. I'm not entirely sure how to get out of this cost
for now, but hopefully for any embeddings on macOS it's not the end of
the world.

Closes #2456

* Add a sketch of arm64 apple support

* store: maintain CallThreadState mapping when switching fibers

* cranelift/aarch64: generate unwind directives to disable pointer auth

Aarch64 post ARMv8.3 has a feature called pointer authentication,
designed to fight ROP/JOP attacks: some pointers may be signed using new
instructions, adding payloads to the high (previously unused) bits of
the pointers. More on this here: https://lwn.net/Articles/718888/

Unwinders on aarch64 need to know if some pointers contained on the call
frame contain an authentication code or not, to be able to properly
authenticate them or use them directly. Since native code may have
enabled it by default (as is the case on the Mac M1), and the default is
that this configuration value is inherited, we need to explicitly
disable it, for the only kind of supported pointers (return addresses).

To do so, we set the value of a non-existing dwarf pseudo register (34)
to 0, as documented in
https://github.com/ARM-software/abi-aa/blob/master/aadwarf64/aadwarf64.rst#note-8.

This is done at the function granularity, in the spirit of Cranelift
compilation model. Alternatively, a single directive could be generated
in the CIE, generating less information per module.

* Make exception handling work on Mac aarch64 too

* fibers: use a breakpoint instruction after the final call in wasmtime_fiber_start

Co-authored-by: Alex Crichton <alex@alexcrichton.com>
  • Loading branch information
bnjbvr and alexcrichton authored Mar 17, 2021
1 parent 4603b3b commit 5fecdfa
Show file tree
Hide file tree
Showing 15 changed files with 948 additions and 447 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions cranelift/codegen/src/isa/aarch64/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,15 @@ impl ABIMachineSpec for AArch64MachineDeps {

fn gen_prologue_frame_setup(flags: &settings::Flags) -> SmallInstVec<Inst> {
let mut insts = SmallVec::new();

if flags.unwind_info() {
insts.push(Inst::Unwind {
inst: UnwindInst::Aarch64SetPointerAuth {
return_addresses: false,
},
});
}

// stp fp (x29), lr (x30), [sp, #-16]!
insts.push(Inst::StoreP64 {
rt: fp_reg(),
Expand Down
11 changes: 11 additions & 0 deletions cranelift/codegen/src/isa/unwind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ pub mod input {
RememberState,
/// Restores the state.
RestoreState,
/// On aarch64 ARMv8.3+ devices, enables or disables pointer authentication.
Aarch64SetPointerAuth {
/// Whether return addresses (hold in LR) contain a pointer-authentication code.
return_addresses: bool,
},
}

/// Unwind information as generated by a backend.
Expand Down Expand Up @@ -234,4 +239,10 @@ pub enum UnwindInst {
/// The saved register.
reg: RealReg,
},
/// Defines if the aarch64-specific pointer authentication available for ARM v8.3+ devices
/// is enabled for certain pointers or not.
Aarch64SetPointerAuth {
/// Whether return addresses (hold in LR) contain a pointer-authentication code.
return_addresses: bool,
},
}
38 changes: 37 additions & 1 deletion cranelift/codegen/src/isa/unwind/systemv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ pub(crate) enum CallFrameInstruction {
RememberState,
RestoreState,
ArgsSize(u32),
/// Enables or disables pointer authentication on aarch64 platforms post ARMv8.3. This
/// particular item maps to gimli::ValExpression(RA_SIGN_STATE, lit0/lit1).
Aarch64SetPointerAuth {
return_addresses: bool,
},
}

impl From<gimli::write::CallFrameInstruction> for CallFrameInstruction {
Expand Down Expand Up @@ -75,7 +80,7 @@ impl From<gimli::write::CallFrameInstruction> for CallFrameInstruction {

impl Into<gimli::write::CallFrameInstruction> for CallFrameInstruction {
fn into(self) -> gimli::write::CallFrameInstruction {
use gimli::{write::CallFrameInstruction, Register};
use gimli::{write::CallFrameInstruction, write::Expression, Register};

match self {
Self::Cfa(reg, offset) => CallFrameInstruction::Cfa(Register(reg), offset),
Expand All @@ -92,6 +97,21 @@ impl Into<gimli::write::CallFrameInstruction> for CallFrameInstruction {
Self::RememberState => CallFrameInstruction::RememberState,
Self::RestoreState => CallFrameInstruction::RestoreState,
Self::ArgsSize(size) => CallFrameInstruction::ArgsSize(size),
Self::Aarch64SetPointerAuth { return_addresses } => {
// To enable pointer authentication for return addresses in dwarf directives, we
// use a small dwarf expression that sets the value of the pseudo-register
// RA_SIGN_STATE (RA stands for return address) to 0 or 1. This behavior is
// documented in
// https://github.com/ARM-software/abi-aa/blob/master/aadwarf64/aadwarf64.rst#41dwarf-register-names.
let mut expr = Expression::new();
expr.op(if return_addresses {
gimli::DW_OP_lit1
} else {
gimli::DW_OP_lit0
});
const RA_SIGN_STATE: Register = Register(34);
CallFrameInstruction::ValExpression(RA_SIGN_STATE, expr)
}
}
}
}
Expand Down Expand Up @@ -187,6 +207,12 @@ pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<regalloc::Reg>>(
let off = (clobber_offset as i32) - (clobber_offset_to_cfa as i32);
instructions.push((instruction_offset, CallFrameInstruction::Offset(reg, off)));
}
&UnwindInst::Aarch64SetPointerAuth { return_addresses } => {
instructions.push((
instruction_offset,
CallFrameInstruction::Aarch64SetPointerAuth { return_addresses },
));
}
}
}

Expand Down Expand Up @@ -245,6 +271,9 @@ impl UnwindInfo {
UnwindCode::RestoreState => {
builder.restore_state(*offset);
}
UnwindCode::Aarch64SetPointerAuth { return_addresses } => {
builder.set_aarch64_pauth(*offset, *return_addresses);
}
}
}

Expand Down Expand Up @@ -399,4 +428,11 @@ impl<'a, Reg: PartialEq + Copy> InstructionBuilder<'a, Reg> {
self.instructions
.push((offset, CallFrameInstruction::RestoreState));
}

fn set_aarch64_pauth(&mut self, offset: u32, return_addresses: bool) {
self.instructions.push((
offset,
CallFrameInstruction::Aarch64SetPointerAuth { return_addresses },
));
}
}
3 changes: 3 additions & 0 deletions cranelift/codegen/src/isa/unwind/winx64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,9 @@ pub(crate) fn create_unwind_info_from_insts<MR: RegisterMapper<regalloc::Reg>>(
});
}
},
&UnwindInst::Aarch64SetPointerAuth { .. } => {
unreachable!("no aarch64 on x64");
}
}
max_unwind_offset = instruction_offset;
}
Expand Down
6 changes: 3 additions & 3 deletions crates/fiber/src/arch/aarch64.S
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ FUNCTION(wasmtime_fiber_start):
// ... and then we call the function! Note that this is a function call so
// our frame stays on the stack to backtrace through.
blr x20
// .. technically we shouldn't get here, and I would like to write in an
// instruction which just aborts, but I don't know such an instruction in
// aarch64 land.
// Unreachable, here for safety. This should help catch unexpected behaviors.
// Use a noticeable payload so one can grep for it in the codebase.
brk 0xf1b3
.cfi_endproc
SIZE(wasmtime_fiber_start)

Expand Down
3 changes: 3 additions & 0 deletions crates/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ psm = "0.1.11"
rand = "0.8.3"
anyhow = "1.0.38"

[target.'cfg(target_os = "macos")'.dependencies]
mach = "0.3.2"

[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.7", features = ["winbase", "memoryapi", "errhandlingapi"] }

Expand Down
Loading

0 comments on commit 5fecdfa

Please sign in to comment.