Skip to main content

rustpython_host_env/
faulthandler.rs

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/// `EXCEPTION_CONTINUE_SEARCH`: this handler only reports; later
506/// handlers decide the process fate.
507#[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}