Skip to main content

libdd_crashtracker/crash_info/
sig_info.rs

1// Copyright 2024-Present Datadog, Inc. https://www.datadoghq.com/
2// SPDX-License-Identifier: Apache-2.0
3use num_derive::{FromPrimitive, ToPrimitive};
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
8pub struct SigInfo {
9    #[serde(default, skip_serializing_if = "Option::is_none")]
10    pub si_addr: Option<String>,
11    pub si_code: libc::c_int,
12    pub si_code_human_readable: SiCodes,
13    pub si_signo: libc::c_int,
14    pub si_signo_human_readable: SignalNames,
15}
16
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
18#[allow(clippy::upper_case_acronyms, non_camel_case_types)]
19#[repr(C)]
20/// See https://man7.org/linux/man-pages/man7/signal.7.html
21pub enum SignalNames {
22    SIGHUP,
23    SIGINT,
24    SIGQUIT,
25    SIGILL,
26    SIGTRAP,
27    SIGABRT,
28    SIGBUS,
29    SIGFPE,
30    SIGKILL,
31    SIGUSR1,
32    SIGSEGV,
33    SIGUSR2,
34    SIGPIPE,
35    SIGALRM,
36    SIGTERM,
37    SIGCHLD,
38    SIGCONT,
39    SIGSTOP,
40    SIGTSTP,
41    SIGTTIN,
42    SIGTTOU,
43    SIGURG,
44    SIGXCPU,
45    SIGXFSZ,
46    SIGVTALRM,
47    SIGPROF,
48    SIGWINCH,
49    SIGIO,
50    SIGSYS,
51    SIGEMT,
52    SIGINFO,
53    UNKNOWN,
54}
55
56#[cfg(unix)]
57pub use unix::*;
58
59#[cfg(unix)]
60mod unix {
61    use super::*;
62
63    impl From<libc::c_int> for SignalNames {
64        fn from(value: libc::c_int) -> Self {
65            match value {
66                libc::SIGHUP => SignalNames::SIGHUP,
67                libc::SIGINT => SignalNames::SIGINT,
68                libc::SIGQUIT => SignalNames::SIGQUIT,
69                libc::SIGILL => SignalNames::SIGILL,
70                libc::SIGTRAP => SignalNames::SIGTRAP,
71                libc::SIGABRT => SignalNames::SIGABRT,
72                libc::SIGBUS => SignalNames::SIGBUS,
73                libc::SIGFPE => SignalNames::SIGFPE,
74                libc::SIGKILL => SignalNames::SIGKILL,
75                libc::SIGUSR1 => SignalNames::SIGUSR1,
76                libc::SIGSEGV => SignalNames::SIGSEGV,
77                libc::SIGUSR2 => SignalNames::SIGUSR2,
78                libc::SIGPIPE => SignalNames::SIGPIPE,
79                libc::SIGALRM => SignalNames::SIGALRM,
80                libc::SIGTERM => SignalNames::SIGTERM,
81                libc::SIGCHLD => SignalNames::SIGCHLD,
82                libc::SIGCONT => SignalNames::SIGCONT,
83                libc::SIGSTOP => SignalNames::SIGSTOP,
84                libc::SIGTSTP => SignalNames::SIGTSTP,
85                libc::SIGTTIN => SignalNames::SIGTTIN,
86                libc::SIGTTOU => SignalNames::SIGTTOU,
87                libc::SIGURG => SignalNames::SIGURG,
88                libc::SIGXCPU => SignalNames::SIGXCPU,
89                libc::SIGXFSZ => SignalNames::SIGXFSZ,
90                libc::SIGVTALRM => SignalNames::SIGVTALRM,
91                libc::SIGPROF => SignalNames::SIGPROF,
92                libc::SIGWINCH => SignalNames::SIGWINCH,
93                libc::SIGIO => SignalNames::SIGIO,
94                libc::SIGSYS => SignalNames::SIGSYS,
95                #[cfg(not(any(
96                    target_os = "android",
97                    target_os = "emscripten",
98                    target_os = "fuchsia",
99                    target_os = "linux",
100                    target_os = "redox",
101                    target_os = "haiku"
102                )))]
103                libc::SIGEMT => SignalNames::SIGEMT,
104                #[cfg(not(any(
105                    target_os = "android",
106                    target_os = "emscripten",
107                    target_os = "fuchsia",
108                    target_os = "linux",
109                    target_os = "redox",
110                    target_os = "haiku",
111                    target_os = "aix"
112                )))]
113                libc::SIGINFO => SignalNames::SIGINFO,
114                _ => SignalNames::UNKNOWN,
115            }
116        }
117    }
118
119    extern "C" {
120        /// A bit of C code which can access the constants in <signal.h>.
121        /// See the file comment on emit_sicodes.c for full details.
122        fn translate_si_code_impl(signum: libc::c_int, si_code: libc::c_int) -> libc::c_int;
123    }
124
125    pub fn translate_si_code(signum: libc::c_int, si_code: libc::c_int) -> SiCodes {
126        use num_traits::FromPrimitive;
127        // SAFETY: this function has no safety requirements
128        let translated = unsafe { translate_si_code_impl(signum, si_code) };
129        SiCodes::from_i32(translated).unwrap_or(SiCodes::UNKNOWN)
130    }
131
132    #[cfg(test)]
133    #[cfg_attr(miri, ignore)]
134    #[test]
135    fn test_si_code() {
136        // standard values differ between oses, but it seems like segv match
137        // https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/siginfo.h
138        // https://github.com/apple/darwin-xnu/blob/main/bsd/sys/signal.h
139        assert_eq!(translate_si_code(libc::SIGSEGV, 2), SiCodes::SEGV_ACCERR);
140
141        // An invalid code should translate to UNKNOWN
142        assert_eq!(translate_si_code(libc::SIGSEGV, 42), SiCodes::UNKNOWN);
143    }
144
145    impl From<nix::sys::signal::Signal> for SignalNames {
146        fn from(value: nix::sys::signal::Signal) -> Self {
147            match value {
148                nix::sys::signal::Signal::SIGHUP => SignalNames::SIGHUP,
149                nix::sys::signal::Signal::SIGINT => SignalNames::SIGINT,
150                nix::sys::signal::Signal::SIGQUIT => SignalNames::SIGQUIT,
151                nix::sys::signal::Signal::SIGILL => SignalNames::SIGILL,
152                nix::sys::signal::Signal::SIGTRAP => SignalNames::SIGTRAP,
153                nix::sys::signal::Signal::SIGABRT => SignalNames::SIGABRT,
154                nix::sys::signal::Signal::SIGBUS => SignalNames::SIGBUS,
155                nix::sys::signal::Signal::SIGFPE => SignalNames::SIGFPE,
156                nix::sys::signal::Signal::SIGKILL => SignalNames::SIGKILL,
157                nix::sys::signal::Signal::SIGUSR1 => SignalNames::SIGUSR1,
158                nix::sys::signal::Signal::SIGSEGV => SignalNames::SIGSEGV,
159                nix::sys::signal::Signal::SIGUSR2 => SignalNames::SIGUSR2,
160                nix::sys::signal::Signal::SIGPIPE => SignalNames::SIGPIPE,
161                nix::sys::signal::Signal::SIGALRM => SignalNames::SIGALRM,
162                nix::sys::signal::Signal::SIGTERM => SignalNames::SIGTERM,
163                nix::sys::signal::Signal::SIGCHLD => SignalNames::SIGCHLD,
164                nix::sys::signal::Signal::SIGCONT => SignalNames::SIGCONT,
165                nix::sys::signal::Signal::SIGSTOP => SignalNames::SIGSTOP,
166                nix::sys::signal::Signal::SIGTSTP => SignalNames::SIGTSTP,
167                nix::sys::signal::Signal::SIGTTIN => SignalNames::SIGTTIN,
168                nix::sys::signal::Signal::SIGTTOU => SignalNames::SIGTTOU,
169                nix::sys::signal::Signal::SIGURG => SignalNames::SIGURG,
170                nix::sys::signal::Signal::SIGXCPU => SignalNames::SIGXCPU,
171                nix::sys::signal::Signal::SIGXFSZ => SignalNames::SIGXFSZ,
172                nix::sys::signal::Signal::SIGVTALRM => SignalNames::SIGVTALRM,
173                nix::sys::signal::Signal::SIGPROF => SignalNames::SIGPROF,
174                nix::sys::signal::Signal::SIGWINCH => SignalNames::SIGWINCH,
175                nix::sys::signal::Signal::SIGIO => SignalNames::SIGIO,
176                nix::sys::signal::Signal::SIGSYS => SignalNames::SIGSYS,
177                #[cfg(not(any(
178                    target_os = "android",
179                    target_os = "emscripten",
180                    target_os = "fuchsia",
181                    target_os = "linux",
182                    target_os = "redox",
183                    target_os = "haiku"
184                )))]
185                nix::sys::signal::Signal::SIGEMT => SignalNames::SIGEMT,
186                #[cfg(not(any(
187                    target_os = "android",
188                    target_os = "emscripten",
189                    target_os = "fuchsia",
190                    target_os = "linux",
191                    target_os = "redox",
192                    target_os = "haiku",
193                    target_os = "aix"
194                )))]
195                nix::sys::signal::Signal::SIGINFO => SignalNames::SIGINFO,
196                _ => SignalNames::UNKNOWN,
197            }
198        }
199    }
200
201    /// Converts a signum into a Signal.  Can't use the from trait because we don't own either type.
202    pub fn signal_from_signum(value: libc::c_int) -> anyhow::Result<nix::sys::signal::Signal> {
203        let rval = match value {
204            libc::SIGHUP => nix::sys::signal::Signal::SIGHUP,
205            libc::SIGINT => nix::sys::signal::Signal::SIGINT,
206            libc::SIGQUIT => nix::sys::signal::Signal::SIGQUIT,
207            libc::SIGILL => nix::sys::signal::Signal::SIGILL,
208            libc::SIGTRAP => nix::sys::signal::Signal::SIGTRAP,
209            libc::SIGABRT => nix::sys::signal::Signal::SIGABRT,
210            libc::SIGBUS => nix::sys::signal::Signal::SIGBUS,
211            libc::SIGFPE => nix::sys::signal::Signal::SIGFPE,
212            libc::SIGKILL => nix::sys::signal::Signal::SIGKILL,
213            libc::SIGUSR1 => nix::sys::signal::Signal::SIGUSR1,
214            libc::SIGSEGV => nix::sys::signal::Signal::SIGSEGV,
215            libc::SIGUSR2 => nix::sys::signal::Signal::SIGUSR2,
216            libc::SIGPIPE => nix::sys::signal::Signal::SIGPIPE,
217            libc::SIGALRM => nix::sys::signal::Signal::SIGALRM,
218            libc::SIGTERM => nix::sys::signal::Signal::SIGTERM,
219            libc::SIGCHLD => nix::sys::signal::Signal::SIGCHLD,
220            libc::SIGCONT => nix::sys::signal::Signal::SIGCONT,
221            libc::SIGSTOP => nix::sys::signal::Signal::SIGSTOP,
222            libc::SIGTSTP => nix::sys::signal::Signal::SIGTSTP,
223            libc::SIGTTIN => nix::sys::signal::Signal::SIGTTIN,
224            libc::SIGTTOU => nix::sys::signal::Signal::SIGTTOU,
225            libc::SIGURG => nix::sys::signal::Signal::SIGURG,
226            libc::SIGXCPU => nix::sys::signal::Signal::SIGXCPU,
227            libc::SIGXFSZ => nix::sys::signal::Signal::SIGXFSZ,
228            libc::SIGVTALRM => nix::sys::signal::Signal::SIGVTALRM,
229            libc::SIGPROF => nix::sys::signal::Signal::SIGPROF,
230            libc::SIGWINCH => nix::sys::signal::Signal::SIGWINCH,
231            libc::SIGIO => nix::sys::signal::Signal::SIGIO,
232            libc::SIGSYS => nix::sys::signal::Signal::SIGSYS,
233            #[cfg(not(any(
234                target_os = "android",
235                target_os = "emscripten",
236                target_os = "fuchsia",
237                target_os = "linux",
238                target_os = "redox",
239                target_os = "haiku"
240            )))]
241            libc::SIGEMT => nix::sys::signal::Signal::SIGEMT,
242            #[cfg(not(any(
243                target_os = "android",
244                target_os = "emscripten",
245                target_os = "fuchsia",
246                target_os = "linux",
247                target_os = "redox",
248                target_os = "haiku",
249                target_os = "aix"
250            )))]
251            libc::SIGINFO => nix::sys::signal::Signal::SIGINFO,
252            _ => anyhow::bail!("Unexpected signal number {value}"),
253        };
254        Ok(rval)
255    }
256}
257
258#[derive(
259    Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, FromPrimitive, ToPrimitive,
260)]
261#[allow(clippy::upper_case_acronyms, non_camel_case_types)]
262#[repr(C)]
263/// See https://man7.org/linux/man-pages/man2/sigaction.2.html
264/// MUST REMAIN IN SYNC WITH THE ENUM IN emit_sigcodes.c
265pub enum SiCodes {
266    BUS_ADRALN,
267    BUS_ADRERR,
268    BUS_MCEERR_AO,
269    BUS_MCEERR_AR,
270    BUS_OBJERR,
271    ILL_BADSTK,
272    ILL_COPROC,
273    ILL_ILLADR,
274    ILL_ILLOPC,
275    ILL_ILLOPN,
276    ILL_ILLTRP,
277    ILL_PRVOPC,
278    ILL_PRVREG,
279    SEGV_ACCERR,
280    SEGV_BNDERR,
281    SEGV_MAPERR,
282    SEGV_PKUERR,
283    SI_ASYNCIO,
284    SI_KERNEL,
285    SI_MESGQ,
286    SI_QUEUE,
287    SI_SIGIO,
288    SI_TIMER,
289    SI_TKILL,
290    SI_USER,
291    SYS_SECCOMP,
292    UNKNOWN,
293}
294
295#[cfg(test)]
296impl SigInfo {
297    pub fn test_instance(_seed: u64) -> Self {
298        Self {
299            si_addr: Some("0x0000000000001234".to_string()),
300            si_code: 1,
301            si_code_human_readable: SiCodes::SEGV_BNDERR,
302            si_signo: 11,
303            si_signo_human_readable: SignalNames::SIGSEGV,
304        }
305    }
306}