diff --git a/Cargo.lock b/Cargo.lock index 68eeeda0d1c..29c762cc714 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -751,9 +751,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "dynasm" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b1801e630bd336d0bbbdbf814de6cc749c9a400c7e3d995e6adfd455d0c83c" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" dependencies = [ "bitflags", "byteorder", @@ -766,9 +766,9 @@ dependencies = [ [[package]] name = "dynasmrt" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d428afc93ad288f6dffc1fa5f4a78201ad2eec33c5a522e51c181009eb09061" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", diff --git a/lib/compiler-singlepass/Cargo.toml b/lib/compiler-singlepass/Cargo.toml index 1fbfc0c2f24..31fa33a2abd 100644 --- a/lib/compiler-singlepass/Cargo.toml +++ b/lib/compiler-singlepass/Cargo.toml @@ -17,8 +17,8 @@ wasmer-types = { path = "../types", version = "=2.2.1", default-features = false hashbrown = { version = "0.11", optional = true } gimli = { version = "0.26", optional = true } more-asserts = "0.2" -dynasm = "1.2.1" -dynasmrt = "1.2.1" +dynasm = "1.2.3" +dynasmrt = "1.2.3" lazy_static = "1.4" byteorder = "1.3" smallvec = "1.6" diff --git a/lib/compiler-singlepass/src/codegen.rs b/lib/compiler-singlepass/src/codegen.rs index 580eaec52b3..f01e05fc492 100644 --- a/lib/compiler-singlepass/src/codegen.rs +++ b/lib/compiler-singlepass/src/codegen.rs @@ -3765,11 +3765,8 @@ impl<'a, M: Machine> FuncGen<'a, M> { } Operator::Unreachable => { self.mark_trappable(); - let offset = self - .machine - .mark_instruction_with_trap_code(TrapCode::UnreachableCodeReached); - self.machine.emit_illegal_op(); - self.machine.mark_instruction_address_end(offset); + self.machine + .emit_illegal_op(TrapCode::UnreachableCodeReached); self.unreachable_depth = 1; } Operator::Return => { @@ -5861,36 +5858,27 @@ impl<'a, M: Machine> FuncGen<'a, M> { self.machine .emit_label(self.special_labels.integer_division_by_zero); self.machine - .mark_address_with_trap_code(TrapCode::IntegerDivisionByZero); - self.machine.emit_illegal_op(); + .emit_illegal_op(TrapCode::IntegerDivisionByZero); self.machine .emit_label(self.special_labels.integer_overflow); - self.machine - .mark_address_with_trap_code(TrapCode::IntegerOverflow); - self.machine.emit_illegal_op(); + self.machine.emit_illegal_op(TrapCode::IntegerOverflow); self.machine.emit_label(self.special_labels.heap_access_oob); self.machine - .mark_address_with_trap_code(TrapCode::HeapAccessOutOfBounds); - self.machine.emit_illegal_op(); + .emit_illegal_op(TrapCode::HeapAccessOutOfBounds); self.machine .emit_label(self.special_labels.table_access_oob); self.machine - .mark_address_with_trap_code(TrapCode::TableAccessOutOfBounds); - self.machine.emit_illegal_op(); + .emit_illegal_op(TrapCode::TableAccessOutOfBounds); self.machine .emit_label(self.special_labels.indirect_call_null); - self.machine - .mark_address_with_trap_code(TrapCode::IndirectCallToNull); - self.machine.emit_illegal_op(); + self.machine.emit_illegal_op(TrapCode::IndirectCallToNull); self.machine.emit_label(self.special_labels.bad_signature); - self.machine - .mark_address_with_trap_code(TrapCode::BadSignature); - self.machine.emit_illegal_op(); + self.machine.emit_illegal_op(TrapCode::BadSignature); // Notify the assembler backend to generate necessary code at end of function. self.machine.finalize_function(); diff --git a/lib/compiler-singlepass/src/emitter_arm64.rs b/lib/compiler-singlepass/src/emitter_arm64.rs index 5a7b0c74da6..abc797fac27 100644 --- a/lib/compiler-singlepass/src/emitter_arm64.rs +++ b/lib/compiler-singlepass/src/emitter_arm64.rs @@ -170,7 +170,7 @@ pub trait EmitterARM64 { fn emit_call_register(&mut self, reg: GPR); fn emit_ret(&mut self); - fn emit_udf(&mut self); + fn emit_udf(&mut self, payload: u16); fn emit_dmb(&mut self); fn emit_brk(&mut self); @@ -2015,8 +2015,8 @@ impl EmitterARM64 for Assembler { dynasm!(self ; ret); } - fn emit_udf(&mut self) { - dynasm!(self ; udf 0x1234); + fn emit_udf(&mut self, payload: u16) { + dynasm!(self ; udf (payload as u32)); } fn emit_dmb(&mut self) { dynasm!(self ; dmb ish); diff --git a/lib/compiler-singlepass/src/emitter_x64.rs b/lib/compiler-singlepass/src/emitter_x64.rs index 7c773007753..a3e3aaa588b 100644 --- a/lib/compiler-singlepass/src/emitter_x64.rs +++ b/lib/compiler-singlepass/src/emitter_x64.rs @@ -198,6 +198,7 @@ pub trait EmitterX64 { fn emit_test_gpr_64(&mut self, reg: GPR); fn emit_ud2(&mut self); + fn emit_ud1_payload(&mut self, payload: u8); fn emit_ret(&mut self); fn emit_call_label(&mut self, label: Label); fn emit_call_location(&mut self, loc: Location); @@ -1853,6 +1854,10 @@ impl EmitterX64 for AssemblerX64 { fn emit_ud2(&mut self) { dynasm!(self ; ud2); } + fn emit_ud1_payload(&mut self, payload: u8) { + assert!(payload & 0xf0 == 0); + dynasm!(self ; ud1 Rd((payload>>3)&1), Rd(payload&7)); + } fn emit_ret(&mut self) { dynasm!(self ; ret); } diff --git a/lib/compiler-singlepass/src/machine.rs b/lib/compiler-singlepass/src/machine.rs index b55ff38efd3..69ede6316c6 100644 --- a/lib/compiler-singlepass/src/machine.rs +++ b/lib/compiler-singlepass/src/machine.rs @@ -253,8 +253,8 @@ pub trait Machine { output: Location, ); - /// emit an Illegal Opcode - fn emit_illegal_op(&mut self); + /// emit an Illegal Opcode, associated with a trapcode + fn emit_illegal_op(&mut self, trp: TrapCode); /// create a new label fn get_label(&mut self) -> Label; /// emit a label diff --git a/lib/compiler-singlepass/src/machine_arm64.rs b/lib/compiler-singlepass/src/machine_arm64.rs index 3e98b36b235..febf9f7755c 100644 --- a/lib/compiler-singlepass/src/machine_arm64.rs +++ b/lib/compiler-singlepass/src/machine_arm64.rs @@ -1206,20 +1206,10 @@ impl MachineARM64 { self.assembler.emit_fcmp(sz, f, f); self.assembler.emit_bcond_label(Condition::Vs, trap_badconv); // fallthru: trap_overflow - let offset = self.assembler.get_offset().0; - self.trap_table - .offset_to_code - .insert(offset, TrapCode::IntegerOverflow); - self.emit_illegal_op(); - self.mark_instruction_address_end(offset); + self.emit_illegal_op_internal(TrapCode::IntegerOverflow); self.emit_label(trap_badconv); - let offset = self.assembler.get_offset().0; - self.trap_table - .offset_to_code - .insert(offset, TrapCode::BadConversionToInteger); - self.emit_illegal_op(); - self.mark_instruction_address_end(offset); + self.emit_illegal_op_internal(TrapCode::BadConversionToInteger); self.emit_label(end); self.restore_fpcr(old_fpcr); @@ -1250,6 +1240,9 @@ impl MachineARM64 { fn emit_unwind_op(&mut self, op: UnwindOps) { self.unwind_ops.push((self.get_offset().0, op)); } + fn emit_illegal_op_internal(&mut self, trap: TrapCode) { + self.assembler.emit_udf(0xc0 | (trap as u8) as u16); + } } impl Machine for MachineARM64 { @@ -2179,8 +2172,10 @@ impl Machine for MachineARM64 { } } - fn emit_illegal_op(&mut self) { - self.assembler.emit_udf(); + fn emit_illegal_op(&mut self, trap: TrapCode) { + let offset = self.assembler.get_offset().0; + self.assembler.emit_udf(0xc0 | (trap as u8) as u16); + self.mark_instruction_address_end(offset); } fn get_label(&mut self) -> Label { self.assembler.new_dynamic_label() diff --git a/lib/compiler-singlepass/src/machine_x64.rs b/lib/compiler-singlepass/src/machine_x64.rs index c1eb953bae1..4f8d892e940 100644 --- a/lib/compiler-singlepass/src/machine_x64.rs +++ b/lib/compiler-singlepass/src/machine_x64.rs @@ -676,21 +676,12 @@ impl MachineX86_64 { ); self.emit_label(trap_overflow); - let offset = self.assembler.get_offset().0; - self.trap_table - .offset_to_code - .insert(offset, TrapCode::IntegerOverflow); - self.emit_illegal_op(); - self.mark_instruction_address_end(offset); + + self.emit_illegal_op_internal(TrapCode::IntegerOverflow); self.emit_label(trap_badconv); - let offset = self.assembler.get_offset().0; - self.trap_table - .offset_to_code - .insert(offset, TrapCode::BadConversionToInteger); - self.emit_illegal_op(); - self.mark_instruction_address_end(offset); + self.emit_illegal_op_internal(TrapCode::BadConversionToInteger); self.emit_label(end); } @@ -819,20 +810,10 @@ impl MachineX86_64 { ); self.emit_label(trap_overflow); - let offset = self.assembler.get_offset().0; - self.trap_table - .offset_to_code - .insert(offset, TrapCode::IntegerOverflow); - self.emit_illegal_op(); - self.mark_instruction_address_end(offset); + self.emit_illegal_op_internal(TrapCode::IntegerOverflow); self.emit_label(trap_badconv); - let offset = self.assembler.get_offset().0; - self.trap_table - .offset_to_code - .insert(offset, TrapCode::BadConversionToInteger); - self.emit_illegal_op(); - self.mark_instruction_address_end(offset); + self.emit_illegal_op_internal(TrapCode::BadConversionToInteger); self.emit_label(end); } @@ -1670,6 +1651,10 @@ impl MachineX86_64 { fn emit_unwind_op(&mut self, op: UnwindOps) { self.unwind_ops.push((self.get_offset().0, op)); } + fn emit_illegal_op_internal(&mut self, trap: TrapCode) { + let v = trap as u8; + self.assembler.emit_ud1_payload(v); + } } impl Machine for MachineX86_64 { @@ -2331,8 +2316,22 @@ impl Machine for MachineX86_64 { self.release_simd(tmp1); } - fn emit_illegal_op(&mut self) { + fn emit_illegal_op(&mut self, trap: TrapCode) { + // code below is kept as a reference on how to emit illegal op with trap info + // without an Undefined opcode with payload + /* + let offset = self.assembler.get_offset().0; + self.trap_table + .offset_to_code + .insert(offset, trap); self.assembler.emit_ud2(); + self.mark_instruction_address_end(offset);*/ + let v = trap as u8; + // payload needs to be between 0-15 + // this will emit an 40 0F B9 Cx opcode, with x the payload + let offset = self.assembler.get_offset().0; + self.assembler.emit_ud1_payload(v); + self.mark_instruction_address_end(offset); } fn get_label(&mut self) -> Label { self.assembler.new_dynamic_label() diff --git a/lib/vm/src/trap/traphandlers.rs b/lib/vm/src/trap/traphandlers.rs index baecaf387f3..b076a9c715a 100644 --- a/lib/vm/src/trap/traphandlers.rs +++ b/lib/vm/src/trap/traphandlers.rs @@ -7,6 +7,7 @@ use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMTrampoline}; use crate::Trap; use backtrace::Backtrace; +use core::ptr::{read, read_unaligned}; use corosensei::stack::DefaultStack; use corosensei::trap::{CoroutineTrapHandler, TrapHandlerRegs}; use corosensei::{CoroutineResult, ScopedCoroutine, Yielder}; @@ -23,6 +24,11 @@ use std::sync::atomic::{compiler_fence, AtomicPtr, Ordering}; use std::sync::{Mutex, Once}; use wasmer_types::TrapCode; +// TrapInformation can be stored in the "Undefined Instruction" itself. +// On x86_64, 0xC? select a "Register" for the Mod R/M part of "ud1" (so with no other bytes after) +// On Arm64, the udf alows for a 16bits values, so we'll use the same 0xC? to store the trapinfo +static MAGIC: u8 = 0xc0; + cfg_if::cfg_if! { if #[cfg(unix)] { /// Function which may handle custom signals while processing traps. @@ -33,6 +39,54 @@ cfg_if::cfg_if! { } } +// Process an IllegalOpcode to see if it has a TrapCode payload +unsafe fn process_illegal_op(addr: usize) -> Option { + let mut val: Option = None; + if cfg!(target_arch = "x86_64") { + val = if read(addr as *mut u8) & 0xf0 == 0x40 + && read((addr + 1) as *mut u8) == 0x0f + && read((addr + 2) as *mut u8) == 0xb9 + { + Some(read((addr + 3) as *mut u8)) + } else if read(addr as *mut u8) == 0x0f && read((addr + 1) as *mut u8) == 0xb9 { + Some(read((addr + 2) as *mut u8)) + } else { + None + } + } + if cfg!(target_arch = "aarch64") { + val = if read_unaligned(addr as *mut u32) & 0xffff0000 == 0 { + Some(read(addr as *mut u8)) + } else { + None + } + } + match val.and_then(|val| { + if val & MAGIC == MAGIC { + Some(val & 0xf) + } else { + None + } + }) { + None => None, + Some(val) => match val { + 0 => Some(TrapCode::StackOverflow), + 1 => Some(TrapCode::HeapAccessOutOfBounds), + 2 => Some(TrapCode::HeapMisaligned), + 3 => Some(TrapCode::TableAccessOutOfBounds), + 4 => Some(TrapCode::OutOfBounds), + 5 => Some(TrapCode::IndirectCallToNull), + 6 => Some(TrapCode::BadSignature), + 7 => Some(TrapCode::IntegerOverflow), + 8 => Some(TrapCode::IntegerDivisionByZero), + 9 => Some(TrapCode::BadConversionToInteger), + 10 => Some(TrapCode::UnreachableCodeReached), + 11 => Some(TrapCode::UnalignedAtomic), + _ => None, + }, + } +} + /// A package of functionality needed by `catch_traps` to figure out what to do /// when handling a trap. /// @@ -154,13 +208,21 @@ cfg_if::cfg_if! { } _ => None, }; - + let trap_code = match signum { + // check if it was cased by a UD and if the Trap info is a payload to it + libc::SIGILL => { + let addr = (*siginfo).si_addr() as usize; + process_illegal_op(addr) + } + _ => None, + }; let ucontext = &mut *(context as *mut libc::ucontext_t); let (pc, sp) = get_pc_sp(ucontext); let handled = TrapHandlerContext::handle_trap( pc, sp, maybe_fault_address, + trap_code, |regs| update_context(ucontext, regs), |handler| handler(signum, siginfo, context), ); @@ -408,13 +470,20 @@ cfg_if::cfg_if! { EXCEPTION_STACK_OVERFLOW => Some(sp), _ => None, }; - + let trap_code = match record.ExceptionCode { + // check if it was cased by a UD and if the Trap info is a payload to it + EXCEPTION_ILLEGAL_INSTRUCTION => { + process_illegal_op(pc) + } + _ => None, + }; // This is basically the same as the unix version above, only with a // few parameters tweaked here and there. let handled = TrapHandlerContext::handle_trap( pc, sp, maybe_fault_address, + trap_code, |regs| update_context(context, regs), |handler| handler(exception_info), ); @@ -587,8 +656,14 @@ thread_local! { /// from traps. struct TrapHandlerContext { inner: *const u8, - handle_trap: - fn(*const u8, usize, usize, Option, &mut dyn FnMut(TrapHandlerRegs)) -> bool, + handle_trap: fn( + *const u8, + usize, + usize, + Option, + Option, + &mut dyn FnMut(TrapHandlerRegs), + ) -> bool, custom_trap: *const dyn TrapHandler, } struct TrapHandlerContextInner { @@ -611,6 +686,7 @@ impl TrapHandlerContext { pc: usize, sp: usize, maybe_fault_address: Option, + trap_code: Option, update_regs: &mut dyn FnMut(TrapHandlerRegs), ) -> bool { unsafe { @@ -618,6 +694,7 @@ impl TrapHandlerContext { pc, sp, maybe_fault_address, + trap_code, update_regs, ) } @@ -652,6 +729,7 @@ impl TrapHandlerContext { pc: usize, sp: usize, maybe_fault_address: Option, + trap_code: Option, mut update_regs: impl FnMut(TrapHandlerRegs), call_handler: impl Fn(&TrapHandlerFn) -> bool, ) -> bool { @@ -667,7 +745,14 @@ impl TrapHandlerContext { return true; } - (ctx.handle_trap)(ctx.inner, pc, sp, maybe_fault_address, &mut update_regs) + (ctx.handle_trap)( + ctx.inner, + pc, + sp, + maybe_fault_address, + trap_code, + &mut update_regs, + ) } } @@ -677,6 +762,7 @@ impl TrapHandlerContextInner { pc: usize, sp: usize, maybe_fault_address: Option, + trap_code: Option, update_regs: &mut dyn FnMut(TrapHandlerRegs), ) -> bool { // Check if this trap occurred while executing on the Wasm stack. We can @@ -685,12 +771,14 @@ impl TrapHandlerContextInner { return false; } - let signal_trap = maybe_fault_address.map(|addr| { - if self.coro_trap_handler.stack_ptr_in_bounds(addr) { - TrapCode::StackOverflow - } else { - TrapCode::HeapAccessOutOfBounds - } + let signal_trap = trap_code.or_else(|| { + maybe_fault_address.map(|addr| { + if self.coro_trap_handler.stack_ptr_in_bounds(addr) { + TrapCode::StackOverflow + } else { + TrapCode::HeapAccessOutOfBounds + } + }) }); // Don't try to generate a backtrace for stack overflows: unwinding