summaryrefslogtreecommitdiffhomepage
path: root/mullvad-daemon/src/exception_logging/unix.rs
blob: 36d1f679dd015eeea6777fdf7da146cb6cd90289 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! Installs signal handlers to catch critical program faults and logs them.

use libc::siginfo_t;
use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal};

use std::{
    ffi::{c_int, c_void},
    sync::Once,
};

static INIT_ONCE: Once = Once::new();

const FAULT_SIGNALS: [Signal; 5] = [
    // Access to invalid memory address
    Signal::SIGBUS,
    // Floating point exception
    Signal::SIGFPE,
    // Illegal instructors
    Signal::SIGILL,
    // Invalid memory reference
    Signal::SIGSEGV,
    // Bad syscall
    Signal::SIGSYS,
];

/// Installs a signal handler.
pub fn enable() {
    INIT_ONCE.call_once(|| {
        // Setup alt stack for signal handlers to be executed in.
        // If the daemon ever needs to be compiled for architectures where memory can't be writeable
        // and executable, the following block of code has to be disabled. This will also mean that
        // stack overflows may be silent and undetectable in logs.
        let sig_handler_flags = {
            // The kernel will use the first properly aligned address, so alignment is not an issue.
            let alt_stack = vec![0u8; libc::SIGSTKSZ];
            let stack_t = libc::stack_t {
                ss_sp: alt_stack.as_ptr() as *mut c_void,
                ss_flags: 0,
                ss_size: alt_stack.len(),
            };
            let ret = unsafe { libc::sigaltstack(&stack_t, std::ptr::null_mut()) };
            if ret != 0 {
                log::error!(
                    "Failed to set alternative stack: {}",
                    std::io::Error::last_os_error()
                );
                SaFlags::empty()
            } else {
                std::mem::forget(alt_stack);
                SaFlags::SA_ONSTACK
            }
        };

        let signal_action = SigAction::new(
            SigHandler::SigAction(fault_handler),
            sig_handler_flags,
            SigSet::empty(),
        );

        for signal in &FAULT_SIGNALS {
            if let Err(err) = unsafe { sigaction(*signal, &signal_action) } {
                log::error!("Failed to install signal handler for {}: {}", signal, err);
            }
        }
    });
}

/// Signal handler to catch signals that are used to indicate unrecoverable errors in the daemon
extern "C" fn fault_handler(
    signum: c_int,
    _siginfo: *mut siginfo_t,
    _thread_context_ptr: *mut c_void,
) {
    let signal: Signal = match Signal::try_from(signum) {
        Ok(signal) => signal,
        Err(err) => {
            log::error!(
                "Signal handler triggered by unknown signal {}, exiting: {}",
                signum,
                err
            );
            std::process::exit(2);
        }
    };

    log::error!("Caught signal {}", signal);
    std::process::exit(2);
}