vmm/
signal_handler.rs

1// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use libc::{
5    SIGBUS, SIGHUP, SIGILL, SIGPIPE, SIGSEGV, SIGSYS, SIGXCPU, SIGXFSZ, c_int, c_void, siginfo_t,
6};
7use log::error;
8
9use crate::FcExitCode;
10use crate::logger::{IncMetric, METRICS, StoreMetric};
11use crate::utils::signal::register_signal_handler;
12
13// The offset of `si_syscall` (offending syscall identifier) within the siginfo structure
14// expressed as an `(u)int*`.
15// Offset `6` for an `i32` field means that the needed information is located at `6 * sizeof(i32)`.
16// See /usr/include/linux/signal.h for the C struct definition.
17// See https://github.com/rust-lang/libc/issues/716 for why the offset is different in Rust.
18const SI_OFF_SYSCALL: isize = 6;
19
20const SYS_SECCOMP_CODE: i32 = 1;
21
22#[inline]
23fn exit_with_code(exit_code: FcExitCode) {
24    // Write the metrics before exiting.
25    if let Err(err) = METRICS.write() {
26        error!("Failed to write metrics while stopping: {}", err);
27    }
28    // SAFETY: Safe because we're terminating the process anyway.
29    unsafe { libc::_exit(exit_code as i32) };
30}
31
32macro_rules! generate_handler {
33    ($fn_name:ident ,$signal_name:ident, $exit_code:ident, $signal_metric:expr, $body:ident) => {
34        #[inline(always)]
35        extern "C" fn $fn_name(num: c_int, info: *mut siginfo_t, _unused: *mut c_void) {
36            // SAFETY: Safe because we're just reading some fields from a supposedly valid argument.
37            let si_signo = unsafe { (*info).si_signo };
38            // SAFETY: Safe because we're just reading some fields from a supposedly valid argument.
39            let si_code = unsafe { (*info).si_code };
40
41            if num != si_signo || num != $signal_name {
42                exit_with_code(FcExitCode::UnexpectedError);
43            }
44            $signal_metric.store(1);
45
46            error!(
47                "Shutting down VM after intercepting signal {}, code {}.",
48                si_signo, si_code
49            );
50
51            $body(si_code, info);
52
53            match si_signo {
54                $signal_name => exit_with_code(crate::FcExitCode::$exit_code),
55                _ => exit_with_code(FcExitCode::UnexpectedError),
56            };
57        }
58    };
59}
60
61fn log_sigsys_err(si_code: c_int, info: *mut siginfo_t) {
62    if si_code != SYS_SECCOMP_CODE {
63        // We received a SIGSYS for a reason other than `bad syscall`.
64        exit_with_code(FcExitCode::UnexpectedError);
65    }
66
67    // SAFETY: Other signals which might do async unsafe things incompatible with the rest of this
68    // function are blocked due to the sa_mask used when registering the signal handler.
69    let syscall = unsafe { *(info as *const i32).offset(SI_OFF_SYSCALL) };
70    error!(
71        "Shutting down VM after intercepting a bad syscall ({}).",
72        syscall
73    );
74}
75
76fn empty_fn(_si_code: c_int, _info: *mut siginfo_t) {}
77
78generate_handler!(
79    sigxfsz_handler,
80    SIGXFSZ,
81    SIGXFSZ,
82    METRICS.signals.sigxfsz,
83    empty_fn
84);
85
86generate_handler!(
87    sigxcpu_handler,
88    SIGXCPU,
89    SIGXCPU,
90    METRICS.signals.sigxcpu,
91    empty_fn
92);
93
94generate_handler!(
95    sigbus_handler,
96    SIGBUS,
97    SIGBUS,
98    METRICS.signals.sigbus,
99    empty_fn
100);
101
102generate_handler!(
103    sigsegv_handler,
104    SIGSEGV,
105    SIGSEGV,
106    METRICS.signals.sigsegv,
107    empty_fn
108);
109
110generate_handler!(
111    sigsys_handler,
112    SIGSYS,
113    BadSyscall,
114    METRICS.seccomp.num_faults,
115    log_sigsys_err
116);
117
118generate_handler!(
119    sighup_handler,
120    SIGHUP,
121    SIGHUP,
122    METRICS.signals.sighup,
123    empty_fn
124);
125generate_handler!(
126    sigill_handler,
127    SIGILL,
128    SIGILL,
129    METRICS.signals.sigill,
130    empty_fn
131);
132
133#[inline(always)]
134extern "C" fn sigpipe_handler(num: c_int, info: *mut siginfo_t, _unused: *mut c_void) {
135    // Just record the metric and allow the process to continue, the EPIPE error needs
136    // to be handled at caller level.
137
138    // SAFETY: Safe because we're just reading some fields from a supposedly valid argument.
139    let si_signo = unsafe { (*info).si_signo };
140    // SAFETY: Safe because we're just reading some fields from a supposedly valid argument.
141    let si_code = unsafe { (*info).si_code };
142
143    if num != si_signo || num != SIGPIPE {
144        error!("Received invalid signal {}, code {}.", si_signo, si_code);
145        return;
146    }
147
148    METRICS.signals.sigpipe.inc();
149
150    error!("Received signal {}, code {}.", si_signo, si_code);
151}
152
153/// Registers all the required signal handlers.
154///
155/// Custom handlers are installed for: `SIGBUS`, `SIGSEGV`, `SIGSYS`
156/// `SIGXFSZ` `SIGXCPU` `SIGPIPE` `SIGHUP` and `SIGILL`.
157pub fn register_signal_handlers() -> vmm_sys_util::errno::Result<()> {
158    // Call to unsafe register_signal_handler which is considered unsafe because it will
159    // register a signal handler which will be called in the current thread and will interrupt
160    // whatever work is done on the current thread, so we have to keep in mind that the registered
161    // signal handler must only do async-signal-safe operations.
162    register_signal_handler(SIGSYS, sigsys_handler)?;
163    register_signal_handler(SIGBUS, sigbus_handler)?;
164    register_signal_handler(SIGSEGV, sigsegv_handler)?;
165    register_signal_handler(SIGXFSZ, sigxfsz_handler)?;
166    register_signal_handler(SIGXCPU, sigxcpu_handler)?;
167    register_signal_handler(SIGPIPE, sigpipe_handler)?;
168    register_signal_handler(SIGHUP, sighup_handler)?;
169    register_signal_handler(SIGILL, sigill_handler)?;
170    Ok(())
171}