1#![allow(
2 clippy::missing_safety_doc,
3 reason = "These wrappers expose low-level fault handler hooks with raw OS ABI semantics."
4)]
5#![allow(
6 clippy::result_unit_err,
7 reason = "These helpers preserve the existing fault-handler error surface."
8)]
9#![allow(static_mut_refs)]
10
11#[cfg(unix)]
12use alloc::vec::Vec;
13#[cfg(unix)]
14use parking_lot::Mutex;
15
16#[cfg(unix)]
17pub use libc::{SA_NODEFER, c_int};
18pub use libc::{SIGFPE, SIGSEGV};
19#[cfg(windows)]
20use windows_sys::Win32::System::{
21 Diagnostics::Debug::{
22 AddVectoredExceptionHandler, EXCEPTION_POINTERS, PVECTORED_EXCEPTION_HANDLER,
23 RaiseException, RemoveVectoredExceptionHandler, SEM_NOGPFAULTERRORBOX, SetErrorMode,
24 },
25 Threading::GetCurrentThreadId,
26};
27
28#[cfg(windows)]
29pub type ExceptionPointers = EXCEPTION_POINTERS;
30
31#[cfg(unix)]
32struct FatalSignalHandler {
33 signum: libc::c_int,
34 enabled: bool,
35 name: &'static str,
36 previous: libc::sigaction,
37}
38
39#[cfg(windows)]
40struct FatalSignalHandler {
41 signum: libc::c_int,
42 enabled: bool,
43 name: &'static str,
44 previous: libc::sighandler_t,
45}
46
47#[cfg(unix)]
48impl FatalSignalHandler {
49 const fn new(signum: libc::c_int, name: &'static str) -> Self {
50 Self {
51 signum,
52 enabled: false,
53 name,
54 previous: unsafe { core::mem::zeroed() },
55 }
56 }
57}
58
59#[cfg(windows)]
60impl FatalSignalHandler {
61 const fn new(signum: libc::c_int, name: &'static str) -> Self {
62 Self {
63 signum,
64 enabled: false,
65 name,
66 previous: 0,
67 }
68 }
69}
70
71#[cfg(unix)]
72const FATAL_SIGNAL_COUNT: usize = 5;
73#[cfg(windows)]
74const FATAL_SIGNAL_COUNT: usize = 4;
75
76#[cfg(unix)]
77static mut FATAL_SIGNAL_HANDLERS: [FatalSignalHandler; FATAL_SIGNAL_COUNT] = [
78 FatalSignalHandler::new(libc::SIGBUS, "Bus error"),
79 FatalSignalHandler::new(libc::SIGILL, "Illegal instruction"),
80 FatalSignalHandler::new(libc::SIGFPE, "Floating-point exception"),
81 FatalSignalHandler::new(libc::SIGABRT, "Aborted"),
82 FatalSignalHandler::new(libc::SIGSEGV, "Segmentation fault"),
83];
84
85#[cfg(windows)]
86static mut FATAL_SIGNAL_HANDLERS: [FatalSignalHandler; FATAL_SIGNAL_COUNT] = [
87 FatalSignalHandler::new(libc::SIGILL, "Illegal instruction"),
88 FatalSignalHandler::new(libc::SIGFPE, "Floating-point exception"),
89 FatalSignalHandler::new(libc::SIGABRT, "Aborted"),
90 FatalSignalHandler::new(libc::SIGSEGV, "Segmentation fault"),
91];
92
93#[cfg(unix)]
94const USER_SIGNAL_CAPACITY: usize = 64;
95
96#[cfg(unix)]
97#[derive(Clone, Copy)]
98pub struct UserSignal {
99 pub fd: i32,
100 pub all_threads: bool,
101 pub chain: bool,
102}
103
104#[cfg(unix)]
105#[derive(Clone, Copy)]
106struct RegisteredUserSignal {
107 enabled: bool,
108 fd: i32,
109 all_threads: bool,
110 chain: bool,
111 previous: libc::sigaction,
112}
113
114#[cfg(unix)]
115impl Default for RegisteredUserSignal {
116 fn default() -> Self {
117 Self {
118 enabled: false,
119 fd: 2,
120 all_threads: true,
121 chain: false,
122 previous: unsafe { core::mem::zeroed() },
123 }
124 }
125}
126
127#[cfg(unix)]
128static USER_SIGNALS: Mutex<Option<Vec<RegisteredUserSignal>>> = Mutex::new(None);
129
130pub fn write_fd(fd: i32, buf: &[u8]) {
131 let _ = unsafe { libc::write(fd, buf.as_ptr() as *const libc::c_void, buf.len() as _) };
132}
133
134#[cfg(any(unix, windows))]
135pub fn is_fatal_signal(signum: libc::c_int) -> bool {
136 unsafe {
137 FATAL_SIGNAL_HANDLERS
138 .iter()
139 .any(|handler| handler.signum == signum)
140 }
141}
142
143#[cfg(any(unix, windows))]
144pub fn fatal_signal_name(signum: libc::c_int) -> Option<&'static str> {
145 unsafe {
146 FATAL_SIGNAL_HANDLERS
147 .iter()
148 .find(|handler| handler.signum == signum)
149 .map(|handler| handler.name)
150 }
151}
152
153#[cfg(any(unix, windows))]
154pub fn abort_process() -> ! {
155 unsafe { libc::abort() }
156}
157
158#[cfg(any(unix, windows))]
159pub fn raise_signal(signum: libc::c_int) {
160 unsafe {
161 libc::raise(signum);
162 }
163}
164
165#[cfg(unix)]
166#[inline]
167pub fn current_thread_id() -> u64 {
168 unsafe { libc::pthread_self() as u64 }
169}
170
171#[cfg(windows)]
172#[inline]
173pub fn current_thread_id() -> u64 {
174 unsafe { GetCurrentThreadId() as u64 }
175}
176
177#[cfg(unix)]
178pub fn install_sigaction(
179 signum: libc::c_int,
180 handler: extern "C" fn(libc::c_int),
181 flags: libc::c_int,
182 previous: &mut libc::sigaction,
183) -> bool {
184 let mut action: libc::sigaction = unsafe { core::mem::zeroed() };
185 action.sa_sigaction = handler as *const () as libc::sighandler_t;
186 action.sa_flags = flags;
187 unsafe { libc::sigaction(signum, &action, previous) == 0 }
188}
189
190#[cfg(unix)]
191unsafe fn disable_fatal_signal_handler(handler: &mut FatalSignalHandler) {
192 if !handler.enabled {
193 return;
194 }
195 handler.enabled = false;
196 restore_sigaction(handler.signum, &handler.previous);
197}
198
199#[cfg(unix)]
200pub fn enable_fatal_handlers(handler: extern "C" fn(libc::c_int), flags: libc::c_int) -> bool {
201 unsafe {
202 let mut installed = Vec::new();
203 for entry in &mut FATAL_SIGNAL_HANDLERS {
204 if entry.enabled {
205 continue;
206 }
207
208 if !install_sigaction(entry.signum, handler, flags, &mut entry.previous) {
209 for signum in installed {
210 disable_fatal_signal(signum);
211 }
212 return false;
213 }
214 entry.enabled = true;
215 installed.push(entry.signum);
216 }
217 }
218 true
219}
220
221#[cfg(unix)]
222pub fn disable_fatal_signal(signum: libc::c_int) {
223 unsafe {
224 if let Some(handler) = FATAL_SIGNAL_HANDLERS
225 .iter_mut()
226 .find(|handler| handler.signum == signum)
227 {
228 disable_fatal_signal_handler(handler);
229 }
230 }
231}
232
233#[cfg(unix)]
234pub fn disable_fatal_handlers() {
235 unsafe {
236 for handler in &mut FATAL_SIGNAL_HANDLERS {
237 disable_fatal_signal_handler(handler);
238 }
239 }
240}
241
242#[cfg(unix)]
243pub fn restore_sigaction(signum: libc::c_int, previous: &libc::sigaction) {
244 unsafe {
245 libc::sigaction(signum, previous, core::ptr::null_mut());
246 }
247}
248
249#[cfg(unix)]
250pub fn signal_default_and_raise(signum: libc::c_int) {
251 unsafe {
252 libc::signal(signum, libc::SIG_DFL);
253 libc::raise(signum);
254 }
255}
256
257#[cfg(unix)]
258pub fn exit_immediately(code: libc::c_int) -> ! {
259 unsafe { libc::_exit(code) }
260}
261
262#[cfg(unix)]
263pub fn get_user_signal(signum: usize) -> Option<UserSignal> {
264 let guard = USER_SIGNALS.lock();
265 guard
266 .as_ref()
267 .and_then(|signals| signals.get(signum))
268 .and_then(|signal| {
269 signal.enabled.then_some(UserSignal {
270 fd: signal.fd,
271 all_threads: signal.all_threads,
272 chain: signal.chain,
273 })
274 })
275}
276
277#[cfg(unix)]
278pub fn register_user_signal(
279 signum: libc::c_int,
280 fd: i32,
281 all_threads: bool,
282 chain: bool,
283 handler: extern "C" fn(libc::c_int),
284) -> std::io::Result<()> {
285 if signum < 0 || signum as usize >= USER_SIGNAL_CAPACITY {
286 return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
287 }
288 let signum = signum as usize;
289 let mut guard = USER_SIGNALS.lock();
290 if guard.is_none() {
291 *guard = Some(vec![RegisteredUserSignal::default(); USER_SIGNAL_CAPACITY]);
292 }
293 let signals = guard
294 .as_mut()
295 .expect("user signal table must be initialized");
296 let entry = &mut signals[signum];
297
298 if !entry.enabled {
299 let mut previous = unsafe { core::mem::zeroed() };
300 if !install_sigaction(
301 signum as libc::c_int,
302 handler,
303 if chain {
304 libc::SA_NODEFER
305 } else {
306 libc::SA_RESTART
307 },
308 &mut previous,
309 ) {
310 return Err(std::io::Error::last_os_error());
311 }
312 entry.previous = previous;
313 }
314
315 entry.enabled = true;
316 entry.fd = fd;
317 entry.all_threads = all_threads;
318 entry.chain = chain;
319 Ok(())
320}
321
322#[cfg(unix)]
323pub fn unregister_user_signal(signum: libc::c_int) -> bool {
324 if signum < 0 {
325 return false;
326 }
327 let signum = signum as usize;
328 let mut guard = USER_SIGNALS.lock();
329 let Some(signals) = guard.as_mut() else {
330 return false;
331 };
332 let Some(entry) = signals.get_mut(signum) else {
333 return false;
334 };
335 if !entry.enabled {
336 return false;
337 }
338
339 let previous = entry.previous;
340 *entry = RegisteredUserSignal::default();
341 restore_sigaction(signum as libc::c_int, &previous);
342 true
343}
344
345#[cfg(unix)]
346pub fn reraise_user_signal(signum: libc::c_int, handler: extern "C" fn(libc::c_int)) -> bool {
347 if signum < 0 {
348 return false;
349 }
350 let signum_usize = signum as usize;
351 let previous = {
352 let guard = USER_SIGNALS.lock();
353 let Some(signals) = guard.as_ref() else {
354 return false;
355 };
356 let Some(entry) = signals.get(signum_usize) else {
357 return false;
358 };
359 if !entry.enabled || !entry.chain {
360 return false;
361 }
362 entry.previous
363 };
364
365 let saved_errno = crate::os::get_errno();
366 restore_sigaction(signum, &previous);
367 crate::os::set_errno(saved_errno);
368 raise_signal(signum);
369
370 let mut ignored_previous = unsafe { core::mem::zeroed() };
371 let _ = install_sigaction(signum, handler, libc::SA_NODEFER, &mut ignored_previous);
372
373 crate::os::set_errno(saved_errno);
374 true
375}
376
377#[cfg(windows)]
378pub fn install_signal_handler(
379 signum: libc::c_int,
380 handler: extern "C" fn(libc::c_int),
381) -> Result<libc::sighandler_t, ()> {
382 let previous = unsafe { libc::signal(signum, handler as *const () as libc::sighandler_t) };
383 if previous == libc::SIG_ERR as libc::sighandler_t {
384 Err(())
385 } else {
386 Ok(previous)
387 }
388}
389
390#[cfg(windows)]
391unsafe fn disable_fatal_signal_handler(handler: &mut FatalSignalHandler) {
392 if !handler.enabled {
393 return;
394 }
395 handler.enabled = false;
396 restore_signal_handler(handler.signum, handler.previous);
397}
398
399#[cfg(windows)]
400pub fn enable_fatal_handlers(handler: extern "C" fn(libc::c_int), _flags: libc::c_int) -> bool {
401 unsafe {
402 for entry in &mut FATAL_SIGNAL_HANDLERS {
403 if entry.enabled {
404 continue;
405 }
406
407 let Ok(previous) = install_signal_handler(entry.signum, handler) else {
408 return false;
409 };
410 entry.previous = previous;
411 entry.enabled = true;
412 }
413 }
414 true
415}
416
417#[cfg(windows)]
418pub fn disable_fatal_signal(signum: libc::c_int) {
419 unsafe {
420 if let Some(handler) = FATAL_SIGNAL_HANDLERS
421 .iter_mut()
422 .find(|handler| handler.signum == signum)
423 {
424 disable_fatal_signal_handler(handler);
425 }
426 }
427}
428
429#[cfg(windows)]
430pub fn disable_fatal_handlers() {
431 unsafe {
432 for handler in &mut FATAL_SIGNAL_HANDLERS {
433 disable_fatal_signal_handler(handler);
434 }
435 }
436}
437
438#[cfg(windows)]
439pub fn restore_signal_handler(signum: libc::c_int, previous: libc::sighandler_t) {
440 unsafe {
441 libc::signal(signum, previous);
442 }
443}
444
445#[cfg(windows)]
446pub fn signal_default_and_raise(signum: libc::c_int) {
447 unsafe {
448 libc::signal(signum, libc::SIG_DFL);
449 libc::raise(signum);
450 }
451}
452
453#[cfg(windows)]
454pub fn add_vectored_exception_handler(handler: PVECTORED_EXCEPTION_HANDLER) -> usize {
455 unsafe { AddVectoredExceptionHandler(1, handler) as usize }
456}
457
458#[cfg(windows)]
459pub fn remove_vectored_exception_handler(handle: usize) {
460 if handle != 0 {
461 unsafe {
462 RemoveVectoredExceptionHandler(handle as *mut core::ffi::c_void);
463 }
464 }
465}
466
467#[cfg(windows)]
468pub fn suppress_crash_report() {
469 unsafe {
470 let mode = SetErrorMode(SEM_NOGPFAULTERRORBOX);
471 SetErrorMode(mode | SEM_NOGPFAULTERRORBOX);
472 }
473}
474
475#[cfg(windows)]
476pub fn raise_exception(code: u32, flags: u32) {
477 unsafe {
478 RaiseException(code, flags, 0, core::ptr::null());
479 }
480}
481
482#[cfg(windows)]
483pub fn ignore_exception(code: u32) -> bool {
484 if (code & 0x8000_0000) == 0 {
485 return true;
486 }
487 code == 0xE06D7363 || code == 0xE0434352
488}
489
490#[cfg(windows)]
491pub fn exception_description(code: u32) -> Option<&'static str> {
492 match code {
493 EXCEPTION_ACCESS_VIOLATION => Some("access violation"),
494 EXCEPTION_FLT_DIVIDE_BY_ZERO => Some("float divide by zero"),
495 EXCEPTION_FLT_OVERFLOW => Some("float overflow"),
496 EXCEPTION_INT_DIVIDE_BY_ZERO => Some("int divide by zero"),
497 EXCEPTION_INT_OVERFLOW => Some("integer overflow"),
498 EXCEPTION_IN_PAGE_ERROR => Some("page error"),
499 EXCEPTION_STACK_OVERFLOW => Some("stack overflow"),
500 EXCEPTION_ILLEGAL_INSTRUCTION => Some("illegal instruction"),
501 _ => None,
502 }
503}
504
505#[cfg(windows)]
508pub const EXCEPTION_CONTINUE_SEARCH: i32 =
509 windows_sys::Win32::System::Diagnostics::Debug::EXCEPTION_CONTINUE_SEARCH;
510
511#[cfg(windows)]
512pub const EXCEPTION_ACCESS_VIOLATION: u32 =
513 windows_sys::Win32::Foundation::EXCEPTION_ACCESS_VIOLATION as u32;
514#[cfg(windows)]
515pub const EXCEPTION_ARRAY_BOUNDS_EXCEEDED: u32 =
516 windows_sys::Win32::Foundation::EXCEPTION_ARRAY_BOUNDS_EXCEEDED as u32;
517#[cfg(windows)]
518pub const EXCEPTION_BREAKPOINT: u32 = windows_sys::Win32::Foundation::EXCEPTION_BREAKPOINT as u32;
519#[cfg(windows)]
520pub const EXCEPTION_DATATYPE_MISALIGNMENT: u32 =
521 windows_sys::Win32::Foundation::EXCEPTION_DATATYPE_MISALIGNMENT as u32;
522#[cfg(windows)]
523pub const EXCEPTION_FLT_DENORMAL_OPERAND: u32 =
524 windows_sys::Win32::Foundation::EXCEPTION_FLT_DENORMAL_OPERAND as u32;
525#[cfg(windows)]
526pub const EXCEPTION_FLT_DIVIDE_BY_ZERO: u32 =
527 windows_sys::Win32::Foundation::EXCEPTION_FLT_DIVIDE_BY_ZERO as u32;
528#[cfg(windows)]
529pub const EXCEPTION_FLT_INEXACT_RESULT: u32 =
530 windows_sys::Win32::Foundation::EXCEPTION_FLT_INEXACT_RESULT as u32;
531#[cfg(windows)]
532pub const EXCEPTION_FLT_INVALID_OPERATION: u32 =
533 windows_sys::Win32::Foundation::EXCEPTION_FLT_INVALID_OPERATION as u32;
534#[cfg(windows)]
535pub const EXCEPTION_FLT_OVERFLOW: u32 =
536 windows_sys::Win32::Foundation::EXCEPTION_FLT_OVERFLOW as u32;
537#[cfg(windows)]
538pub const EXCEPTION_FLT_STACK_CHECK: u32 =
539 windows_sys::Win32::Foundation::EXCEPTION_FLT_STACK_CHECK as u32;
540#[cfg(windows)]
541pub const EXCEPTION_FLT_UNDERFLOW: u32 =
542 windows_sys::Win32::Foundation::EXCEPTION_FLT_UNDERFLOW as u32;
543#[cfg(windows)]
544pub const EXCEPTION_INT_DIVIDE_BY_ZERO: u32 =
545 windows_sys::Win32::Foundation::EXCEPTION_INT_DIVIDE_BY_ZERO as u32;
546#[cfg(windows)]
547pub const EXCEPTION_INT_OVERFLOW: u32 =
548 windows_sys::Win32::Foundation::EXCEPTION_INT_OVERFLOW as u32;
549#[cfg(windows)]
550pub const EXCEPTION_IN_PAGE_ERROR: u32 =
551 windows_sys::Win32::Foundation::EXCEPTION_IN_PAGE_ERROR as u32;
552#[cfg(windows)]
553pub const EXCEPTION_PRIV_INSTRUCTION: u32 =
554 windows_sys::Win32::Foundation::EXCEPTION_PRIV_INSTRUCTION as u32;
555#[cfg(windows)]
556pub const EXCEPTION_SINGLE_STEP: u32 = windows_sys::Win32::Foundation::EXCEPTION_SINGLE_STEP as u32;
557#[cfg(windows)]
558pub const EXCEPTION_STACK_OVERFLOW: u32 =
559 windows_sys::Win32::Foundation::EXCEPTION_STACK_OVERFLOW as u32;
560#[cfg(windows)]
561pub const EXCEPTION_ILLEGAL_INSTRUCTION: u32 =
562 windows_sys::Win32::Foundation::EXCEPTION_ILLEGAL_INSTRUCTION as u32;
563#[cfg(windows)]
564pub const EXCEPTION_NONCONTINUABLE: u32 =
565 windows_sys::Win32::System::SystemServices::EXCEPTION_NONCONTINUABLE;
566#[cfg(windows)]
567pub const EXCEPTION_NONCONTINUABLE_EXCEPTION: u32 =
568 windows_sys::Win32::Foundation::EXCEPTION_NONCONTINUABLE_EXCEPTION as u32;
569
570#[cfg(windows)]
571pub unsafe fn exception_code(exc_info: *mut EXCEPTION_POINTERS) -> u32 {
572 let record = unsafe { &*(*exc_info).ExceptionRecord };
573 record.ExceptionCode as u32
574}
575
576#[cfg(windows)]
577#[inline]
578pub fn is_access_violation(code: u32) -> bool {
579 code == EXCEPTION_ACCESS_VIOLATION
580}