wasmtime/runtime/vm/sys/unix/
signals.rs1use crate::prelude::*;
4use crate::runtime::vm::traphandlers::{TrapRegisters, TrapTest, tls};
5use std::cell::RefCell;
6use std::io;
7use std::mem;
8use std::ptr::{self, null_mut};
9use wasmtime_unwinder::Handler;
10
11pub type SignalHandler =
13 Box<dyn Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool + Send + Sync>;
14
15const UNINIT_SIGACTION: libc::sigaction = unsafe { mem::zeroed() };
16static mut PREV_SIGSEGV: libc::sigaction = UNINIT_SIGACTION;
17static mut PREV_SIGBUS: libc::sigaction = UNINIT_SIGACTION;
18static mut PREV_SIGILL: libc::sigaction = UNINIT_SIGACTION;
19static mut PREV_SIGFPE: libc::sigaction = UNINIT_SIGACTION;
20
21pub struct TrapHandler;
22
23impl TrapHandler {
24 pub unsafe fn new(macos_use_mach_ports: bool) -> TrapHandler {
32 assert!(!macos_use_mach_ports || !cfg!(target_vendor = "apple"));
35
36 foreach_handler(|slot, signal| {
37 let mut handler: libc::sigaction = unsafe { mem::zeroed() };
38 handler.sa_flags = libc::SA_SIGINFO | libc::SA_NODEFER | libc::SA_ONSTACK;
52 handler.sa_sigaction = (trap_handler as *const ()).addr();
53 unsafe {
54 libc::sigemptyset(&mut handler.sa_mask);
55 if libc::sigaction(signal, &handler, slot) != 0 {
56 panic!(
57 "unable to install signal handler: {}",
58 io::Error::last_os_error(),
59 );
60 }
61 }
62 });
63
64 TrapHandler
65 }
66
67 pub fn validate_config(&self, macos_use_mach_ports: bool) {
68 assert!(!macos_use_mach_ports || !cfg!(target_vendor = "apple"));
69 }
70}
71
72fn foreach_handler(mut f: impl FnMut(*mut libc::sigaction, i32)) {
73 f(&raw mut PREV_SIGSEGV, libc::SIGSEGV);
75
76 f(&raw mut PREV_SIGILL, libc::SIGILL);
78
79 if cfg!(target_arch = "x86_64") || cfg!(target_arch = "s390x") {
81 f(&raw mut PREV_SIGFPE, libc::SIGFPE);
82 }
83
84 if cfg!(target_vendor = "apple") || cfg!(target_os = "freebsd") {
87 f(&raw mut PREV_SIGBUS, libc::SIGBUS);
88 }
89
90 }
93
94impl Drop for TrapHandler {
95 fn drop(&mut self) {
96 unsafe {
97 foreach_handler(|slot, signal| {
98 let mut prev: libc::sigaction = mem::zeroed();
99
100 if libc::sigaction(signal, slot, &mut prev) != 0 {
102 eprintln!(
103 "unable to reinstall signal handler: {}",
104 io::Error::last_os_error(),
105 );
106 libc::abort();
107 }
108
109 if prev.sa_sigaction != (trap_handler as *const ()).addr() {
115 eprintln!(
116 "
117Wasmtime's signal handler was not the last signal handler to be installed
118in the process so it's not certain how to unload signal handlers. In this
119situation the Engine::unload_process_handlers API is not applicable and requires
120perhaps initializing libraries in a different order. The process will be aborted
121now.
122"
123 );
124 libc::abort();
125 }
126 });
127 }
128 }
129}
130
131unsafe extern "C" fn trap_handler(
132 signum: libc::c_int,
133 siginfo: *mut libc::siginfo_t,
134 context: *mut libc::c_void,
135) {
136 let previous = match signum {
137 libc::SIGSEGV => &raw const PREV_SIGSEGV,
138 libc::SIGBUS => &raw const PREV_SIGBUS,
139 libc::SIGFPE => &raw const PREV_SIGFPE,
140 libc::SIGILL => &raw const PREV_SIGILL,
141 _ => panic!("unknown signal: {signum}"),
142 };
143 let handled = tls::with(|info| {
144 let info = match info {
147 Some(info) => info,
148 None => return false,
149 };
150
151 let faulting_addr = match signum {
159 libc::SIGSEGV | libc::SIGBUS => unsafe { Some((*siginfo).si_addr() as usize) },
160 _ => None,
161 };
162 let regs = unsafe { get_trap_registers(context, signum) };
163 let test = info.test_if_trap(regs, faulting_addr, |handler| {
164 handler(signum, siginfo, context)
165 });
166
167 match test {
172 TrapTest::NotWasm => {
173 if let Some(faulting_addr) = faulting_addr {
174 let range = unsafe { &info.vm_store_context.get().as_ref().async_guard_range };
175 if range.start.addr() <= faulting_addr && faulting_addr < range.end.addr() {
176 abort_stack_overflow();
177 }
178 }
179 false
180 }
181 TrapTest::HandledByEmbedder => true,
182 TrapTest::Trap(handler) => {
183 unsafe {
184 store_handler_in_ucontext(context, &handler);
185 }
186 true
187 }
188 }
189 });
190
191 if handled {
192 return;
193 }
194
195 unsafe { delegate_signal_to_previous_handler(previous, signum, siginfo, context) }
196}
197
198pub unsafe fn delegate_signal_to_previous_handler(
199 previous: *const libc::sigaction,
200 signum: libc::c_int,
201 siginfo: *mut libc::siginfo_t,
202 context: *mut libc::c_void,
203) {
204 unsafe {
214 let previous = *previous;
215 if previous.sa_flags & libc::SA_SIGINFO != 0 {
216 mem::transmute::<
217 usize,
218 extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void),
219 >(previous.sa_sigaction)(signum, siginfo, context)
220 } else if previous.sa_sigaction == libc::SIG_DFL || previous.sa_sigaction == libc::SIG_IGN {
221 libc::sigaction(signum, &previous as *const _, ptr::null_mut());
222 } else {
223 mem::transmute::<usize, extern "C" fn(libc::c_int)>(previous.sa_sigaction)(signum)
224 }
225 }
226}
227
228pub fn abort_stack_overflow() -> ! {
229 unsafe {
230 let msg = "execution on async fiber has overflowed its stack";
231 libc::write(libc::STDERR_FILENO, msg.as_ptr().cast(), msg.len());
232 libc::abort();
233 }
234}
235
236#[allow(
237 clippy::cast_possible_truncation,
238 reason = "too fiddly to handle and wouldn't help much anyway"
239)]
240unsafe fn get_trap_registers(cx: *mut libc::c_void, _signum: libc::c_int) -> TrapRegisters {
241 cfg_select! {
242 all(any(target_os = "linux", target_os = "android", target_os = "illumos"), target_arch = "x86_64") => {
243 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
244 TrapRegisters {
245 pc: cx.uc_mcontext.gregs[libc::REG_RIP as usize] as usize,
246 fp: cx.uc_mcontext.gregs[libc::REG_RBP as usize] as usize,
247 }
248 }
249 all(target_os = "linux", target_arch = "x86") => {
250 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
251 TrapRegisters {
252 pc: cx.uc_mcontext.gregs[libc::REG_EIP as usize] as usize,
253 fp: cx.uc_mcontext.gregs[libc::REG_EBP as usize] as usize,
254 }
255 }
256 all(any(target_os = "linux", target_os = "android"), target_arch = "aarch64") => {
257 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
258 TrapRegisters {
259 pc: cx.uc_mcontext.pc as usize,
260 fp: cx.uc_mcontext.regs[29] as usize,
261 }
262 }
263 all(target_os = "linux", target_arch = "s390x") => {
264 let trap_offset = match _signum {
275 libc::SIGILL | libc::SIGFPE => 1,
276 _ => 0,
277 };
278 unsafe {
279 let cx = &*(cx as *const libc::ucontext_t);
280 TrapRegisters {
281 pc: (cx.uc_mcontext.psw.addr - trap_offset) as usize,
282 fp: *(cx.uc_mcontext.gregs[15] as *const usize),
283 }
284 }
285 }
286 all(target_vendor = "apple", target_arch = "x86_64") => {
287 unsafe {
288 let cx = &*(cx as *const libc::ucontext_t);
289 TrapRegisters {
290 pc: (*cx.uc_mcontext).__ss.__rip as usize,
291 fp: (*cx.uc_mcontext).__ss.__rbp as usize,
292 }
293 }
294 }
295 all(target_vendor = "apple", target_arch = "aarch64") => {
296 unsafe {
297 let cx = &*(cx as *const libc::ucontext_t);
298 TrapRegisters {
299 pc: (*cx.uc_mcontext).__ss.__pc as usize,
300 fp: (*cx.uc_mcontext).__ss.__fp as usize,
301 }
302 }
303 }
304 all(target_os = "freebsd", target_arch = "x86_64") => {
305 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
306 TrapRegisters {
307 pc: cx.uc_mcontext.mc_rip as usize,
308 fp: cx.uc_mcontext.mc_rbp as usize,
309 }
310 }
311 all(target_os = "linux", target_arch = "riscv64") => {
312 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
313 TrapRegisters {
314 pc: cx.uc_mcontext.__gregs[libc::REG_PC] as usize,
315 fp: cx.uc_mcontext.__gregs[libc::REG_S0] as usize,
316 }
317 }
318 all(target_os = "freebsd", target_arch = "aarch64") => {
319 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
320 TrapRegisters {
321 pc: cx.uc_mcontext.mc_gpregs.gp_elr as usize,
322 fp: cx.uc_mcontext.mc_gpregs.gp_x[29] as usize,
323 }
324 }
325 all(target_os = "openbsd", target_arch = "x86_64") => {
326 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
327 TrapRegisters {
328 pc: cx.sc_rip as usize,
329 fp: cx.sc_rbp as usize,
330 }
331 }
332 all(target_os = "linux", target_arch = "arm") => {
333 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
334 TrapRegisters {
335 pc: cx.uc_mcontext.arm_pc as usize,
336 fp: cx.uc_mcontext.arm_fp as usize,
337 }
338 }
339 _ => {
340 compile_error!("unsupported platform");
341 panic!();
342 }
343 }
344}
345
346unsafe fn store_handler_in_ucontext(cx: *mut libc::c_void, handler: &Handler) {
349 cfg_select! {
350 all(any(target_os = "linux", target_os = "android", target_os = "illumos"), target_arch = "x86_64") => {
351 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
352 cx.uc_mcontext.gregs[libc::REG_RIP as usize] = handler.pc as _;
353 cx.uc_mcontext.gregs[libc::REG_RSP as usize] = handler.sp as _;
354 cx.uc_mcontext.gregs[libc::REG_RBP as usize] = handler.fp as _;
355 cx.uc_mcontext.gregs[libc::REG_RAX as usize] = 0;
356 cx.uc_mcontext.gregs[libc::REG_RDX as usize] = 0;
357 }
358 all(any(target_os = "linux", target_os = "android"), target_arch = "aarch64") => {
359 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
360 cx.uc_mcontext.pc = handler.pc as _;
361 cx.uc_mcontext.sp = handler.sp as _;
362 cx.uc_mcontext.regs[29] = handler.fp as _;
363 cx.uc_mcontext.regs[0] = 0;
364 cx.uc_mcontext.regs[1] = 0;
365 }
366 all(target_os = "linux", target_arch = "s390x") => {
367 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
368 cx.uc_mcontext.psw.addr = handler.pc as _;
369 cx.uc_mcontext.gregs[15] = handler.sp as _;
370 cx.uc_mcontext.gregs[6] = 0;
371 cx.uc_mcontext.gregs[7] = 0;
372 }
373 all(target_vendor = "apple", target_arch = "x86_64") => {
374 unsafe {
375 let cx = cx.cast::<libc::ucontext_t>().as_mut().unwrap();
376 let cx = cx.uc_mcontext.as_mut().unwrap();
377 cx.__ss.__rip = handler.pc as _;
378 cx.__ss.__rsp = handler.sp as _;
379 cx.__ss.__rbp = handler.fp as _;
380 cx.__ss.__rax = 0;
381 cx.__ss.__rdx = 0;
382 }
383 }
384 all(target_vendor = "apple", target_arch = "aarch64") => {
385 unsafe {
386 let cx = cx.cast::<libc::ucontext_t>().as_mut().unwrap();
387 let cx = cx.uc_mcontext.as_mut().unwrap();
388 cx.__ss.__pc = handler.pc as _;
389 cx.__ss.__sp = handler.sp as _;
390 cx.__ss.__fp = handler.fp as _;
391 cx.__ss.__x[0] = 0;
392 cx.__ss.__x[1] = 0;
393 }
394 }
395 all(target_os = "freebsd", target_arch = "x86_64") => {
396 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
397 cx.uc_mcontext.mc_rip = handler.pc as _;
398 cx.uc_mcontext.mc_rbp = handler.fp as _;
399 cx.uc_mcontext.mc_rsp = handler.sp as _;
400 cx.uc_mcontext.mc_rax = 0;
401 cx.uc_mcontext.mc_rdx = 0;
402 }
403 all(target_os = "freebsd", target_arch = "aarch64") => {
404 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
405 cx.uc_mcontext.mc_gpregs.gp_elr = handler.pc as _;
406 cx.uc_mcontext.mc_gpregs.gp_sp = handler.sp as _;
407 cx.uc_mcontext.mc_gpregs.gp_x[29] = handler.fp as _;
408 cx.uc_mcontext.mc_gpregs.gp_x[0] = 0;
409 cx.uc_mcontext.mc_gpregs.gp_x[1] = 0;
410 }
411 all(target_os = "openbsd", target_arch = "x86_64") => {
412 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
413 cx.sc_rip = handler.pc as _;
414 cx.sc_rbp = handler.fp as _;
415 cx.sc_rsp = handler.sp as _;
416 cx.sc_rax = 0;
417 cx.sc_rdx = 0;
418 }
419 all(target_os = "linux", target_arch = "riscv64") => {
420 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
421 cx.uc_mcontext.__gregs[libc::REG_PC] = handler.pc as _;
422 cx.uc_mcontext.__gregs[libc::REG_S0] = handler.fp as _;
423 cx.uc_mcontext.__gregs[libc::REG_SP] = handler.sp as _;
424 cx.uc_mcontext.__gregs[libc::REG_A0] = 0;
425 cx.uc_mcontext.__gregs[libc::REG_A0 + 1] = 0;
426 }
427 _ => {
428 compile_error!("unsupported platform");
429 panic!();
430 }
431 }
432}
433
434#[cold]
459pub fn lazy_per_thread_init() {
460 if cfg!(asan) {
492 return;
493 }
494
495 std::thread_local! {
499 static STACK: RefCell<Option<Stack>> = const { RefCell::new(None) };
500 }
501
502 const MIN_STACK_SIZE: usize = 64 * 4096;
512
513 struct Stack {
514 mmap_ptr: *mut libc::c_void,
515 mmap_size: usize,
516 }
517
518 return STACK.with(|s| {
519 *s.borrow_mut() = unsafe { allocate_sigaltstack() };
520 });
521
522 unsafe fn allocate_sigaltstack() -> Option<Stack> {
523 let mut old_stack = unsafe { mem::zeroed() };
526 let r = unsafe { libc::sigaltstack(ptr::null(), &mut old_stack) };
527 assert_eq!(
528 r,
529 0,
530 "learning about sigaltstack failed: {}",
531 io::Error::last_os_error()
532 );
533 if old_stack.ss_flags & libc::SS_DISABLE == 0 && old_stack.ss_size >= MIN_STACK_SIZE {
534 return None;
535 }
536
537 let page_size = crate::runtime::vm::host_page_size();
540 let guard_size = page_size;
541 let alloc_size = guard_size + MIN_STACK_SIZE;
542
543 let ptr = unsafe {
544 rustix::mm::mmap_anonymous(
545 null_mut(),
546 alloc_size,
547 rustix::mm::ProtFlags::empty(),
548 rustix::mm::MapFlags::PRIVATE,
549 )
550 .expect("failed to allocate memory for sigaltstack")
551 };
552
553 let stack_ptr = (ptr as usize + guard_size) as *mut std::ffi::c_void;
556 unsafe {
557 rustix::mm::mprotect(
558 stack_ptr,
559 MIN_STACK_SIZE,
560 rustix::mm::MprotectFlags::READ | rustix::mm::MprotectFlags::WRITE,
561 )
562 .expect("mprotect to configure memory for sigaltstack failed");
563 }
564 let new_stack = libc::stack_t {
565 ss_sp: stack_ptr,
566 ss_flags: 0,
567 ss_size: MIN_STACK_SIZE,
568 };
569 let r = unsafe { libc::sigaltstack(&new_stack, ptr::null_mut()) };
570 assert_eq!(
571 r,
572 0,
573 "registering new sigaltstack failed: {}",
574 io::Error::last_os_error()
575 );
576
577 Some(Stack {
578 mmap_ptr: ptr,
579 mmap_size: alloc_size,
580 })
581 }
582
583 impl Drop for Stack {
584 fn drop(&mut self) {
585 unsafe {
586 let new_stack = libc::stack_t {
594 ss_sp: ptr::null_mut(),
595 ss_flags: libc::SS_DISABLE,
596 ss_size: MIN_STACK_SIZE,
597 };
598 let r = libc::sigaltstack(&new_stack, ptr::null_mut());
599 debug_assert_eq!(
600 r,
601 0,
602 "registering new sigaltstack failed: {}",
603 io::Error::last_os_error()
604 );
605
606 let r = rustix::mm::munmap(self.mmap_ptr, self.mmap_size);
609 debug_assert!(r.is_ok(), "munmap failed during thread shutdown");
610 }
611 }
612 }
613}