1#![allow(static_mut_refs)]
5
6use crate::vmcontext::{VMFunctionContext, VMTrampoline};
10use crate::{Trap, VMContext, VMFunctionBody};
11use backtrace::Backtrace;
12use core::ptr::{read, read_unaligned};
13use corosensei::stack::DefaultStack;
14use corosensei::trap::{CoroutineTrapHandler, TrapHandlerRegs};
15use corosensei::{Coroutine, CoroutineResult, Yielder};
16use scopeguard::defer;
17use std::any::Any;
18use std::cell::Cell;
19use std::error::Error;
20use std::io;
21use std::mem;
22#[cfg(unix)]
23use std::mem::MaybeUninit;
24use std::ptr::{self, NonNull};
25use std::sync::atomic::{compiler_fence, AtomicPtr, AtomicUsize, Ordering};
26use std::sync::Once;
27use wasmer_types::TrapCode;
28
29pub struct VMConfig {
32 pub wasm_stack_size: Option<usize>,
34}
35
36static MAGIC: u8 = 0xc0;
40
41static DEFAULT_STACK_SIZE: AtomicUsize = AtomicUsize::new(1024 * 1024);
42
43#[repr(C)]
46#[cfg(all(target_arch = "aarch64", target_os = "macos"))]
47#[allow(non_camel_case_types)]
48struct ucontext_t {
49 uc_onstack: libc::c_int,
50 uc_sigmask: libc::sigset_t,
51 uc_stack: libc::stack_t,
52 uc_link: *mut libc::ucontext_t,
53 uc_mcsize: usize,
54 uc_mcontext: libc::mcontext_t,
55}
56
57#[repr(C)]
60#[cfg(all(target_arch = "aarch64", target_os = "freebsd"))]
61#[allow(non_camel_case_types)]
62struct ucontext_t {
63 uc_sigmask: libc::sigset_t,
64 uc_mcontext: libc::mcontext_t,
65 uc_link: *mut ucontext_t,
66 uc_stack: libc::stack_t,
67 uc_flags: libc::c_int,
68 spare: [libc::c_int; 4],
69}
70
71#[cfg(all(
72 unix,
73 not(all(target_arch = "aarch64", target_os = "macos")),
74 not(all(target_arch = "aarch64", target_os = "freebsd"))
75))]
76use libc::ucontext_t;
77
78pub fn set_stack_size(size: usize) {
80 DEFAULT_STACK_SIZE.store(size.clamp(8 * 1024, 100 * 1024 * 1024), Ordering::Relaxed);
81}
82
83cfg_if::cfg_if! {
84 if #[cfg(unix)] {
85 pub type TrapHandlerFn<'a> = dyn Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool + Send + Sync + 'a;
87 } else if #[cfg(target_os = "windows")] {
88 pub type TrapHandlerFn<'a> = dyn Fn(*mut windows_sys::Win32::System::Diagnostics::Debug::EXCEPTION_POINTERS) -> bool + Send + Sync + 'a;
90 }
91}
92
93unsafe fn process_illegal_op(addr: usize) -> Option<TrapCode> {
95 let mut val: Option<u8> = None;
96 if cfg!(target_arch = "x86_64") {
97 val = if read(addr as *mut u8) & 0xf0 == 0x40
98 && read((addr + 1) as *mut u8) == 0x0f
99 && read((addr + 2) as *mut u8) == 0xb9
100 {
101 Some(read((addr + 3) as *mut u8))
102 } else if read(addr as *mut u8) == 0x0f && read((addr + 1) as *mut u8) == 0xb9 {
103 Some(read((addr + 2) as *mut u8))
104 } else {
105 None
106 }
107 }
108 if cfg!(target_arch = "aarch64") {
109 val = if read_unaligned(addr as *mut u32) & 0xffff0000 == 0 {
110 Some(read(addr as *mut u8))
111 } else {
112 None
113 }
114 }
115 match val.and_then(|val| {
116 if val & MAGIC == MAGIC {
117 Some(val & 0xf)
118 } else {
119 None
120 }
121 }) {
122 None => None,
123 Some(val) => match val {
124 0 => Some(TrapCode::StackOverflow),
125 1 => Some(TrapCode::HeapAccessOutOfBounds),
126 2 => Some(TrapCode::HeapMisaligned),
127 3 => Some(TrapCode::TableAccessOutOfBounds),
128 4 => Some(TrapCode::IndirectCallToNull),
129 5 => Some(TrapCode::BadSignature),
130 6 => Some(TrapCode::IntegerOverflow),
131 7 => Some(TrapCode::IntegerDivisionByZero),
132 8 => Some(TrapCode::BadConversionToInteger),
133 9 => Some(TrapCode::UnreachableCodeReached),
134 10 => Some(TrapCode::UnalignedAtomic),
135 _ => None,
136 },
137 }
138}
139
140cfg_if::cfg_if! {
141 if #[cfg(unix)] {
142 static mut PREV_SIGSEGV: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
143 static mut PREV_SIGBUS: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
144 static mut PREV_SIGILL: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
145 static mut PREV_SIGFPE: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
146
147 unsafe fn platform_init() {
148 let register = |slot: &mut MaybeUninit<libc::sigaction>, signal: i32| {
149 let mut handler: libc::sigaction = mem::zeroed();
150 handler.sa_flags = libc::SA_SIGINFO | libc::SA_NODEFER | libc::SA_ONSTACK;
164 handler.sa_sigaction = trap_handler as usize;
165 libc::sigemptyset(&mut handler.sa_mask);
166 if libc::sigaction(signal, &handler, slot.as_mut_ptr()) != 0 {
167 panic!(
168 "unable to install signal handler: {}",
169 io::Error::last_os_error(),
170 );
171 }
172 };
173
174 register(&mut PREV_SIGSEGV, libc::SIGSEGV);
176
177 register(&mut PREV_SIGILL, libc::SIGILL);
179
180 if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") {
182 register(&mut PREV_SIGFPE, libc::SIGFPE);
183 }
184
185 if cfg!(target_arch = "arm") || cfg!(target_vendor = "apple") {
188 register(&mut PREV_SIGBUS, libc::SIGBUS);
189 }
190
191 #[cfg(target_vendor = "apple")]
194 {
195 use mach2::exception_types::*;
196 use mach2::kern_return::*;
197 use mach2::port::*;
198 use mach2::thread_status::*;
199 use mach2::traps::*;
200 use mach2::mach_types::*;
201
202 extern "C" {
203 fn task_set_exception_ports(
204 task: task_t,
205 exception_mask: exception_mask_t,
206 new_port: mach_port_t,
207 behavior: exception_behavior_t,
208 new_flavor: thread_state_flavor_t,
209 ) -> kern_return_t;
210 }
211
212 #[allow(non_snake_case)]
213 #[cfg(target_arch = "x86_64")]
214 let MACHINE_THREAD_STATE = x86_THREAD_STATE64;
215 #[allow(non_snake_case)]
216 #[cfg(target_arch = "aarch64")]
217 let MACHINE_THREAD_STATE = 6;
218
219 task_set_exception_ports(
220 mach_task_self(),
221 EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC | EXC_MASK_BAD_INSTRUCTION,
222 MACH_PORT_NULL,
223 EXCEPTION_STATE_IDENTITY as exception_behavior_t,
224 MACHINE_THREAD_STATE,
225 );
226 }
227 }
228
229 unsafe extern "C" fn trap_handler(
230 signum: libc::c_int,
231 siginfo: *mut libc::siginfo_t,
232 context: *mut libc::c_void,
233 ) {
234 let previous = match signum {
235 libc::SIGSEGV => &PREV_SIGSEGV,
236 libc::SIGBUS => &PREV_SIGBUS,
237 libc::SIGFPE => &PREV_SIGFPE,
238 libc::SIGILL => &PREV_SIGILL,
239 _ => panic!("unknown signal: {}", signum),
240 };
241 let maybe_fault_address = match signum {
243 libc::SIGSEGV | libc::SIGBUS => {
244 Some((*siginfo).si_addr() as usize)
245 }
246 _ => None,
247 };
248 let trap_code = match signum {
249 libc::SIGILL => {
251 let addr = (*siginfo).si_addr() as usize;
252 process_illegal_op(addr)
253 }
254 _ => None,
255 };
256 let ucontext = &mut *(context as *mut ucontext_t);
257 let (pc, sp) = get_pc_sp(ucontext);
258 let handled = TrapHandlerContext::handle_trap(
259 pc,
260 sp,
261 maybe_fault_address,
262 trap_code,
263 |regs| update_context(ucontext, regs),
264 |handler| handler(signum, siginfo, context),
265 );
266
267 if handled {
268 return;
269 }
270
271 let previous = &*previous.as_ptr();
281 if previous.sa_flags & libc::SA_SIGINFO != 0 {
282 mem::transmute::<
283 usize,
284 extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void),
285 >(previous.sa_sigaction)(signum, siginfo, context)
286 } else if previous.sa_sigaction == libc::SIG_DFL
287 {
288 libc::sigaction(signum, previous, ptr::null_mut());
289 } else if previous.sa_sigaction != libc::SIG_IGN {
290 mem::transmute::<usize, extern "C" fn(libc::c_int)>(
291 previous.sa_sigaction
292 )(signum)
293 }
294 }
295
296 unsafe fn get_pc_sp(context: &ucontext_t) -> (usize, usize) {
297 let (pc, sp);
298 cfg_if::cfg_if! {
299 if #[cfg(all(
300 any(target_os = "linux", target_os = "android"),
301 target_arch = "x86_64",
302 ))] {
303 pc = context.uc_mcontext.gregs[libc::REG_RIP as usize] as usize;
304 sp = context.uc_mcontext.gregs[libc::REG_RSP as usize] as usize;
305 } else if #[cfg(all(
306 any(target_os = "linux", target_os = "android"),
307 target_arch = "x86",
308 ))] {
309 pc = context.uc_mcontext.gregs[libc::REG_EIP as usize] as usize;
310 sp = context.uc_mcontext.gregs[libc::REG_ESP as usize] as usize;
311 } else if #[cfg(all(target_os = "freebsd", target_arch = "x86"))] {
312 pc = context.uc_mcontext.mc_eip as usize;
313 sp = context.uc_mcontext.mc_esp as usize;
314 } else if #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] {
315 pc = context.uc_mcontext.mc_rip as usize;
316 sp = context.uc_mcontext.mc_rsp as usize;
317 } else if #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))] {
318 pc = (*context.uc_mcontext).__ss.__rip as usize;
319 sp = (*context.uc_mcontext).__ss.__rsp as usize;
320 } else if #[cfg(all(
321 any(target_os = "linux", target_os = "android"),
322 target_arch = "aarch64",
323 ))] {
324 pc = context.uc_mcontext.pc as usize;
325 sp = context.uc_mcontext.sp as usize;
326 } else if #[cfg(all(
327 any(target_os = "linux", target_os = "android"),
328 target_arch = "arm",
329 ))] {
330 pc = context.uc_mcontext.arm_pc as usize;
331 sp = context.uc_mcontext.arm_sp as usize;
332 } else if #[cfg(all(
333 any(target_os = "linux", target_os = "android"),
334 any(target_arch = "riscv64", target_arch = "riscv32"),
335 ))] {
336 pc = context.uc_mcontext.__gregs[libc::REG_PC] as usize;
337 sp = context.uc_mcontext.__gregs[libc::REG_SP] as usize;
338 } else if #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] {
339 pc = (*context.uc_mcontext).__ss.__pc as usize;
340 sp = (*context.uc_mcontext).__ss.__sp as usize;
341 } else if #[cfg(all(target_os = "freebsd", target_arch = "aarch64"))] {
342 pc = context.uc_mcontext.mc_gpregs.gp_elr as usize;
343 sp = context.uc_mcontext.mc_gpregs.gp_sp as usize;
344 } else if #[cfg(all(target_os = "linux", target_arch = "loongarch64"))] {
345 pc = context.uc_mcontext.__gregs[1] as usize;
346 sp = context.uc_mcontext.__gregs[3] as usize;
347 } else {
348 compile_error!("Unsupported platform");
349 }
350 };
351 (pc, sp)
352 }
353
354 unsafe fn update_context(context: &mut ucontext_t, regs: TrapHandlerRegs) {
355 cfg_if::cfg_if! {
356 if #[cfg(all(
357 any(target_os = "linux", target_os = "android"),
358 target_arch = "x86_64",
359 ))] {
360 let TrapHandlerRegs { rip, rsp, rbp, rdi, rsi } = regs;
361 context.uc_mcontext.gregs[libc::REG_RIP as usize] = rip as i64;
362 context.uc_mcontext.gregs[libc::REG_RSP as usize] = rsp as i64;
363 context.uc_mcontext.gregs[libc::REG_RBP as usize] = rbp as i64;
364 context.uc_mcontext.gregs[libc::REG_RDI as usize] = rdi as i64;
365 context.uc_mcontext.gregs[libc::REG_RSI as usize] = rsi as i64;
366 } else if #[cfg(all(
367 any(target_os = "linux", target_os = "android"),
368 target_arch = "x86",
369 ))] {
370 let TrapHandlerRegs { eip, esp, ebp, ecx, edx } = regs;
371 context.uc_mcontext.gregs[libc::REG_EIP as usize] = eip as i32;
372 context.uc_mcontext.gregs[libc::REG_ESP as usize] = esp as i32;
373 context.uc_mcontext.gregs[libc::REG_EBP as usize] = ebp as i32;
374 context.uc_mcontext.gregs[libc::REG_ECX as usize] = ecx as i32;
375 context.uc_mcontext.gregs[libc::REG_EDX as usize] = edx as i32;
376 } else if #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))] {
377 let TrapHandlerRegs { rip, rsp, rbp, rdi, rsi } = regs;
378 (*context.uc_mcontext).__ss.__rip = rip;
379 (*context.uc_mcontext).__ss.__rsp = rsp;
380 (*context.uc_mcontext).__ss.__rbp = rbp;
381 (*context.uc_mcontext).__ss.__rdi = rdi;
382 (*context.uc_mcontext).__ss.__rsi = rsi;
383 } else if #[cfg(all(target_os = "freebsd", target_arch = "x86"))] {
384 let TrapHandlerRegs { eip, esp, ebp, ecx, edx } = regs;
385 context.uc_mcontext.mc_eip = eip as libc::register_t;
386 context.uc_mcontext.mc_esp = esp as libc::register_t;
387 context.uc_mcontext.mc_ebp = ebp as libc::register_t;
388 context.uc_mcontext.mc_ecx = ecx as libc::register_t;
389 context.uc_mcontext.mc_edx = edx as libc::register_t;
390 } else if #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] {
391 let TrapHandlerRegs { rip, rsp, rbp, rdi, rsi } = regs;
392 context.uc_mcontext.mc_rip = rip as libc::register_t;
393 context.uc_mcontext.mc_rsp = rsp as libc::register_t;
394 context.uc_mcontext.mc_rbp = rbp as libc::register_t;
395 context.uc_mcontext.mc_rdi = rdi as libc::register_t;
396 context.uc_mcontext.mc_rsi = rsi as libc::register_t;
397 } else if #[cfg(all(
398 any(target_os = "linux", target_os = "android"),
399 target_arch = "aarch64",
400 ))] {
401 let TrapHandlerRegs { pc, sp, x0, x1, x29, lr } = regs;
402 context.uc_mcontext.pc = pc;
403 context.uc_mcontext.sp = sp;
404 context.uc_mcontext.regs[0] = x0;
405 context.uc_mcontext.regs[1] = x1;
406 context.uc_mcontext.regs[29] = x29;
407 context.uc_mcontext.regs[30] = lr;
408 } else if #[cfg(all(
409 any(target_os = "linux", target_os = "android"),
410 target_arch = "arm",
411 ))] {
412 let TrapHandlerRegs {
413 pc,
414 r0,
415 r1,
416 r7,
417 r11,
418 r13,
419 r14,
420 cpsr_thumb,
421 cpsr_endian,
422 } = regs;
423 context.uc_mcontext.arm_pc = pc;
424 context.uc_mcontext.arm_r0 = r0;
425 context.uc_mcontext.arm_r1 = r1;
426 context.uc_mcontext.arm_r7 = r7;
427 context.uc_mcontext.arm_fp = r11;
428 context.uc_mcontext.arm_sp = r13;
429 context.uc_mcontext.arm_lr = r14;
430 if cpsr_thumb {
431 context.uc_mcontext.arm_cpsr |= 0x20;
432 } else {
433 context.uc_mcontext.arm_cpsr &= !0x20;
434 }
435 if cpsr_endian {
436 context.uc_mcontext.arm_cpsr |= 0x200;
437 } else {
438 context.uc_mcontext.arm_cpsr &= !0x200;
439 }
440 } else if #[cfg(all(
441 any(target_os = "linux", target_os = "android"),
442 any(target_arch = "riscv64", target_arch = "riscv32"),
443 ))] {
444 let TrapHandlerRegs { pc, ra, sp, a0, a1, s0 } = regs;
445 context.uc_mcontext.__gregs[libc::REG_PC] = pc as libc::c_ulong;
446 context.uc_mcontext.__gregs[libc::REG_RA] = ra as libc::c_ulong;
447 context.uc_mcontext.__gregs[libc::REG_SP] = sp as libc::c_ulong;
448 context.uc_mcontext.__gregs[libc::REG_A0] = a0 as libc::c_ulong;
449 context.uc_mcontext.__gregs[libc::REG_A0 + 1] = a1 as libc::c_ulong;
450 context.uc_mcontext.__gregs[libc::REG_S0] = s0 as libc::c_ulong;
451 } else if #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] {
452 let TrapHandlerRegs { pc, sp, x0, x1, x29, lr } = regs;
453 (*context.uc_mcontext).__ss.__pc = pc;
454 (*context.uc_mcontext).__ss.__sp = sp;
455 (*context.uc_mcontext).__ss.__x[0] = x0;
456 (*context.uc_mcontext).__ss.__x[1] = x1;
457 (*context.uc_mcontext).__ss.__fp = x29;
458 (*context.uc_mcontext).__ss.__lr = lr;
459 } else if #[cfg(all(target_os = "freebsd", target_arch = "aarch64"))] {
460 let TrapHandlerRegs { pc, sp, x0, x1, x29, lr } = regs;
461 context.uc_mcontext.mc_gpregs.gp_elr = pc as libc::register_t;
462 context.uc_mcontext.mc_gpregs.gp_sp = sp as libc::register_t;
463 context.uc_mcontext.mc_gpregs.gp_x[0] = x0 as libc::register_t;
464 context.uc_mcontext.mc_gpregs.gp_x[1] = x1 as libc::register_t;
465 context.uc_mcontext.mc_gpregs.gp_x[29] = x29 as libc::register_t;
466 context.uc_mcontext.mc_gpregs.gp_x[30] = lr as libc::register_t;
467 } else if #[cfg(all(target_os = "linux", target_arch = "loongarch64"))] {
468 let TrapHandlerRegs { pc, sp, a0, a1, fp, ra } = regs;
469 context.uc_mcontext.__pc = pc;
470 context.uc_mcontext.__gregs[1] = ra;
471 context.uc_mcontext.__gregs[3] = sp;
472 context.uc_mcontext.__gregs[4] = a0;
473 context.uc_mcontext.__gregs[5] = a1;
474 context.uc_mcontext.__gregs[22] = fp;
475 } else {
476 compile_error!("Unsupported platform");
477 }
478 };
479 }
480 } else if #[cfg(target_os = "windows")] {
481 use windows_sys::Win32::System::Diagnostics::Debug::{
482 AddVectoredExceptionHandler,
483 CONTEXT,
484 EXCEPTION_CONTINUE_EXECUTION,
485 EXCEPTION_CONTINUE_SEARCH,
486 EXCEPTION_POINTERS,
487 };
488 use windows_sys::Win32::Foundation::{
489 EXCEPTION_ACCESS_VIOLATION,
490 EXCEPTION_ILLEGAL_INSTRUCTION,
491 EXCEPTION_INT_DIVIDE_BY_ZERO,
492 EXCEPTION_INT_OVERFLOW,
493 EXCEPTION_STACK_OVERFLOW,
494 };
495
496 unsafe fn platform_init() {
497 if AddVectoredExceptionHandler(1, Some(exception_handler)).is_null() {
501 panic!("failed to add exception handler: {}", io::Error::last_os_error());
502 }
503 }
504
505 unsafe extern "system" fn exception_handler(
506 exception_info: *mut EXCEPTION_POINTERS
507 ) -> i32 {
508 let record = &*(*exception_info).ExceptionRecord;
512 if record.ExceptionCode != EXCEPTION_ACCESS_VIOLATION &&
513 record.ExceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION &&
514 record.ExceptionCode != EXCEPTION_STACK_OVERFLOW &&
515 record.ExceptionCode != EXCEPTION_INT_DIVIDE_BY_ZERO &&
516 record.ExceptionCode != EXCEPTION_INT_OVERFLOW
517 {
518 return EXCEPTION_CONTINUE_SEARCH;
519 }
520
521 let context = &mut *(*exception_info).ContextRecord;
534 let (pc, sp) = get_pc_sp(context);
535
536 let maybe_fault_address = match record.ExceptionCode {
538 EXCEPTION_ACCESS_VIOLATION => Some(record.ExceptionInformation[1]),
539 EXCEPTION_STACK_OVERFLOW => Some(sp),
540 _ => None,
541 };
542 let trap_code = match record.ExceptionCode {
543 EXCEPTION_ILLEGAL_INSTRUCTION => {
545 process_illegal_op(pc)
546 }
547 _ => None,
548 };
549 let handled = TrapHandlerContext::handle_trap(
552 pc,
553 sp,
554 maybe_fault_address,
555 trap_code,
556 |regs| update_context(context, regs),
557 |handler| handler(exception_info),
558 );
559
560 if handled {
561 EXCEPTION_CONTINUE_EXECUTION
562 } else {
563 EXCEPTION_CONTINUE_SEARCH
564 }
565 }
566
567 unsafe fn get_pc_sp(context: &CONTEXT) -> (usize, usize) {
568 let (pc, sp);
569 cfg_if::cfg_if! {
570 if #[cfg(target_arch = "x86_64")] {
571 pc = context.Rip as usize;
572 sp = context.Rsp as usize;
573 } else if #[cfg(target_arch = "x86")] {
574 pc = context.Rip as usize;
575 sp = context.Rsp as usize;
576 } else {
577 compile_error!("Unsupported platform");
578 }
579 };
580 (pc, sp)
581 }
582
583 unsafe fn update_context(context: &mut CONTEXT, regs: TrapHandlerRegs) {
584 cfg_if::cfg_if! {
585 if #[cfg(target_arch = "x86_64")] {
586 let TrapHandlerRegs { rip, rsp, rbp, rdi, rsi } = regs;
587 context.Rip = rip;
588 context.Rsp = rsp;
589 context.Rbp = rbp;
590 context.Rdi = rdi;
591 context.Rsi = rsi;
592 } else if #[cfg(target_arch = "x86")] {
593 let TrapHandlerRegs { eip, esp, ebp, ecx, edx } = regs;
594 context.Eip = eip;
595 context.Esp = esp;
596 context.Ebp = ebp;
597 context.Ecx = ecx;
598 context.Edx = edx;
599 } else {
600 compile_error!("Unsupported platform");
601 }
602 };
603 }
604 }
605}
606
607pub fn init_traps() {
616 static INIT: Once = Once::new();
617 INIT.call_once(|| unsafe {
618 platform_init();
619 });
620}
621
622pub unsafe fn raise_user_trap(data: Box<dyn Error + Send + Sync>) -> ! {
635 unwind_with(UnwindReason::UserTrap(data))
636}
637
638pub unsafe fn raise_lib_trap(trap: Trap) -> ! {
650 unwind_with(UnwindReason::LibTrap(trap))
651}
652
653pub unsafe fn resume_panic(payload: Box<dyn Any + Send>) -> ! {
662 unwind_with(UnwindReason::Panic(payload))
663}
664
665pub unsafe fn wasmer_call_trampoline(
681 trap_handler: Option<*const TrapHandlerFn<'static>>,
682 config: &VMConfig,
683 vmctx: VMFunctionContext,
684 trampoline: VMTrampoline,
685 callee: *const VMFunctionBody,
686 values_vec: *mut u8,
687) -> Result<(), Trap> {
688 catch_traps(trap_handler, config, move || {
689 mem::transmute::<
690 unsafe extern "C" fn(
691 *mut VMContext,
692 *const VMFunctionBody,
693 *mut wasmer_types::RawValue,
694 ),
695 extern "C" fn(VMFunctionContext, *const VMFunctionBody, *mut u8),
696 >(trampoline)(vmctx, callee, values_vec);
697 })
698}
699
700pub unsafe fn catch_traps<F, R: 'static>(
707 trap_handler: Option<*const TrapHandlerFn<'static>>,
708 config: &VMConfig,
709 closure: F,
710) -> Result<R, Trap>
711where
712 F: FnOnce() -> R + 'static,
713{
714 lazy_per_thread_init()?;
716 let stack_size = config
717 .wasm_stack_size
718 .unwrap_or_else(|| DEFAULT_STACK_SIZE.load(Ordering::Relaxed));
719 on_wasm_stack(stack_size, trap_handler, closure).map_err(UnwindReason::into_trap)
720}
721
722thread_local! {
731 static YIELDER: Cell<Option<NonNull<Yielder<(), UnwindReason>>>> = const { Cell::new(None) };
732 static TRAP_HANDLER: AtomicPtr<TrapHandlerContext> = const { AtomicPtr::new(ptr::null_mut()) };
733}
734
735#[allow(clippy::type_complexity)]
738struct TrapHandlerContext {
739 inner: *const u8,
740 handle_trap: fn(
741 *const u8,
742 usize,
743 usize,
744 Option<usize>,
745 Option<TrapCode>,
746 &mut dyn FnMut(TrapHandlerRegs),
747 ) -> bool,
748 custom_trap: Option<*const TrapHandlerFn<'static>>,
749}
750struct TrapHandlerContextInner<T> {
751 coro_trap_handler: CoroutineTrapHandler<Result<T, UnwindReason>>,
754}
755
756impl TrapHandlerContext {
757 fn install<T, R>(
760 custom_trap: Option<*const TrapHandlerFn<'static>>,
761 coro_trap_handler: CoroutineTrapHandler<Result<T, UnwindReason>>,
762 f: impl FnOnce() -> R,
763 ) -> R {
764 fn func<T>(
766 ptr: *const u8,
767 pc: usize,
768 sp: usize,
769 maybe_fault_address: Option<usize>,
770 trap_code: Option<TrapCode>,
771 update_regs: &mut dyn FnMut(TrapHandlerRegs),
772 ) -> bool {
773 unsafe {
774 (*(ptr as *const TrapHandlerContextInner<T>)).handle_trap(
775 pc,
776 sp,
777 maybe_fault_address,
778 trap_code,
779 update_regs,
780 )
781 }
782 }
783 let inner = TrapHandlerContextInner { coro_trap_handler };
784 let ctx = Self {
785 inner: &inner as *const _ as *const u8,
786 handle_trap: func::<T>,
787 custom_trap,
788 };
789
790 compiler_fence(Ordering::Release);
791 let prev = TRAP_HANDLER.with(|ptr| {
792 let prev = ptr.load(Ordering::Relaxed);
793 ptr.store(&ctx as *const Self as *mut Self, Ordering::Relaxed);
794 prev
795 });
796
797 defer! {
798 TRAP_HANDLER.with(|ptr| ptr.store(prev, Ordering::Relaxed));
799 compiler_fence(Ordering::Acquire);
800 }
801
802 f()
803 }
804
805 unsafe fn handle_trap(
807 pc: usize,
808 sp: usize,
809 maybe_fault_address: Option<usize>,
810 trap_code: Option<TrapCode>,
811 mut update_regs: impl FnMut(TrapHandlerRegs),
812 call_handler: impl Fn(&TrapHandlerFn<'static>) -> bool,
813 ) -> bool {
814 let ptr = TRAP_HANDLER.with(|ptr| ptr.load(Ordering::Relaxed));
815 if ptr.is_null() {
816 return false;
817 }
818
819 let ctx = &*ptr;
820
821 if let Some(trap_handler) = ctx.custom_trap {
823 if call_handler(&*trap_handler) {
824 return true;
825 }
826 }
827
828 (ctx.handle_trap)(
829 ctx.inner,
830 pc,
831 sp,
832 maybe_fault_address,
833 trap_code,
834 &mut update_regs,
835 )
836 }
837}
838
839impl<T> TrapHandlerContextInner<T> {
840 unsafe fn handle_trap(
841 &self,
842 pc: usize,
843 sp: usize,
844 maybe_fault_address: Option<usize>,
845 trap_code: Option<TrapCode>,
846 update_regs: &mut dyn FnMut(TrapHandlerRegs),
847 ) -> bool {
848 if !self.coro_trap_handler.stack_ptr_in_bounds(sp) {
851 return false;
852 }
853
854 let signal_trap = trap_code.or_else(|| {
855 maybe_fault_address.map(|addr| {
856 if self.coro_trap_handler.stack_ptr_in_bounds(addr) {
857 TrapCode::StackOverflow
858 } else {
859 TrapCode::HeapAccessOutOfBounds
860 }
861 })
862 });
863
864 let backtrace = if signal_trap == Some(TrapCode::StackOverflow) {
871 Backtrace::from(vec![])
872 } else {
873 Backtrace::new_unresolved()
874 };
875
876 let unwind = UnwindReason::WasmTrap {
879 backtrace,
880 signal_trap,
881 pc,
882 };
883 let regs = self
884 .coro_trap_handler
885 .setup_trap_handler(move || Err(unwind));
886 update_regs(regs);
887 true
888 }
889}
890
891enum UnwindReason {
892 Panic(Box<dyn Any + Send>),
894 UserTrap(Box<dyn Error + Send + Sync>),
896 LibTrap(Trap),
898 WasmTrap {
900 backtrace: Backtrace,
901 pc: usize,
902 signal_trap: Option<TrapCode>,
903 },
904}
905
906impl UnwindReason {
907 fn into_trap(self) -> Trap {
908 match self {
909 Self::UserTrap(data) => Trap::User(data),
910 Self::LibTrap(trap) => trap,
911 Self::WasmTrap {
912 backtrace,
913 pc,
914 signal_trap,
915 } => Trap::wasm(pc, backtrace, signal_trap),
916 Self::Panic(panic) => std::panic::resume_unwind(panic),
917 }
918 }
919}
920
921unsafe fn unwind_with(reason: UnwindReason) -> ! {
922 let yielder = YIELDER
923 .with(|cell| cell.replace(None))
924 .expect("not running on Wasm stack");
925
926 yielder.as_ref().suspend(reason);
927
928 unreachable!();
930}
931
932fn on_wasm_stack<F: FnOnce() -> T + 'static, T: 'static>(
936 stack_size: usize,
937 trap_handler: Option<*const TrapHandlerFn<'static>>,
938 f: F,
939) -> Result<T, UnwindReason> {
940 lazy_static::lazy_static! {
945 static ref STACK_POOL: crossbeam_queue::SegQueue<DefaultStack> = crossbeam_queue::SegQueue::new();
946 }
947 let stack = STACK_POOL
948 .pop()
949 .unwrap_or_else(|| DefaultStack::new(stack_size).unwrap());
950 let mut stack = scopeguard::guard(stack, |stack| STACK_POOL.push(stack));
951
952 let mut coro = Coroutine::with_stack(&mut *stack, move |yielder, ()| {
954 YIELDER.with(|cell| cell.set(Some(yielder.into())));
956
957 Ok(f())
958 });
959
960 defer! {
962 YIELDER.with(|cell| cell.set(None));
963 }
964
965 TrapHandlerContext::install(trap_handler, coro.trap_handler(), || {
968 match coro.resume(()) {
969 CoroutineResult::Yield(trap) => {
970 unsafe {
973 coro.force_reset();
974 }
975 Err(trap)
976 }
977 CoroutineResult::Return(result) => result,
978 }
979 })
980}
981
982pub fn on_host_stack<F: FnOnce() -> T, T>(f: F) -> T {
991 let yielder_ptr = YIELDER.with(|cell| cell.replace(None));
994
995 let yielder = match yielder_ptr {
998 Some(ptr) => unsafe { ptr.as_ref() },
999 None => return f(),
1000 };
1001
1002 defer! {
1004 YIELDER.with(|cell| cell.set(yielder_ptr));
1005 }
1006
1007 struct SendWrapper<T>(T);
1011 unsafe impl<T> Send for SendWrapper<T> {}
1012 let wrapped = SendWrapper(f);
1013 yielder.on_parent_stack(move || {
1014 let wrapped = wrapped;
1015 (wrapped.0)()
1016 })
1017}
1018
1019#[cfg(windows)]
1020pub fn lazy_per_thread_init() -> Result<(), Trap> {
1021 use windows_sys::Win32::System::Threading::SetThreadStackGuarantee;
1025 if unsafe { SetThreadStackGuarantee(&mut 0x10000) } == 0 {
1026 panic!("failed to set thread stack guarantee");
1027 }
1028
1029 Ok(())
1030}
1031
1032#[cfg(unix)]
1039pub fn lazy_per_thread_init() -> Result<(), Trap> {
1040 use std::ptr::null_mut;
1041
1042 thread_local! {
1043 static TLS: Tls = unsafe { init_sigstack() };
1046 }
1047
1048 const MIN_STACK_SIZE: usize = 16 * 4096;
1051
1052 enum Tls {
1053 OutOfMemory,
1054 Allocated {
1055 mmap_ptr: *mut libc::c_void,
1056 mmap_size: usize,
1057 },
1058 BigEnough,
1059 }
1060
1061 unsafe fn init_sigstack() -> Tls {
1062 let mut old_stack = mem::zeroed();
1065 let r = libc::sigaltstack(ptr::null(), &mut old_stack);
1066 assert_eq!(r, 0, "learning about sigaltstack failed");
1067 if old_stack.ss_flags & libc::SS_DISABLE == 0 && old_stack.ss_size >= MIN_STACK_SIZE {
1068 return Tls::BigEnough;
1069 }
1070
1071 let page_size: usize = region::page::size();
1074 let guard_size = page_size;
1075 let alloc_size = guard_size + MIN_STACK_SIZE;
1076
1077 let ptr = libc::mmap(
1078 null_mut(),
1079 alloc_size,
1080 libc::PROT_NONE,
1081 libc::MAP_PRIVATE | libc::MAP_ANON,
1082 -1,
1083 0,
1084 );
1085 if ptr == libc::MAP_FAILED {
1086 return Tls::OutOfMemory;
1087 }
1088
1089 let stack_ptr = (ptr as usize + guard_size) as *mut libc::c_void;
1092 let r = libc::mprotect(
1093 stack_ptr,
1094 MIN_STACK_SIZE,
1095 libc::PROT_READ | libc::PROT_WRITE,
1096 );
1097 assert_eq!(r, 0, "mprotect to configure memory for sigaltstack failed");
1098 let new_stack = libc::stack_t {
1099 ss_sp: stack_ptr,
1100 ss_flags: 0,
1101 ss_size: MIN_STACK_SIZE,
1102 };
1103 let r = libc::sigaltstack(&new_stack, ptr::null_mut());
1104 assert_eq!(r, 0, "registering new sigaltstack failed");
1105
1106 Tls::Allocated {
1107 mmap_ptr: ptr,
1108 mmap_size: alloc_size,
1109 }
1110 }
1111
1112 return TLS.with(|tls| {
1115 if let Tls::OutOfMemory = tls {
1116 Err(Trap::oom())
1117 } else {
1118 Ok(())
1119 }
1120 });
1121
1122 impl Drop for Tls {
1123 fn drop(&mut self) {
1124 let (ptr, size) = match self {
1125 Self::Allocated {
1126 mmap_ptr,
1127 mmap_size,
1128 } => (*mmap_ptr, *mmap_size),
1129 _ => return,
1130 };
1131 unsafe {
1132 let r = libc::munmap(ptr, size);
1134 debug_assert_eq!(r, 0, "munmap failed during thread shutdown");
1135 }
1136 }
1137 }
1138}