hw_exception/
signals.rs

1// Copyright (c) 2023 Daniel Fox Franke
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3
4use crate::cdecls::*;
5
6use std::any::Any;
7use std::ffi::{c_int, c_short, c_void};
8use std::marker::PhantomData;
9use std::mem::MaybeUninit;
10
11///Error on unsupported signal number or code.
12///
13/// This can be returned by the `from_raw` constructor of [`Signo`] or
14/// [`Signal`] when the signal number or the subtype code is not one that is
15/// supported by this crate.
16#[derive(Debug, Copy, Clone)]
17pub enum UnsupportedSignalError {
18    /// The signal number is not supported.
19    UnsupportedSigno {
20        /// Signal number.
21        signo: c_int,
22    },
23    /// The signal number is supported, but the code is not.
24    UnsupportedCode {
25        /// Signal number.
26        signo: c_int,
27        /// Signal code.
28        code: c_int,
29    },
30}
31
32impl std::fmt::Display for UnsupportedSignalError {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        match self {
35            UnsupportedSignalError::UnsupportedSigno { signo } => {
36                write!(f, "Unsupported signal number: signo={}", signo)
37            }
38            UnsupportedSignalError::UnsupportedCode { signo, code } => {
39                write!(f, "Unsupported signal code: signo={}, code={}", signo, code)
40            }
41        }
42    }
43}
44
45impl std::error::Error for UnsupportedSignalError {}
46
47#[non_exhaustive]
48#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
49/// Signal numbers that this crate supports handling.
50pub enum Signo {
51    /// Illegal instruction.
52    /// 
53    /// Note that Rust's `abort` intrinsic works by executing an illegal instruction.
54    /// Be sure when handling this signal that your hook's behavior does not conflict
55    /// with this intent.
56    SIGILL,
57    /// Floating point exception.
58    SIGFPE,
59    /// Segmentation fault.
60    SIGSEGV,
61    /// Bus error.
62    SIGBUS,
63    /// Trace/breakpoint trap.
64    SIGTRAP,
65}
66
67impl Signo {
68    /// Returns a list of all signal numbers supported by this crate.
69    pub const fn all() -> &'static [Signo] {
70        use Signo::*;
71        &[SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP]
72    }
73
74    /// Constructs a `Signo` from a raw signal number.
75    pub fn from_raw(raw: c_int) -> Result<Signo, UnsupportedSignalError> {
76        match raw {
77            libc::SIGILL => Ok(Signo::SIGILL),
78            libc::SIGFPE => Ok(Signo::SIGFPE),
79            libc::SIGSEGV => Ok(Signo::SIGSEGV),
80            libc::SIGBUS => Ok(Signo::SIGBUS),
81            libc::SIGTRAP => Ok(Signo::SIGTRAP),
82            _ => Err(UnsupportedSignalError::UnsupportedSigno { signo: raw }),
83        }
84    }
85}
86
87impl From<Signo> for c_int {
88    fn from(value: Signo) -> Self {
89        match value {
90            Signo::SIGILL => libc::SIGILL,
91            Signo::SIGFPE => libc::SIGFPE,
92            Signo::SIGSEGV => libc::SIGSEGV,
93            Signo::SIGBUS => libc::SIGBUS,
94            Signo::SIGTRAP => libc::SIGTRAP,
95        }
96    }
97}
98
99#[non_exhaustive]
100#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
101/// Signal number plus associated subtype code.
102pub enum Signal {
103    /// Illegal instruction.
104    Ill(SigillCode),
105    /// Floating point exception.
106    Fpe(SigfpeCode),
107    /// Segmentation fault.
108    Segv(SigsegvCode),
109    /// Bus error.
110    Bus(SigbusCode),
111    /// Trace/breakpoint trap.
112    Trap(SigtrapCode),
113}
114
115impl Signal {
116    /// Constructs a `Signal` from a raw signal number and subtype code.
117    pub fn from_raw(signo: c_int, code: c_int) -> Result<Signal, UnsupportedSignalError> {
118        let cooked_signo = Signo::from_raw(signo)?;
119        let err = UnsupportedSignalError::UnsupportedCode { signo, code };
120
121        match cooked_signo {
122            Signo::SIGILL => unsafe {
123                let mut cooked_code: MaybeUninit<SigillCode> = MaybeUninit::uninit();
124                let ret = hwexception_translate_sigill_code(cooked_code.as_mut_ptr(), code);
125                if ret == 1 {
126                    Ok(Signal::Ill(cooked_code.assume_init()))
127                } else {
128                    Err(err)
129                }
130            },
131            Signo::SIGFPE => unsafe {
132                let mut cooked_code: MaybeUninit<SigfpeCode> = MaybeUninit::uninit();
133                let ret = hwexception_translate_sigfpe_code(cooked_code.as_mut_ptr(), code);
134                if ret == 1 {
135                    Ok(Signal::Fpe(cooked_code.assume_init()))
136                } else {
137                    Err(err)
138                }
139            },
140            Signo::SIGSEGV => unsafe {
141                let mut cooked_code: MaybeUninit<SigsegvCode> = MaybeUninit::uninit();
142                let ret = hwexception_translate_sigsegv_code(cooked_code.as_mut_ptr(), code);
143                if ret == 1 {
144                    Ok(Signal::Segv(cooked_code.assume_init()))
145                } else {
146                    Err(err)
147                }
148            },
149            Signo::SIGBUS => unsafe {
150                let mut cooked_code: MaybeUninit<SigbusCode> = MaybeUninit::uninit();
151                let ret = hwexception_translate_sigbus_code(cooked_code.as_mut_ptr(), code);
152                if ret == 1 {
153                    Ok(Signal::Bus(cooked_code.assume_init()))
154                } else {
155                    Err(err)
156                }
157            },
158            Signo::SIGTRAP => unsafe {
159                let mut cooked_code: MaybeUninit<SigtrapCode> = MaybeUninit::uninit();
160                let ret = hwexception_translate_sigtrap_code(cooked_code.as_mut_ptr(), code);
161                if ret == 1 {
162                    Ok(Signal::Trap(cooked_code.assume_init()))
163                } else {
164                    Err(err)
165                }
166            },
167        }
168    }
169
170    /// Returns the signal number associated with this signal.
171    pub fn signo(self) -> Signo {
172        use Signal::*;
173        match self {
174            Ill(_) => Signo::SIGILL,
175            Fpe(_) => Signo::SIGFPE,
176            Segv(_) => Signo::SIGSEGV,
177            Bus(_) => Signo::SIGBUS,
178            Trap(_) => Signo::SIGTRAP,
179        }
180    }
181}
182
183#[derive(Debug,Clone)]
184enum ExceptionInfoImpl<'a> {
185    Borrowed {
186        signo: c_int,
187        siginfo: *mut libc::siginfo_t,
188        context: *mut c_void,
189        _phantom: PhantomData<&'a ()>,
190    },
191    Owned {
192        signo: c_int,
193        siginfo: libc::siginfo_t,
194        context: *mut c_void,
195        sp: *mut c_void,
196        ip: *mut c_void,
197        symbol_address: *mut c_void,
198    },
199}
200
201#[derive(Debug,Clone)]
202/// Information about an exception.
203pub struct ExceptionInfo<'a>(ExceptionInfoImpl<'a>);
204
205impl ExceptionInfo<'_> {
206    /// Construct from arguments passed to a POSIX signal handler.
207    ///
208    /// # Safety
209    /// The caller must ensure the validity of the pointers and limit the
210    /// lifetime of the `ExceptionInfo` structure to that of the pointers. On
211    /// platforms which have a `ucontext_t` type, `context` must point to
212    /// one or else be NULL.
213    pub unsafe fn new<'a>(
214        signo: c_int,
215        siginfo: *mut libc::siginfo_t,
216        context: *mut c_void,
217    ) -> ExceptionInfo<'a> {
218        ExceptionInfo(ExceptionInfoImpl::Borrowed {
219            signo,
220            siginfo,
221            context,
222            _phantom: PhantomData,
223        })
224    }
225
226    /// Converts `self` into a representation which owns its storage.
227    pub fn into_owned(self) -> ExceptionInfo<'static> {
228        ExceptionInfo(match self.0 {
229            ExceptionInfoImpl::Borrowed {
230                signo,
231                siginfo,
232                context,
233                ..
234            } => unsafe {
235                ExceptionInfoImpl::Owned {
236                    signo,
237                    siginfo: *siginfo,
238                    context,
239                    sp: hwexception_get_sp(context),
240                    ip: hwexception_get_ip(context),
241                    symbol_address: hwexception_get_symbol_address(context),
242                }
243            },
244            ExceptionInfoImpl::Owned {
245                signo,
246                siginfo,
247                context,
248                sp,
249                ip,
250                symbol_address,
251            } => ExceptionInfoImpl::Owned {
252                signo,
253                siginfo,
254                context,
255                sp,
256                ip,
257                symbol_address
258            },
259        })
260    }
261
262    /// Returns the raw signal number which was passed to the signal handler.
263    pub fn signo_raw(&self) -> c_int {
264        match &self.0 {
265            ExceptionInfoImpl::Borrowed { signo, .. } => *signo,
266            ExceptionInfoImpl::Owned { signo, .. } => *signo,
267        }
268    }
269
270    /// Returns a reference to the raw `siginfo_t` structure which was passed to
271    /// the signal handler.
272    pub fn signinfo_raw(&self) -> &libc::siginfo_t {
273        match &self.0 {
274            ExceptionInfoImpl::Borrowed { siginfo, .. } => unsafe { &**siginfo },
275            ExceptionInfoImpl::Owned { siginfo, .. } => siginfo,
276        }
277    }
278
279    /// Returns the context pointer which was passed to the signal handler.
280    pub fn context(&self) -> *mut c_void {
281        match &self.0 {
282            ExceptionInfoImpl::Borrowed { context, .. } => *context,
283            ExceptionInfoImpl::Owned { context, .. } => *context,
284        }
285    }
286
287    /// Returns the type of signal which triggered this exception.
288    pub fn signo(&self) -> Signo {
289        Signo::from_raw(self.signo_raw()).unwrap()
290    }
291
292    /// Returns the address of the memory access which triggered the exception.
293    pub fn addr(&self) -> *mut c_void {
294        unsafe { self.signinfo_raw().si_addr() }
295    }
296
297    /// Returns the type of signal which triggered this exception along with its
298    /// associated subtype.
299    ///
300    /// This method is guaranteed to succeed when called on an `ExceptionInfo`
301    /// which was passed to a registered hook by a signal handler which was
302    /// installed by this crate.
303    pub fn signal(&self) -> Result<Signal, UnsupportedSignalError> {
304        Signal::from_raw(self.signo_raw(), self.signinfo_raw().si_code)
305    }
306
307    /// For SIGBUS signals of subtype `MCEERR_AO` or `MCEERR_AR`, this returns
308    /// an indication the least significant bit of the reported address and
309    /// therefore the extent of the corruption. For example, if a full page is
310    /// corrupted, this returns `log2(sysconf(_SC_PAGESIZE))`.
311    ///
312    /// For all other signals and subtypes, this returns `None`.
313    pub fn addr_lsb(&self) -> Option<c_short> {
314        match self.signal().ok()? {
315            Signal::Bus(SigbusCode::MCEERR_AO | SigbusCode::MCEERR_AR) => unsafe {
316                Some(hwexception_get_addr_lsb(self.signinfo_raw()))
317            },
318            _ => None,
319        }
320    }
321
322    /// For SIGSEGV signals of subtype `BNDERR`, this returns the lower bound of
323    /// the failed bounds check. Otherwise it returns `None`.
324    pub fn lower(&self) -> Option<*mut c_void> {
325        match self.signal().ok()? {
326            Signal::Segv(SigsegvCode::BNDERR) => unsafe {
327                Some(hwexception_get_lower(self.signinfo_raw()))
328            },
329            _ => None,
330        }
331    }
332
333    /// For SIGSEGV signals of subtype `BNDERR`, this returns the upper bound of
334    /// the failed bounds check. Otherwise it returns `None`.
335    pub fn upper(&self) -> Option<*mut c_void> {
336        match self.signal().ok()? {
337            Signal::Segv(SigsegvCode::BNDERR) => unsafe {
338                Some(hwexception_get_upper(self.signinfo_raw()))
339            },
340            _ => None,
341        }
342    }
343
344    /// For SIGSEGV signals of subtype `PKUERR`, this returns the protection key on
345    /// the page table entry (PTE) which caused the exception.
346    pub fn pkey(&self) -> Option<c_int> {
347        match self.signal().ok()? {
348            Signal::Segv(SigsegvCode::PKUERR) => unsafe {
349                Some(hwexception_get_pkey(self.signinfo_raw()))
350            },
351            _ => None,
352        }
353    }
354
355    /// Returns the contents of the stack pointer register at the time of the
356    /// exception.
357    /// 
358    /// Not supported on all platforms. If unsupported, returns null. Adding
359    /// support for new platforms is usually straightforward; please file an
360    /// issue if you want something added.
361    pub fn sp(&self) -> *mut c_void {
362        match &self.0 {
363            ExceptionInfoImpl::Borrowed { context, .. } => unsafe {
364                hwexception_get_sp(*context)
365            }
366            ExceptionInfoImpl::Owned { sp, .. } => *sp,
367        }
368    }
369
370    /// Returns the address of the instruction which triggered the exception.
371    /// 
372    /// Not supported on all platforms. If unsupported, returns null. Adding
373    /// support for new platforms is usually straightforward; please file an
374    /// issue if you want something added.
375    pub fn ip(&self) -> *mut c_void {
376        match &self.0 {
377            ExceptionInfoImpl::Borrowed { context, .. } => unsafe {
378                hwexception_get_ip(*context)
379            }
380            ExceptionInfoImpl::Owned { ip, .. } => *ip,
381        }
382    }
383
384    /// Returns the starting symbol address of the function which triggered the
385    /// exception.
386    /// 
387    /// Not supported on all platforms. If unsupported, returns null. Adding
388    /// support for new platforms is usually straightforward; please file an
389    /// issue if you want something added.
390    pub fn symbol_address(&self) -> *mut c_void {
391        match &self.0 {
392            ExceptionInfoImpl::Borrowed { context, .. } => unsafe {
393                hwexception_get_symbol_address(*context)
394            }
395            ExceptionInfoImpl::Owned { symbol_address, .. } => *symbol_address,
396        }
397    }
398}
399
400#[derive(Debug)]
401/// Exception information plus optional additional payload.
402pub struct ExtExceptionInfo {
403    info: Box<ExceptionInfo<'static>>,
404    additional: Option<Box<dyn Any + 'static>>,
405}
406
407impl ExtExceptionInfo {
408    /// Returns the exception information.
409    pub fn info(&self) -> &ExceptionInfo<'static> {
410        self.info.as_ref()
411    }
412    /// Returns an optional additional payload.
413    pub fn additional(&self) -> Option<&(dyn Any + 'static)> {
414        self.additional.as_ref().map(|b| b.as_ref())
415    }
416}
417
418impl From<ExceptionInfo<'_>> for ExtExceptionInfo {
419    fn from(info: ExceptionInfo<'_>) -> Self {
420        ExtExceptionInfo {
421            info: Box::new(info.into_owned()),
422            additional: None,
423        }
424    }
425}
426
427impl<A> From<(ExceptionInfo<'_>, A)> for ExtExceptionInfo
428where
429    A: 'static,
430{
431    fn from(val: (ExceptionInfo<'_>, A)) -> Self {
432        ExtExceptionInfo {
433            info: Box::new(val.0.into_owned()),
434            additional: Some(Box::new(val.1)),
435        }
436    }
437}