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 usize;
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 usize {
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.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_if::cfg_if! {
242 if #[cfg(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 } else if #[cfg(all(target_os = "linux", target_arch = "x86"))] {
249 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
250 TrapRegisters {
251 pc: cx.uc_mcontext.gregs[libc::REG_EIP as usize] as usize,
252 fp: cx.uc_mcontext.gregs[libc::REG_EBP as usize] as usize,
253 }
254 } else if #[cfg(all(any(target_os = "linux", target_os = "android"), target_arch = "aarch64"))] {
255 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
256 TrapRegisters {
257 pc: cx.uc_mcontext.pc as usize,
258 fp: cx.uc_mcontext.regs[29] as usize,
259 }
260 } else if #[cfg(all(target_os = "linux", target_arch = "s390x"))] {
261 let trap_offset = match _signum {
272 libc::SIGILL | libc::SIGFPE => 1,
273 _ => 0,
274 };
275 unsafe {
276 let cx = &*(cx as *const libc::ucontext_t);
277 TrapRegisters {
278 pc: (cx.uc_mcontext.psw.addr - trap_offset) as usize,
279 fp: *(cx.uc_mcontext.gregs[15] as *const usize),
280 }
281 }
282 } else if #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))] {
283 unsafe {
284 let cx = &*(cx as *const libc::ucontext_t);
285 TrapRegisters {
286 pc: (*cx.uc_mcontext).__ss.__rip as usize,
287 fp: (*cx.uc_mcontext).__ss.__rbp as usize,
288 }
289 }
290 } else if #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] {
291 unsafe {
292 let cx = &*(cx as *const libc::ucontext_t);
293 TrapRegisters {
294 pc: (*cx.uc_mcontext).__ss.__pc as usize,
295 fp: (*cx.uc_mcontext).__ss.__fp as usize,
296 }
297 }
298 } else if #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] {
299 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
300 TrapRegisters {
301 pc: cx.uc_mcontext.mc_rip as usize,
302 fp: cx.uc_mcontext.mc_rbp as usize,
303 }
304 } else if #[cfg(all(target_os = "linux", target_arch = "riscv64"))] {
305 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
306 TrapRegisters {
307 pc: cx.uc_mcontext.__gregs[libc::REG_PC] as usize,
308 fp: cx.uc_mcontext.__gregs[libc::REG_S0] as usize,
309 }
310 } else if #[cfg(all(target_os = "freebsd", target_arch = "aarch64"))] {
311 let cx = unsafe { &*(cx as *const libc::mcontext_t) };
312 TrapRegisters {
313 pc: cx.mc_gpregs.gp_elr as usize,
314 fp: cx.mc_gpregs.gp_x[29] as usize,
315 }
316 } else if #[cfg(all(target_os = "openbsd", target_arch = "x86_64"))] {
317 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
318 TrapRegisters {
319 pc: cx.sc_rip as usize,
320 fp: cx.sc_rbp as usize,
321 }
322 } else if #[cfg(all(target_os = "linux", target_arch = "arm"))] {
323 let cx = unsafe { &*(cx as *const libc::ucontext_t) };
324 TrapRegisters {
325 pc: cx.uc_mcontext.arm_pc as usize,
326 fp: cx.uc_mcontext.arm_fp as usize,
327 }
328 } else {
329 compile_error!("unsupported platform");
330 panic!();
331 }
332 }
333}
334
335unsafe fn store_handler_in_ucontext(cx: *mut libc::c_void, handler: &Handler) {
338 cfg_if::cfg_if! {
339 if #[cfg(all(any(target_os = "linux", target_os = "android", target_os = "illumos"), target_arch = "x86_64"))] {
340 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
341 cx.uc_mcontext.gregs[libc::REG_RIP as usize] = handler.pc as _;
342 cx.uc_mcontext.gregs[libc::REG_RSP as usize] = handler.sp as _;
343 cx.uc_mcontext.gregs[libc::REG_RBP as usize] = handler.fp as _;
344 cx.uc_mcontext.gregs[libc::REG_RAX as usize] = 0;
345 cx.uc_mcontext.gregs[libc::REG_RDX as usize] = 0;
346 } else if #[cfg(all(any(target_os = "linux", target_os = "android"), target_arch = "aarch64"))] {
347 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
348 cx.uc_mcontext.pc = handler.pc as _;
349 cx.uc_mcontext.sp = handler.sp as _;
350 cx.uc_mcontext.regs[29] = handler.fp as _;
351 cx.uc_mcontext.regs[0] = 0;
352 cx.uc_mcontext.regs[1] = 0;
353 } else if #[cfg(all(target_os = "linux", target_arch = "s390x"))] {
354 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
355 cx.uc_mcontext.psw.addr = handler.pc as _;
356 cx.uc_mcontext.gregs[15] = handler.sp as _;
357 cx.uc_mcontext.gregs[6] = 0;
358 cx.uc_mcontext.gregs[7] = 0;
359 } else if #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))] {
360 unsafe {
361 let cx = cx.cast::<libc::ucontext_t>().as_mut().unwrap();
362 let cx = cx.uc_mcontext.as_mut().unwrap();
363 cx.__ss.__rip = handler.pc as _;
364 cx.__ss.__rsp = handler.sp as _;
365 cx.__ss.__rbp = handler.fp as _;
366 cx.__ss.__rax = 0;
367 cx.__ss.__rdx = 0;
368 }
369 } else if #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] {
370 unsafe {
371 let cx = cx.cast::<libc::ucontext_t>().as_mut().unwrap();
372 let cx = cx.uc_mcontext.as_mut().unwrap();
373 cx.__ss.__pc = handler.pc as _;
374 cx.__ss.__sp = handler.sp as _;
375 cx.__ss.__fp = handler.fp as _;
376 cx.__ss.__x[0] = 0;
377 cx.__ss.__x[1] = 0;
378 }
379 } else if #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] {
380 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
381 cx.uc_mcontext.mc_rip = handler.pc as _;
382 cx.uc_mcontext.mc_rbp = handler.fp as _;
383 cx.uc_mcontext.mc_rsp = handler.sp as _;
384 cx.uc_mcontext.mc_rax = 0;
385 cx.uc_mcontext.mc_rdx = 0;
386 } else if #[cfg(all(target_os = "linux", target_arch = "riscv64"))] {
387 let cx = unsafe { cx.cast::<libc::ucontext_t>().as_mut().unwrap() };
388 cx.uc_mcontext.__gregs[libc::REG_PC] = handler.pc as _;
389 cx.uc_mcontext.__gregs[libc::REG_S0] = handler.fp as _;
390 cx.uc_mcontext.__gregs[libc::REG_SP] = handler.sp as _;
391 cx.uc_mcontext.__gregs[libc::REG_A0] = 0;
392 cx.uc_mcontext.__gregs[libc::REG_A0 + 1] = 0;
393 } else {
394 compile_error!("unsupported platform");
395 panic!();
396 }
397 }
398}
399
400#[cold]
425pub fn lazy_per_thread_init() {
426 if cfg!(asan) {
458 return;
459 }
460
461 std::thread_local! {
465 static STACK: RefCell<Option<Stack>> = const { RefCell::new(None) };
466 }
467
468 const MIN_STACK_SIZE: usize = 64 * 4096;
478
479 struct Stack {
480 mmap_ptr: *mut libc::c_void,
481 mmap_size: usize,
482 }
483
484 return STACK.with(|s| {
485 *s.borrow_mut() = unsafe { allocate_sigaltstack() };
486 });
487
488 unsafe fn allocate_sigaltstack() -> Option<Stack> {
489 let mut old_stack = unsafe { mem::zeroed() };
492 let r = unsafe { libc::sigaltstack(ptr::null(), &mut old_stack) };
493 assert_eq!(
494 r,
495 0,
496 "learning about sigaltstack failed: {}",
497 io::Error::last_os_error()
498 );
499 if old_stack.ss_flags & libc::SS_DISABLE == 0 && old_stack.ss_size >= MIN_STACK_SIZE {
500 return None;
501 }
502
503 let page_size = crate::runtime::vm::host_page_size();
506 let guard_size = page_size;
507 let alloc_size = guard_size + MIN_STACK_SIZE;
508
509 let ptr = unsafe {
510 rustix::mm::mmap_anonymous(
511 null_mut(),
512 alloc_size,
513 rustix::mm::ProtFlags::empty(),
514 rustix::mm::MapFlags::PRIVATE,
515 )
516 .expect("failed to allocate memory for sigaltstack")
517 };
518
519 let stack_ptr = (ptr as usize + guard_size) as *mut std::ffi::c_void;
522 unsafe {
523 rustix::mm::mprotect(
524 stack_ptr,
525 MIN_STACK_SIZE,
526 rustix::mm::MprotectFlags::READ | rustix::mm::MprotectFlags::WRITE,
527 )
528 .expect("mprotect to configure memory for sigaltstack failed");
529 }
530 let new_stack = libc::stack_t {
531 ss_sp: stack_ptr,
532 ss_flags: 0,
533 ss_size: MIN_STACK_SIZE,
534 };
535 let r = unsafe { libc::sigaltstack(&new_stack, ptr::null_mut()) };
536 assert_eq!(
537 r,
538 0,
539 "registering new sigaltstack failed: {}",
540 io::Error::last_os_error()
541 );
542
543 Some(Stack {
544 mmap_ptr: ptr,
545 mmap_size: alloc_size,
546 })
547 }
548
549 impl Drop for Stack {
550 fn drop(&mut self) {
551 unsafe {
552 let r = rustix::mm::munmap(self.mmap_ptr, self.mmap_size);
554 debug_assert!(r.is_ok(), "munmap failed during thread shutdown");
555 }
556 }
557 }
558}