crash_handler/linux.rs
1pub mod jmp;
2mod state;
3
4use crate::Error;
5
6/// The signals that we support catching and raising
7#[derive(Copy, Clone, PartialEq, Eq)]
8#[repr(i32)]
9pub enum Signal {
10 Abort = libc::SIGABRT,
11 Bus = libc::SIGBUS,
12 Fpe = libc::SIGFPE,
13 Illegal = libc::SIGILL,
14 Segv = libc::SIGSEGV,
15 Trap = libc::SIGTRAP,
16}
17
18impl Signal {
19 #[inline]
20 pub fn ignore(self) {
21 unsafe {
22 state::ignore_signal(self);
23 }
24 }
25}
26
27/// A Linux/Android signal handler
28pub struct CrashHandler;
29
30#[allow(clippy::unused_self)]
31impl CrashHandler {
32 /// Attaches the signal handler.
33 ///
34 /// The provided callback will be invoked if a signal is caught, providing a
35 /// [`crate::CrashContext`] with the details of the thread where the
36 /// signal was raised.
37 ///
38 /// The callback runs in a compromised context, so it is highly recommended
39 /// to not perform actions that may fail due to corrupted state that caused
40 /// or is a symptom of the original signal. This includes doing heap
41 /// allocations from the same allocator as the crashing code.
42 pub fn attach(on_crash: Box<dyn crate::CrashEvent>) -> Result<Self, Error> {
43 state::attach(on_crash)?;
44 Ok(Self)
45 }
46
47 /// Detaches the handler.
48 ///
49 /// This is done automatically when this [`CrashHandler`] is dropped.
50 #[inline]
51 pub fn detach(self) {
52 state::detach();
53 }
54
55 /// Set the process that is allowed to perform `ptrace` operations on the
56 /// current process.
57 ///
58 /// If you want to write a minidump from a child/external process when
59 /// a crash occurs in this process, you can use this method to set that
60 /// process as the only process allowed to use ptrace on this process.
61 ///
62 /// The process set by this method will be used by calling
63 /// `prctl(PR_SET_PTRACER, <the pid you want to ptrace this process>, ...)`
64 /// before handing off control to your user callback, presumably to trigger
65 /// dumping of your process via the specified process. By default if this
66 /// method is not called, `PR_SET_PTRACER_ANY` is used to allow any process
67 /// to dump the current process.
68 ///
69 /// Note that this is only needed if `/proc/sys/kernel/yama/ptrace_scope`
70 /// is 1 "restricted ptrace", but there is no harm in setting this if it is
71 /// in another mode.
72 ///
73 /// See <https://www.kernel.org/doc/Documentation/security/Yama.txt> for
74 /// the full documentation.
75 #[inline]
76 pub fn set_ptracer(&self, pid: Option<u32>) {
77 let mut lock = state::HANDLER.lock();
78
79 if let Some(handler) = &mut *lock {
80 handler.dump_process = pid;
81 }
82 }
83
84 /// Sends the specified user signal.
85 pub fn simulate_signal(&self, signal: u32) -> crate::CrashEventResult {
86 // Normally this would be an unsafe function, since this unsafe encompasses
87 // the entirety of the body, however the user is really not required to
88 // uphold any guarantees on their end, so no real need to declare the
89 // function itself unsafe.
90 unsafe {
91 let mut siginfo: libc::signalfd_siginfo = std::mem::zeroed();
92 siginfo.ssi_signo = signal;
93 siginfo.ssi_code = state::SI_USER;
94 siginfo.ssi_pid = std::process::id();
95
96 let mut context = std::mem::zeroed();
97 crash_context::crash_context_getcontext(&mut context);
98
99 let lock = state::HANDLER.lock();
100 if let Some(handler) = &*lock {
101 handler.handle_signal(
102 &mut *(&mut siginfo as *mut libc::signalfd_siginfo).cast::<libc::siginfo_t>(),
103 &mut *(&mut context as *mut crash_context::ucontext_t).cast::<libc::c_void>(),
104 )
105 } else {
106 crate::CrashEventResult::Handled(false)
107 }
108 }
109 }
110}
111
112impl Drop for CrashHandler {
113 fn drop(&mut self) {
114 state::detach();
115 }
116}