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 lr: None,
248 }
249 }
250 all(target_os = "linux", target_arch = "x86") => {
251 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
252 TrapRegisters {
253 pc: cx.uc_mcontext.gregs[libc::REG_EIP as usize] as usize,
254 fp: cx.uc_mcontext.gregs[libc::REG_EBP as usize] as usize,
255 lr: None,
256 }
257 }
258 all(any(target_os = "linux", target_os = "android"), target_arch = "aarch64") => {
259 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
260 TrapRegisters {
261 pc: cx.uc_mcontext.pc as usize,
262 fp: cx.uc_mcontext.regs[29] as usize,
263 lr: None,
264 }
265 }
266 all(target_os = "linux", target_arch = "s390x") => {
267 let trap_offset = match _signum {
278 libc::SIGILL | libc::SIGFPE => 1,
279 _ => 0,
280 };
281 unsafe {
282 let cx = &*(cx as *const libc::ucontext_t);
283 TrapRegisters {
284 pc: (cx.uc_mcontext.psw.addr - trap_offset) as usize,
285 fp: *(cx.uc_mcontext.gregs[15] as *const usize),
286 lr: Some(cx.uc_mcontext.gregs[14] as usize),
289 }
290 }
291 }
292 all(target_vendor = "apple", target_arch = "x86_64") => {
293 unsafe {
294 let cx = &*(cx as *const libc::ucontext_t);
295 TrapRegisters {
296 pc: (*cx.uc_mcontext).__ss.__rip as usize,
297 fp: (*cx.uc_mcontext).__ss.__rbp as usize,
298 lr: None,
299 }
300 }
301 }
302 all(target_vendor = "apple", target_arch = "aarch64") => {
303 unsafe {
304 let cx = &*(cx as *const libc::ucontext_t);
305 TrapRegisters {
306 pc: (*cx.uc_mcontext).__ss.__pc as usize,
307 fp: (*cx.uc_mcontext).__ss.__fp as usize,
308 lr: None,
309 }
310 }
311 }
312 all(target_os = "freebsd", target_arch = "x86_64") => {
313 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
314 TrapRegisters {
315 pc: cx.uc_mcontext.mc_rip as usize,
316 fp: cx.uc_mcontext.mc_rbp as usize,
317 lr: None,
318 }
319 }
320 all(target_os = "linux", target_arch = "riscv64") => {
321 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
322 TrapRegisters {
323 pc: cx.uc_mcontext.__gregs[libc::REG_PC] as usize,
324 fp: cx.uc_mcontext.__gregs[libc::REG_S0] as usize,
325 lr: None,
326 }
327 }
328 all(target_os = "freebsd", target_arch = "aarch64") => {
329 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
330 TrapRegisters {
331 pc: cx.uc_mcontext.mc_gpregs.gp_elr as usize,
332 fp: cx.uc_mcontext.mc_gpregs.gp_x[29] as usize,
333 lr: None,
334 }
335 }
336 all(target_os = "openbsd", target_arch = "x86_64") => {
337 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
338 TrapRegisters {
339 pc: cx.sc_rip as usize,
340 fp: cx.sc_rbp as usize,
341 lr: None,
342 }
343 }
344 all(target_os = "linux", target_arch = "arm") => {
345 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
346 TrapRegisters {
347 pc: cx.uc_mcontext.arm_pc as usize,
348 fp: cx.uc_mcontext.arm_fp as usize,
349 lr: None,
350 }
351 }
352 all(target_os = "redox", target_arch = "x86_64") => {
353 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
354 TrapRegisters {
355 pc: cx.uc_mcontext.rip as usize,
356 fp: cx.uc_mcontext.rbp as usize,
357 lr: None,
358 }
359 }
360 _ => {
361 compile_error!("unsupported platform");
362 panic!();
363 }
364 }
365}
366
367unsafe fn store_handler_in_ucontext(cx: *mut libc::c_void, handler: &Handler) {
370 cfg_select! {
371 all(any(target_os = "linux", target_os = "android", target_os = "illumos"), target_arch = "x86_64") => {
372 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
373 cx.uc_mcontext.gregs[libc::REG_RIP as usize] = handler.pc as _;
374 cx.uc_mcontext.gregs[libc::REG_RSP as usize] = handler.sp as _;
375 cx.uc_mcontext.gregs[libc::REG_RBP as usize] = handler.fp as _;
376 cx.uc_mcontext.gregs[libc::REG_RAX as usize] = 0;
377 cx.uc_mcontext.gregs[libc::REG_RDX as usize] = 0;
378 }
379 all(any(target_os = "linux", target_os = "android"), target_arch = "aarch64") => {
380 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
381 cx.uc_mcontext.pc = handler.pc as _;
382 cx.uc_mcontext.sp = handler.sp as _;
383 cx.uc_mcontext.regs[29] = handler.fp as _;
384 cx.uc_mcontext.regs[0] = 0;
385 cx.uc_mcontext.regs[1] = 0;
386 }
387 all(target_os = "linux", target_arch = "s390x") => {
388 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
389 cx.uc_mcontext.psw.addr = handler.pc as _;
390 cx.uc_mcontext.gregs[15] = handler.sp as _;
391 cx.uc_mcontext.gregs[6] = 0;
392 cx.uc_mcontext.gregs[7] = 0;
393 }
394 all(target_vendor = "apple", target_arch = "x86_64") => {
395 unsafe {
396 let cx = cx.cast::<libc::ucontext_t>().as_mut().unwrap();
397 let cx = cx.uc_mcontext.as_mut().unwrap();
398 cx.__ss.__rip = handler.pc as _;
399 cx.__ss.__rsp = handler.sp as _;
400 cx.__ss.__rbp = handler.fp as _;
401 cx.__ss.__rax = 0;
402 cx.__ss.__rdx = 0;
403 }
404 }
405 all(target_vendor = "apple", target_arch = "aarch64") => {
406 unsafe {
407 let cx = cx.cast::<libc::ucontext_t>().as_mut().unwrap();
408 let cx = cx.uc_mcontext.as_mut().unwrap();
409 cx.__ss.__pc = handler.pc as _;
410 cx.__ss.__sp = handler.sp as _;
411 cx.__ss.__fp = handler.fp as _;
412 cx.__ss.__x[0] = 0;
413 cx.__ss.__x[1] = 0;
414 }
415 }
416 all(target_os = "freebsd", target_arch = "x86_64") => {
417 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
418 cx.uc_mcontext.mc_rip = handler.pc as _;
419 cx.uc_mcontext.mc_rbp = handler.fp as _;
420 cx.uc_mcontext.mc_rsp = handler.sp as _;
421 cx.uc_mcontext.mc_rax = 0;
422 cx.uc_mcontext.mc_rdx = 0;
423 }
424 all(target_os = "freebsd", target_arch = "aarch64") => {
425 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
426 cx.uc_mcontext.mc_gpregs.gp_elr = handler.pc as _;
427 cx.uc_mcontext.mc_gpregs.gp_sp = handler.sp as _;
428 cx.uc_mcontext.mc_gpregs.gp_x[29] = handler.fp as _;
429 cx.uc_mcontext.mc_gpregs.gp_x[0] = 0;
430 cx.uc_mcontext.mc_gpregs.gp_x[1] = 0;
431 }
432 all(target_os = "openbsd", target_arch = "x86_64") => {
433 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
434 cx.sc_rip = handler.pc as _;
435 cx.sc_rbp = handler.fp as _;
436 cx.sc_rsp = handler.sp as _;
437 cx.sc_rax = 0;
438 cx.sc_rdx = 0;
439 }
440 all(target_os = "linux", target_arch = "riscv64") => {
441 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
442 cx.uc_mcontext.__gregs[libc::REG_PC] = handler.pc as _;
443 cx.uc_mcontext.__gregs[libc::REG_S0] = handler.fp as _;
444 cx.uc_mcontext.__gregs[libc::REG_SP] = handler.sp as _;
445 cx.uc_mcontext.__gregs[libc::REG_A0] = 0;
446 cx.uc_mcontext.__gregs[libc::REG_A0 + 1] = 0;
447 }
448 all(target_os = "redox", target_arch = "x86_64") => {
449 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
450 cx.uc_mcontext.rip = handler.pc as _;
451 cx.uc_mcontext.rbp = handler.fp as _;
452 cx.uc_mcontext.rsp = handler.sp as _;
453 cx.uc_mcontext.rax = 0;
454 cx.uc_mcontext.rdx = 0;
455 }
456 _ => {
457 compile_error!("unsupported platform");
458 panic!();
459 }
460 }
461}
462
463#[cold]
488pub fn lazy_per_thread_init() {
489 if cfg!(asan) {
521 return;
522 }
523
524 std::thread_local! {
528 static STACK: RefCell<Option<Stack>> = const { RefCell::new(None) };
529 }
530
531 const MIN_STACK_SIZE: usize = 64 * 4096;
541
542 struct Stack {
543 mmap_ptr: *mut libc::c_void,
544 mmap_size: usize,
545 }
546
547 return STACK.with(|s| {
548 *s.borrow_mut() = unsafe { allocate_sigaltstack() };
549 });
550
551 unsafe fn allocate_sigaltstack() -> Option<Stack> {
552 let mut old_stack = unsafe { mem::zeroed() };
555 let r = unsafe { libc::sigaltstack(ptr::null(), &mut old_stack) };
556 assert_eq!(
557 r,
558 0,
559 "learning about sigaltstack failed: {}",
560 io::Error::last_os_error()
561 );
562 if old_stack.ss_flags & libc::SS_DISABLE == 0 && old_stack.ss_size >= MIN_STACK_SIZE {
563 return None;
564 }
565
566 let page_size = crate::runtime::vm::host_page_size();
569 let guard_size = page_size;
570 let alloc_size = guard_size + MIN_STACK_SIZE;
571
572 let ptr = unsafe {
573 rustix::mm::mmap_anonymous(
574 null_mut(),
575 alloc_size,
576 rustix::mm::ProtFlags::empty(),
577 rustix::mm::MapFlags::PRIVATE,
578 )
579 .expect("failed to allocate memory for sigaltstack")
580 };
581
582 let stack_ptr = (ptr as usize + guard_size) as *mut std::ffi::c_void;
585 unsafe {
586 rustix::mm::mprotect(
587 stack_ptr,
588 MIN_STACK_SIZE,
589 rustix::mm::MprotectFlags::READ | rustix::mm::MprotectFlags::WRITE,
590 )
591 .expect("mprotect to configure memory for sigaltstack failed");
592 }
593 let new_stack = libc::stack_t {
594 ss_sp: stack_ptr,
595 ss_flags: 0,
596 ss_size: MIN_STACK_SIZE,
597 };
598 let r = unsafe { libc::sigaltstack(&new_stack, ptr::null_mut()) };
599 assert_eq!(
600 r,
601 0,
602 "registering new sigaltstack failed: {}",
603 io::Error::last_os_error()
604 );
605
606 Some(Stack {
607 mmap_ptr: ptr,
608 mmap_size: alloc_size,
609 })
610 }
611
612 impl Drop for Stack {
613 fn drop(&mut self) {
614 unsafe {
615 let new_stack = libc::stack_t {
623 ss_sp: ptr::null_mut(),
624 ss_flags: libc::SS_DISABLE,
625 ss_size: MIN_STACK_SIZE,
626 };
627 let r = libc::sigaltstack(&new_stack, ptr::null_mut());
628 debug_assert_eq!(
629 r,
630 0,
631 "registering new sigaltstack failed: {}",
632 io::Error::last_os_error()
633 );
634
635 let r = rustix::mm::munmap(self.mmap_ptr, self.mmap_size);
638 debug_assert!(r.is_ok(), "munmap failed during thread shutdown");
639 }
640 }
641 }
642}