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::{CoroutineResult, ScopedCoroutine, 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::{AtomicPtr, AtomicUsize, Ordering, compiler_fence};
26use std::sync::{LazyLock, 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 unsafe {
96 let mut val: Option<u8> = None;
97 if cfg!(target_arch = "x86_64") {
98 val = if read(addr as *mut u8) & 0xf0 == 0x40
99 && read((addr + 1) as *mut u8) == 0x0f
100 && read((addr + 2) as *mut u8) == 0xb9
101 {
102 Some(read((addr + 3) as *mut u8))
103 } else if read(addr as *mut u8) == 0x0f && read((addr + 1) as *mut u8) == 0xb9 {
104 Some(read((addr + 2) as *mut u8))
105 } else {
106 None
107 }
108 }
109 if cfg!(target_arch = "aarch64") {
110 val = if read_unaligned(addr as *mut u32) & 0xffff0000 == 0 {
111 Some(read(addr as *mut u8))
112 } else {
113 None
114 }
115 }
116 match val.and_then(|val| {
117 if val & MAGIC == MAGIC {
118 Some(val & 0xf)
119 } else {
120 None
121 }
122 }) {
123 None => None,
124 Some(val) => match val {
125 0 => Some(TrapCode::StackOverflow),
126 1 => Some(TrapCode::HeapAccessOutOfBounds),
127 2 => Some(TrapCode::HeapMisaligned),
128 3 => Some(TrapCode::TableAccessOutOfBounds),
129 4 => Some(TrapCode::IndirectCallToNull),
130 5 => Some(TrapCode::BadSignature),
131 6 => Some(TrapCode::IntegerOverflow),
132 7 => Some(TrapCode::IntegerDivisionByZero),
133 8 => Some(TrapCode::BadConversionToInteger),
134 9 => Some(TrapCode::UnreachableCodeReached),
135 10 => Some(TrapCode::UnalignedAtomic),
136 _ => None,
137 },
138 }
139 }
140}
141
142cfg_if::cfg_if! {
143 if #[cfg(unix)] {
144 static mut PREV_SIGSEGV: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
145 static mut PREV_SIGBUS: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
146 static mut PREV_SIGILL: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
147 static mut PREV_SIGFPE: MaybeUninit<libc::sigaction> = MaybeUninit::uninit();
148
149 unsafe fn platform_init() { unsafe {
150 let register = |slot: &mut MaybeUninit<libc::sigaction>, signal: i32| {
151 let mut handler: libc::sigaction = mem::zeroed();
152 handler.sa_flags = libc::SA_SIGINFO | libc::SA_NODEFER | libc::SA_ONSTACK;
166 handler.sa_sigaction = trap_handler as *const () as usize;
167 libc::sigemptyset(&mut handler.sa_mask);
168 if libc::sigaction(signal, &handler, slot.as_mut_ptr()) != 0 {
169 panic!(
170 "unable to install signal handler: {}",
171 io::Error::last_os_error(),
172 );
173 }
174 };
175
176 register(&mut PREV_SIGSEGV, libc::SIGSEGV);
178
179 register(&mut PREV_SIGILL, libc::SIGILL);
181
182 if cfg!(target_arch = "x86") || cfg!(target_arch = "x86_64") {
184 register(&mut PREV_SIGFPE, libc::SIGFPE);
185 }
186
187 if cfg!(target_arch = "arm") || cfg!(target_vendor = "apple") {
190 register(&mut PREV_SIGBUS, libc::SIGBUS);
191 }
192
193 #[cfg(target_vendor = "apple")]
196 {
197 use mach2::exception_types::*;
198 use mach2::kern_return::*;
199 use mach2::port::*;
200 use mach2::thread_status::*;
201 use mach2::traps::*;
202 use mach2::mach_types::*;
203
204 unsafe extern "C" {
205 fn task_set_exception_ports(
206 task: task_t,
207 exception_mask: exception_mask_t,
208 new_port: mach_port_t,
209 behavior: exception_behavior_t,
210 new_flavor: thread_state_flavor_t,
211 ) -> kern_return_t;
212 }
213
214 #[allow(non_snake_case)]
215 #[cfg(target_arch = "x86_64")]
216 let MACHINE_THREAD_STATE = x86_THREAD_STATE64;
217 #[allow(non_snake_case)]
218 #[cfg(target_arch = "aarch64")]
219 let MACHINE_THREAD_STATE = 6;
220
221 task_set_exception_ports(
222 mach_task_self(),
223 EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC | EXC_MASK_BAD_INSTRUCTION,
224 MACH_PORT_NULL,
225 EXCEPTION_STATE_IDENTITY as exception_behavior_t,
226 MACHINE_THREAD_STATE,
227 );
228 }
229 }}
230
231 unsafe extern "C" fn trap_handler(
232 signum: libc::c_int,
233 siginfo: *mut libc::siginfo_t,
234 context: *mut libc::c_void,
235 ) { unsafe {
236 let previous = match signum {
237 libc::SIGSEGV => &PREV_SIGSEGV,
238 libc::SIGBUS => &PREV_SIGBUS,
239 libc::SIGFPE => &PREV_SIGFPE,
240 libc::SIGILL => &PREV_SIGILL,
241 _ => panic!("unknown signal: {signum}"),
242 };
243 let maybe_fault_address = match signum {
245 libc::SIGSEGV | libc::SIGBUS => {
246 Some((*siginfo).si_addr() as usize)
247 }
248 _ => None,
249 };
250 let trap_code = match signum {
251 libc::SIGILL => {
253 let addr = (*siginfo).si_addr() as usize;
254 process_illegal_op(addr)
255 }
256 _ => None,
257 };
258 let ucontext = &mut *(context as *mut ucontext_t);
259 let (pc, sp) = get_pc_sp(ucontext);
260 let handled = TrapHandlerContext::handle_trap(
261 pc,
262 sp,
263 maybe_fault_address,
264 trap_code,
265 |regs| update_context(ucontext, regs),
266 |handler| handler(signum, siginfo, context),
267 );
268
269 if handled {
270 return;
271 }
272
273 let previous = &*previous.as_ptr();
283 if previous.sa_flags & libc::SA_SIGINFO != 0 {
284 mem::transmute::<
285 usize,
286 extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void),
287 >(previous.sa_sigaction)(signum, siginfo, context)
288 } else if previous.sa_sigaction == libc::SIG_DFL
289 {
290 libc::sigaction(signum, previous, ptr::null_mut());
291 } else if previous.sa_sigaction != libc::SIG_IGN {
292 mem::transmute::<usize, extern "C" fn(libc::c_int)>(
293 previous.sa_sigaction
294 )(signum)
295 }
296 }}
297
298 unsafe fn get_pc_sp(context: &ucontext_t) -> (usize, usize) {
299 let (pc, sp);
300 cfg_if::cfg_if! {
301 if #[cfg(all(
302 any(target_os = "linux", target_os = "android"),
303 target_arch = "x86_64",
304 ))] {
305 pc = context.uc_mcontext.gregs[libc::REG_RIP as usize] as usize;
306 sp = context.uc_mcontext.gregs[libc::REG_RSP as usize] as usize;
307 } else if #[cfg(all(
308 any(target_os = "linux", target_os = "android"),
309 target_arch = "x86",
310 ))] {
311 pc = context.uc_mcontext.gregs[libc::REG_EIP as usize] as usize;
312 sp = context.uc_mcontext.gregs[libc::REG_ESP as usize] as usize;
313 } else if #[cfg(all(target_os = "freebsd", target_arch = "x86"))] {
314 pc = context.uc_mcontext.mc_eip as usize;
315 sp = context.uc_mcontext.mc_esp as usize;
316 } else if #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] {
317 pc = context.uc_mcontext.mc_rip as usize;
318 sp = context.uc_mcontext.mc_rsp as usize;
319 } else if #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))] {
320 let mcontext = unsafe { &*context.uc_mcontext };
321 pc = mcontext.__ss.__rip as usize;
322 sp = mcontext.__ss.__rsp as usize;
323 } else if #[cfg(all(
324 any(target_os = "linux", target_os = "android"),
325 target_arch = "aarch64",
326 ))] {
327 pc = context.uc_mcontext.pc as usize;
328 sp = context.uc_mcontext.sp as usize;
329 } else if #[cfg(all(
330 any(target_os = "linux", target_os = "android"),
331 target_arch = "arm",
332 ))] {
333 pc = context.uc_mcontext.arm_pc as usize;
334 sp = context.uc_mcontext.arm_sp as usize;
335 } else if #[cfg(all(
336 any(target_os = "linux", target_os = "android"),
337 any(target_arch = "riscv64", target_arch = "riscv32"),
338 ))] {
339 pc = context.uc_mcontext.__gregs[libc::REG_PC] as usize;
340 sp = context.uc_mcontext.__gregs[libc::REG_SP] as usize;
341 } else if #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] {
342 let mcontext = unsafe { &*context.uc_mcontext };
343 pc = mcontext.__ss.__pc as usize;
344 sp = mcontext.__ss.__sp as usize;
345 } else if #[cfg(all(target_os = "freebsd", target_arch = "aarch64"))] {
346 pc = context.uc_mcontext.mc_gpregs.gp_elr as usize;
347 sp = context.uc_mcontext.mc_gpregs.gp_sp as usize;
348 } else if #[cfg(all(target_os = "linux", target_arch = "loongarch64"))] {
349 pc = context.uc_mcontext.__gregs[1] as usize;
350 sp = context.uc_mcontext.__gregs[3] as usize;
351 } else if #[cfg(all(target_os = "linux", target_arch = "powerpc64"))] {
352 pc = (*context.uc_mcontext.regs).nip as usize;
353 sp = (*context.uc_mcontext.regs).gpr[1] as usize;
354 } else {
355 compile_error!("Unsupported platform");
356 }
357 };
358 (pc, sp)
359 }
360
361 unsafe fn update_context(context: &mut ucontext_t, regs: TrapHandlerRegs) {
362 cfg_if::cfg_if! {
363 if #[cfg(all(
364 any(target_os = "linux", target_os = "android"),
365 target_arch = "x86_64",
366 ))] {
367 let TrapHandlerRegs { rip, rsp, rbp, rdi, rsi } = regs;
368 context.uc_mcontext.gregs[libc::REG_RIP as usize] = rip as i64;
369 context.uc_mcontext.gregs[libc::REG_RSP as usize] = rsp as i64;
370 context.uc_mcontext.gregs[libc::REG_RBP as usize] = rbp as i64;
371 context.uc_mcontext.gregs[libc::REG_RDI as usize] = rdi as i64;
372 context.uc_mcontext.gregs[libc::REG_RSI as usize] = rsi as i64;
373 } else if #[cfg(all(
374 any(target_os = "linux", target_os = "android"),
375 target_arch = "x86",
376 ))] {
377 let TrapHandlerRegs { eip, esp, ebp, ecx, edx } = regs;
378 context.uc_mcontext.gregs[libc::REG_EIP as usize] = eip as i32;
379 context.uc_mcontext.gregs[libc::REG_ESP as usize] = esp as i32;
380 context.uc_mcontext.gregs[libc::REG_EBP as usize] = ebp as i32;
381 context.uc_mcontext.gregs[libc::REG_ECX as usize] = ecx as i32;
382 context.uc_mcontext.gregs[libc::REG_EDX as usize] = edx as i32;
383 } else if #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))] {
384 let TrapHandlerRegs { rip, rsp, rbp, rdi, rsi } = regs;
385 let mcontext = unsafe { &mut *context.uc_mcontext };
386 mcontext.__ss.__rip = rip;
387 mcontext.__ss.__rsp = rsp;
388 mcontext.__ss.__rbp = rbp;
389 mcontext.__ss.__rdi = rdi;
390 mcontext.__ss.__rsi = rsi;
391 } else if #[cfg(all(target_os = "freebsd", target_arch = "x86"))] {
392 let TrapHandlerRegs { eip, esp, ebp, ecx, edx } = regs;
393 context.uc_mcontext.mc_eip = eip as libc::register_t;
394 context.uc_mcontext.mc_esp = esp as libc::register_t;
395 context.uc_mcontext.mc_ebp = ebp as libc::register_t;
396 context.uc_mcontext.mc_ecx = ecx as libc::register_t;
397 context.uc_mcontext.mc_edx = edx as libc::register_t;
398 } else if #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] {
399 let TrapHandlerRegs { rip, rsp, rbp, rdi, rsi } = regs;
400 context.uc_mcontext.mc_rip = rip as libc::register_t;
401 context.uc_mcontext.mc_rsp = rsp as libc::register_t;
402 context.uc_mcontext.mc_rbp = rbp as libc::register_t;
403 context.uc_mcontext.mc_rdi = rdi as libc::register_t;
404 context.uc_mcontext.mc_rsi = rsi as libc::register_t;
405 } else if #[cfg(all(
406 any(target_os = "linux", target_os = "android"),
407 target_arch = "aarch64",
408 ))] {
409 let TrapHandlerRegs { pc, sp, x0, x1, x29, lr } = regs;
410 context.uc_mcontext.pc = pc;
411 context.uc_mcontext.sp = sp;
412 context.uc_mcontext.regs[0] = x0;
413 context.uc_mcontext.regs[1] = x1;
414 context.uc_mcontext.regs[29] = x29;
415 context.uc_mcontext.regs[30] = lr;
416 } else if #[cfg(all(
417 any(target_os = "linux", target_os = "android"),
418 target_arch = "arm",
419 ))] {
420 let TrapHandlerRegs {
421 pc,
422 r0,
423 r1,
424 r7,
425 r11,
426 r13,
427 r14,
428 cpsr_thumb,
429 cpsr_endian,
430 } = regs;
431 context.uc_mcontext.arm_pc = pc;
432 context.uc_mcontext.arm_r0 = r0;
433 context.uc_mcontext.arm_r1 = r1;
434 context.uc_mcontext.arm_r7 = r7;
435 context.uc_mcontext.arm_fp = r11;
436 context.uc_mcontext.arm_sp = r13;
437 context.uc_mcontext.arm_lr = r14;
438 if cpsr_thumb {
439 context.uc_mcontext.arm_cpsr |= 0x20;
440 } else {
441 context.uc_mcontext.arm_cpsr &= !0x20;
442 }
443 if cpsr_endian {
444 context.uc_mcontext.arm_cpsr |= 0x200;
445 } else {
446 context.uc_mcontext.arm_cpsr &= !0x200;
447 }
448 } else if #[cfg(all(
449 any(target_os = "linux", target_os = "android"),
450 any(target_arch = "riscv64", target_arch = "riscv32"),
451 ))] {
452 let TrapHandlerRegs { pc, ra, sp, a0, a1, s0 } = regs;
453 context.uc_mcontext.__gregs[libc::REG_PC] = pc as libc::c_ulong;
454 context.uc_mcontext.__gregs[libc::REG_RA] = ra as libc::c_ulong;
455 context.uc_mcontext.__gregs[libc::REG_SP] = sp as libc::c_ulong;
456 context.uc_mcontext.__gregs[libc::REG_A0] = a0 as libc::c_ulong;
457 context.uc_mcontext.__gregs[libc::REG_A0 + 1] = a1 as libc::c_ulong;
458 context.uc_mcontext.__gregs[libc::REG_S0] = s0 as libc::c_ulong;
459 } else if #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] {
460 let TrapHandlerRegs { pc, sp, x0, x1, x29, lr } = regs;
461 let mcontext = unsafe { &mut *context.uc_mcontext };
462 mcontext.__ss.__pc = pc;
463 mcontext.__ss.__sp = sp;
464 mcontext.__ss.__x[0] = x0;
465 mcontext.__ss.__x[1] = x1;
466 mcontext.__ss.__fp = x29;
467 mcontext.__ss.__lr = lr;
468 } else if #[cfg(all(target_os = "freebsd", target_arch = "aarch64"))] {
469 let TrapHandlerRegs { pc, sp, x0, x1, x29, lr } = regs;
470 context.uc_mcontext.mc_gpregs.gp_elr = pc as libc::register_t;
471 context.uc_mcontext.mc_gpregs.gp_sp = sp as libc::register_t;
472 context.uc_mcontext.mc_gpregs.gp_x[0] = x0 as libc::register_t;
473 context.uc_mcontext.mc_gpregs.gp_x[1] = x1 as libc::register_t;
474 context.uc_mcontext.mc_gpregs.gp_x[29] = x29 as libc::register_t;
475 context.uc_mcontext.mc_gpregs.gp_x[30] = lr as libc::register_t;
476 } else if #[cfg(all(target_os = "linux", target_arch = "loongarch64"))] {
477 let TrapHandlerRegs { pc, sp, a0, a1, fp, ra } = regs;
478 context.uc_mcontext.__pc = pc;
479 context.uc_mcontext.__gregs[1] = ra;
480 context.uc_mcontext.__gregs[3] = sp;
481 context.uc_mcontext.__gregs[4] = a0;
482 context.uc_mcontext.__gregs[5] = a1;
483 context.uc_mcontext.__gregs[22] = fp;
484 } else if #[cfg(all(target_os = "linux", target_arch = "powerpc64"))] {
485 let TrapHandlerRegs { pc, sp, r3, r4, r31, lr } = regs;
486 (*context.uc_mcontext.regs).nip = pc;
487 (*context.uc_mcontext.regs).gpr[1] = sp;
488 (*context.uc_mcontext.regs).gpr[3] = r3;
489 (*context.uc_mcontext.regs).gpr[4] = r4;
490 (*context.uc_mcontext.regs).gpr[31] = r31;
491 (*context.uc_mcontext.regs).link = lr;
492 } else {
493 compile_error!("Unsupported platform");
494 }
495 };
496 }
497 } else if #[cfg(target_os = "windows")] {
498 use windows_sys::Win32::System::Diagnostics::Debug::{
499 AddVectoredExceptionHandler,
500 CONTEXT,
501 EXCEPTION_CONTINUE_EXECUTION,
502 EXCEPTION_CONTINUE_SEARCH,
503 EXCEPTION_POINTERS,
504 };
505 use windows_sys::Win32::Foundation::{
506 EXCEPTION_ACCESS_VIOLATION,
507 EXCEPTION_ILLEGAL_INSTRUCTION,
508 EXCEPTION_INT_DIVIDE_BY_ZERO,
509 EXCEPTION_INT_OVERFLOW,
510 EXCEPTION_STACK_OVERFLOW,
511 };
512
513 unsafe fn platform_init() {
514 unsafe {
515 let handler = AddVectoredExceptionHandler(1, Some(exception_handler));
519 if handler.is_null() {
520 panic!("failed to add exception handler: {}", io::Error::last_os_error());
521 }
522 }
523 }
524
525 unsafe extern "system" fn exception_handler(
526 exception_info: *mut EXCEPTION_POINTERS
527 ) -> i32 {
528 unsafe {
529 let record = &*(*exception_info).ExceptionRecord;
533 if record.ExceptionCode != EXCEPTION_ACCESS_VIOLATION &&
534 record.ExceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION &&
535 record.ExceptionCode != EXCEPTION_STACK_OVERFLOW &&
536 record.ExceptionCode != EXCEPTION_INT_DIVIDE_BY_ZERO &&
537 record.ExceptionCode != EXCEPTION_INT_OVERFLOW
538 {
539 return EXCEPTION_CONTINUE_SEARCH;
540 }
541
542 let context = &mut *(*exception_info).ContextRecord;
555 let (pc, sp) = get_pc_sp(context);
556
557 let maybe_fault_address = match record.ExceptionCode {
559 EXCEPTION_ACCESS_VIOLATION => Some(record.ExceptionInformation[1]),
560 EXCEPTION_STACK_OVERFLOW => Some(sp),
561 _ => None,
562 };
563 let trap_code = match record.ExceptionCode {
564 EXCEPTION_ILLEGAL_INSTRUCTION => {
566 process_illegal_op(pc)
567 }
568 _ => None,
569 };
570 let handled = TrapHandlerContext::handle_trap(
573 pc,
574 sp,
575 maybe_fault_address,
576 trap_code,
577 |regs| update_context(context, regs),
578 |handler| handler(exception_info),
579 );
580
581 if handled {
582 EXCEPTION_CONTINUE_EXECUTION
583 } else {
584 EXCEPTION_CONTINUE_SEARCH
585 }
586 }
587 }
588
589 unsafe fn get_pc_sp(context: &CONTEXT) -> (usize, usize) {
590 let (pc, sp);
591 cfg_if::cfg_if! {
592 if #[cfg(target_arch = "x86_64")] {
593 pc = context.Rip as usize;
594 sp = context.Rsp as usize;
595 } else if #[cfg(target_arch = "x86")] {
596 pc = context.Rip as usize;
597 sp = context.Rsp as usize;
598 } else {
599 compile_error!("Unsupported platform");
600 }
601 };
602 (pc, sp)
603 }
604
605 unsafe fn update_context(context: &mut CONTEXT, regs: TrapHandlerRegs) {
606 cfg_if::cfg_if! {
607 if #[cfg(target_arch = "x86_64")] {
608 let TrapHandlerRegs { rip, rsp, rbp, rdi, rsi } = regs;
609 context.Rip = rip;
610 context.Rsp = rsp;
611 context.Rbp = rbp;
612 context.Rdi = rdi;
613 context.Rsi = rsi;
614 } else if #[cfg(target_arch = "x86")] {
615 let TrapHandlerRegs { eip, esp, ebp, ecx, edx } = regs;
616 context.Eip = eip;
617 context.Esp = esp;
618 context.Ebp = ebp;
619 context.Ecx = ecx;
620 context.Edx = edx;
621 } else {
622 compile_error!("Unsupported platform");
623 }
624 };
625 }
626 }
627}
628
629pub fn init_traps() {
638 static INIT: Once = Once::new();
639 INIT.call_once(|| unsafe {
640 platform_init();
641 });
642}
643
644pub unsafe fn raise_user_trap(data: Box<dyn Error + Send + Sync>) -> ! {
657 unsafe { unwind_with(UnwindReason::UserTrap(data)) }
658}
659
660pub unsafe fn raise_lib_trap(trap: Trap) -> ! {
672 unsafe { unwind_with(UnwindReason::LibTrap(trap)) }
673}
674
675pub unsafe fn resume_panic(payload: Box<dyn Any + Send>) -> ! {
684 unsafe { unwind_with(UnwindReason::Panic(payload)) }
685}
686
687pub unsafe fn wasmer_call_trampoline(
703 trap_handler: Option<*const TrapHandlerFn<'static>>,
704 config: &VMConfig,
705 vmctx: VMFunctionContext,
706 trampoline: VMTrampoline,
707 callee: *const VMFunctionBody,
708 values_vec: *mut u8,
709) -> Result<(), Trap> {
710 unsafe {
711 catch_traps(trap_handler, config, move || {
712 mem::transmute::<
713 unsafe extern "C" fn(
714 *mut VMContext,
715 *const VMFunctionBody,
716 *mut wasmer_types::RawValue,
717 ),
718 extern "C" fn(VMFunctionContext, *const VMFunctionBody, *mut u8),
719 >(trampoline)(vmctx, callee, values_vec);
720 })
721 }
722}
723
724pub unsafe fn catch_traps<F, R: 'static>(
731 trap_handler: Option<*const TrapHandlerFn<'static>>,
732 config: &VMConfig,
733 closure: F,
734) -> Result<R, Trap>
735where
736 F: FnOnce() -> R + 'static,
737{
738 lazy_per_thread_init()?;
740 let stack_size = config
741 .wasm_stack_size
742 .unwrap_or_else(|| DEFAULT_STACK_SIZE.load(Ordering::Relaxed));
743 on_wasm_stack(stack_size, trap_handler, closure).map_err(UnwindReason::into_trap)
744}
745
746thread_local! {
755 static YIELDER: Cell<Option<NonNull<Yielder<(), UnwindReason>>>> = const { Cell::new(None) };
756 static TRAP_HANDLER: AtomicPtr<TrapHandlerContext> = const { AtomicPtr::new(ptr::null_mut()) };
757}
758
759#[allow(clippy::type_complexity)]
762struct TrapHandlerContext {
763 inner: *const u8,
764 handle_trap: fn(
765 *const u8,
766 usize,
767 usize,
768 Option<usize>,
769 Option<TrapCode>,
770 &mut dyn FnMut(TrapHandlerRegs),
771 ) -> bool,
772 custom_trap: Option<*const TrapHandlerFn<'static>>,
773}
774struct TrapHandlerContextInner<T> {
775 coro_trap_handler: CoroutineTrapHandler<Result<T, UnwindReason>>,
778}
779
780impl TrapHandlerContext {
781 fn install<T, R>(
784 custom_trap: Option<*const TrapHandlerFn<'static>>,
785 coro_trap_handler: CoroutineTrapHandler<Result<T, UnwindReason>>,
786 f: impl FnOnce() -> R,
787 ) -> R {
788 fn func<T>(
790 ptr: *const u8,
791 pc: usize,
792 sp: usize,
793 maybe_fault_address: Option<usize>,
794 trap_code: Option<TrapCode>,
795 update_regs: &mut dyn FnMut(TrapHandlerRegs),
796 ) -> bool {
797 unsafe {
798 (*(ptr as *const TrapHandlerContextInner<T>)).handle_trap(
799 pc,
800 sp,
801 maybe_fault_address,
802 trap_code,
803 update_regs,
804 )
805 }
806 }
807 let inner = TrapHandlerContextInner { coro_trap_handler };
808 let ctx = Self {
809 inner: &inner as *const _ as *const u8,
810 handle_trap: func::<T>,
811 custom_trap,
812 };
813
814 compiler_fence(Ordering::Release);
815 let prev = TRAP_HANDLER.with(|ptr| {
816 let prev = ptr.load(Ordering::Relaxed);
817 ptr.store(&ctx as *const Self as *mut Self, Ordering::Relaxed);
818 prev
819 });
820
821 defer! {
822 TRAP_HANDLER.with(|ptr| ptr.store(prev, Ordering::Relaxed));
823 compiler_fence(Ordering::Acquire);
824 }
825
826 f()
827 }
828
829 unsafe fn handle_trap(
831 pc: usize,
832 sp: usize,
833 maybe_fault_address: Option<usize>,
834 trap_code: Option<TrapCode>,
835 mut update_regs: impl FnMut(TrapHandlerRegs),
836 call_handler: impl Fn(&TrapHandlerFn<'static>) -> bool,
837 ) -> bool {
838 unsafe {
839 let ptr = TRAP_HANDLER.with(|ptr| ptr.load(Ordering::Relaxed));
840 if ptr.is_null() {
841 return false;
842 }
843
844 let ctx = &*ptr;
845
846 if let Some(trap_handler) = ctx.custom_trap
848 && call_handler(&*trap_handler)
849 {
850 return true;
851 }
852
853 (ctx.handle_trap)(
854 ctx.inner,
855 pc,
856 sp,
857 maybe_fault_address,
858 trap_code,
859 &mut update_regs,
860 )
861 }
862 }
863}
864
865impl<T> TrapHandlerContextInner<T> {
866 unsafe fn handle_trap(
867 &self,
868 pc: usize,
869 sp: usize,
870 maybe_fault_address: Option<usize>,
871 trap_code: Option<TrapCode>,
872 update_regs: &mut dyn FnMut(TrapHandlerRegs),
873 ) -> bool {
874 unsafe {
875 if !self.coro_trap_handler.stack_ptr_in_bounds(sp) {
878 return false;
879 }
880
881 let signal_trap = trap_code.or_else(|| {
882 maybe_fault_address.map(|addr| {
883 if self.coro_trap_handler.stack_ptr_in_bounds(addr) {
884 TrapCode::StackOverflow
885 } else {
886 TrapCode::HeapAccessOutOfBounds
887 }
888 })
889 });
890
891 let backtrace = if signal_trap == Some(TrapCode::StackOverflow) {
898 Backtrace::from(vec![])
899 } else {
900 Backtrace::new_unresolved()
901 };
902
903 let unwind = UnwindReason::WasmTrap {
906 backtrace,
907 signal_trap,
908 pc,
909 };
910 let regs = self
911 .coro_trap_handler
912 .setup_trap_handler(move || Err(unwind));
913 update_regs(regs);
914 true
915 }
916 }
917}
918
919enum UnwindReason {
920 Panic(Box<dyn Any + Send>),
922 UserTrap(Box<dyn Error + Send + Sync>),
924 LibTrap(Trap),
926 WasmTrap {
928 backtrace: Backtrace,
929 pc: usize,
930 signal_trap: Option<TrapCode>,
931 },
932}
933
934impl UnwindReason {
935 fn into_trap(self) -> Trap {
936 match self {
937 Self::UserTrap(data) => Trap::User(data),
938 Self::LibTrap(trap) => trap,
939 Self::WasmTrap {
940 backtrace,
941 pc,
942 signal_trap,
943 } => Trap::wasm(pc, backtrace, signal_trap),
944 Self::Panic(panic) => std::panic::resume_unwind(panic),
945 }
946 }
947}
948
949unsafe fn unwind_with(reason: UnwindReason) -> ! {
950 unsafe {
951 let yielder = YIELDER
952 .with(|cell| cell.replace(None))
953 .expect("not running on Wasm stack");
954
955 yielder.as_ref().suspend(reason);
956
957 unreachable!();
959 }
960}
961
962fn on_wasm_stack<F: FnOnce() -> T + 'static, T: 'static>(
966 stack_size: usize,
967 trap_handler: Option<*const TrapHandlerFn<'static>>,
968 f: F,
969) -> Result<T, UnwindReason> {
970 static STACK_POOL: LazyLock<crossbeam_queue::SegQueue<DefaultStack>> =
975 LazyLock::new(crossbeam_queue::SegQueue::new);
976
977 let stack = STACK_POOL
978 .pop()
979 .unwrap_or_else(|| DefaultStack::new(stack_size).unwrap());
980 let mut stack = scopeguard::guard(stack, |stack| STACK_POOL.push(stack));
981
982 let coro = ScopedCoroutine::with_stack(&mut *stack, move |yielder, ()| {
984 YIELDER.with(|cell| cell.set(Some(yielder.into())));
986
987 Ok(f())
988 });
989
990 defer! {
992 YIELDER.with(|cell| cell.set(None));
993 }
994
995 coro.scope(|mut coro_ref| {
996 TrapHandlerContext::install(trap_handler, coro_ref.trap_handler(), || {
999 match coro_ref.resume(()) {
1000 CoroutineResult::Yield(trap) => {
1001 unsafe {
1004 coro_ref.force_reset();
1005 }
1006 Err(trap)
1007 }
1008 CoroutineResult::Return(result) => result,
1009 }
1010 })
1011 })
1012}
1013
1014pub fn on_host_stack<F: FnOnce() -> T, T>(f: F) -> T {
1023 let yielder_ptr = YIELDER.with(|cell| cell.replace(None));
1026
1027 let yielder = match yielder_ptr {
1030 Some(ptr) => unsafe { ptr.as_ref() },
1031 None => return f(),
1032 };
1033
1034 defer! {
1036 YIELDER.with(|cell| cell.set(yielder_ptr));
1037 }
1038
1039 struct SendWrapper<T>(T);
1043 unsafe impl<T> Send for SendWrapper<T> {}
1044 let wrapped = SendWrapper(f);
1045 yielder.on_parent_stack(move || {
1046 let wrapped = wrapped;
1047 (wrapped.0)()
1048 })
1049}
1050
1051#[cfg(windows)]
1052pub fn lazy_per_thread_init() -> Result<(), Trap> {
1053 use windows_sys::Win32::System::Threading::SetThreadStackGuarantee;
1057 if unsafe { SetThreadStackGuarantee(&mut 0x10000) } == 0 {
1058 panic!("failed to set thread stack guarantee");
1059 }
1060
1061 Ok(())
1062}
1063
1064#[cfg(unix)]
1071pub fn lazy_per_thread_init() -> Result<(), Trap> {
1072 use std::ptr::null_mut;
1073
1074 thread_local! {
1075 static TLS: Tls = unsafe { init_sigstack() };
1078 }
1079
1080 const MIN_STACK_SIZE: usize = 16 * 4096;
1083
1084 enum Tls {
1085 OutOfMemory,
1086 Allocated {
1087 mmap_ptr: *mut libc::c_void,
1088 mmap_size: usize,
1089 },
1090 BigEnough,
1091 }
1092
1093 unsafe fn init_sigstack() -> Tls {
1094 unsafe {
1095 let mut old_stack = mem::zeroed();
1098 let r = libc::sigaltstack(ptr::null(), &mut old_stack);
1099 assert_eq!(r, 0, "learning about sigaltstack failed");
1100 if old_stack.ss_flags & libc::SS_DISABLE == 0 && old_stack.ss_size >= MIN_STACK_SIZE {
1101 return Tls::BigEnough;
1102 }
1103
1104 let page_size: usize = region::page::size();
1107 let guard_size = page_size;
1108 let alloc_size = guard_size + MIN_STACK_SIZE;
1109
1110 let ptr = libc::mmap(
1111 null_mut(),
1112 alloc_size,
1113 libc::PROT_NONE,
1114 libc::MAP_PRIVATE | libc::MAP_ANON,
1115 -1,
1116 0,
1117 );
1118 if ptr == libc::MAP_FAILED {
1119 return Tls::OutOfMemory;
1120 }
1121
1122 let stack_ptr = (ptr as usize + guard_size) as *mut libc::c_void;
1125 let r = libc::mprotect(
1126 stack_ptr,
1127 MIN_STACK_SIZE,
1128 libc::PROT_READ | libc::PROT_WRITE,
1129 );
1130 assert_eq!(r, 0, "mprotect to configure memory for sigaltstack failed");
1131 let new_stack = libc::stack_t {
1132 ss_sp: stack_ptr,
1133 ss_flags: 0,
1134 ss_size: MIN_STACK_SIZE,
1135 };
1136 let r = libc::sigaltstack(&new_stack, ptr::null_mut());
1137 assert_eq!(r, 0, "registering new sigaltstack failed");
1138
1139 Tls::Allocated {
1140 mmap_ptr: ptr,
1141 mmap_size: alloc_size,
1142 }
1143 }
1144 }
1145
1146 return TLS.with(|tls| {
1149 if let Tls::OutOfMemory = tls {
1150 Err(Trap::oom())
1151 } else {
1152 Ok(())
1153 }
1154 });
1155
1156 impl Drop for Tls {
1157 fn drop(&mut self) {
1158 let (ptr, size) = match self {
1159 Self::Allocated {
1160 mmap_ptr,
1161 mmap_size,
1162 } => (*mmap_ptr, *mmap_size),
1163 _ => return,
1164 };
1165 unsafe {
1166 let r = libc::munmap(ptr, size);
1168 debug_assert_eq!(r, 0, "munmap failed during thread shutdown");
1169 }
1170 }
1171 }
1172}