forked from firecracker-microvm/firecracker
-
Notifications
You must be signed in to change notification settings - Fork 0
/
signal.rs
230 lines (207 loc) · 7.82 KB
/
signal.rs
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
extern crate logger;
extern crate sys_util;
use std::io;
use std::mem;
use std::ptr::null_mut;
use std::result::Result;
use libc::{
_exit, c_int, c_uint, c_void, sigaction, sigfillset, siginfo_t, sigset_t, SA_SIGINFO, SIGBUS,
SIGSEGV,
};
use logger::LOGGER;
type SiginfoHandler = extern "C" fn(num: c_int, info: *mut siginfo_t, _unused: *mut c_void) -> ();
// `SIGBUS` codes.
// Hardware memory error consumed on a machine check: action required.
const BUS_MCEERR_AR: c_int = 4;
// Hardware memory error detected in process but not consumed: action optional.
const BUS_MCEERR_AO: c_int = 5;
// `SIGSEGV` codes.
// Failed address bound checks.
const SEGV_BNDERR: c_int = 3;
// Failed protection key checks.
const SEGV_PKUERR: c_int = 4;
/// Address bounds at time of fault.
/// See https://elixir.bootlin.com/linux/v4.14/source/include/uapi/asm-generic/siginfo.h
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct _si_addr_bnd_t {
/// Lower bound.
pub(crate) _lower: *const c_void,
/// Upper bound.
pub(crate) _upper: *const c_void,
}
/// Representation of the fault stats for different `SIGSEGV` codes.
/// See https://elixir.bootlin.com/linux/v4.14/source/include/uapi/asm-generic/siginfo.h
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) union _si_fault_t {
/// Used when `si_code` == `SEGV_BNDERR`.
pub(crate) _addr_bnd: _si_addr_bnd_t,
/// Used when `si_code` == `SEGV_PKUERR`.
pub(crate) _pkey: c_uint,
}
/// Stats filled in for `SIGILL`, `SIGFPE`, `SIGSEGV`, `SIGBUS`.
/// See https://elixir.bootlin.com/linux/v4.14/source/include/uapi/asm-generic/siginfo.h
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct _sigfault_t {
/// Faulting instruction / memory reference address.
pub(crate) _si_addr: *const c_void,
/// Valid LSB of the reported address.
pub(crate) _si_addr_lsb: i16,
/// Fault stats.
pub(crate) _si_stats: _si_fault_t,
}
/// Stats filled in for `SIGSYS`.
/// See https://elixir.bootlin.com/linux/v4.14/source/include/uapi/asm-generic/siginfo.h
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct _sigsys_t {
/// Call address.
pub(crate) _call_addr: *const c_void,
/// Offending syscall number.
pub(crate) _syscall: c_int,
/// Architecture identifier.
pub(crate) _arch: c_uint,
}
/// Union of possible additional stats returned in the `siginfo` struct.
/// See https://elixir.bootlin.com/linux/v4.14/source/include/uapi/asm-generic/siginfo.h
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) union _si_fields_t {
/// Fault info. Filled in for `SIGSEGV`, `SIGBUS`, `SIGILL`, `SIGFPE`.
pub(crate) _si_sigfault: _sigfault_t,
/// `SIGSYS` info. Filled in for seccomp faults.
pub(crate) _si_sigsys: _sigsys_t,
/// Padding.
_pad: [c_int; 29],
}
/// Representation of the `siginfo` struct with its relevant fields.
/// See https://elixir.bootlin.com/linux/v4.14/source/include/uapi/asm-generic/siginfo.h
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct _si_siginfo_t {
/// Signal number.
pub(crate) si_signo: c_int,
/// If non-zero, errno value associated with the signal.
pub(crate) si_errno: c_int,
/// Signal code.
pub(crate) si_code: c_int,
/// Additional fields.
pub(crate) si_fields: _si_fields_t,
}
/// Sets up the specified handler for the signals.
///
/// # Arguments
///
/// * `signals` - vector of signals to be handled.
/// * `handler` - signal handler function.
///
pub(crate) fn setup_signal_handler(
signals: &Vec<c_int>,
handler: SiginfoHandler,
) -> Result<(), io::Error> {
// Safe, because this is a POD struct.
let mut sigact: sigaction = unsafe { mem::zeroed() };
sigact.sa_flags = SA_SIGINFO;
sigact.sa_sigaction = handler as usize;
// We set all the bits of sa_mask, so all signals are blocked on the current thread while the
// signal handler is executing. Safe because the parameter is valid and we check the return
// value.
if unsafe { sigfillset(&mut sigact.sa_mask as *mut sigset_t) } < 0 {
return Err(io::Error::last_os_error());
}
for signal in signals.iter() {
// Safe because the parameters are valid and we check the return value.
if unsafe { sigaction(*signal, &sigact, null_mut()) } < 0 {
return Err(io::Error::last_os_error());
}
}
Ok(())
}
/// Handles `SIGBUS` and `SIGSEGV`.
///
/// Logs all the available information on the fault that occurred and exits the process.
///
/// # Arguments
///
/// * `num` - signal number.
/// * `info` - signal information filled in by the kernel.
/// * `c_void` - signal context. Unused.
///
pub(crate) extern "C" fn sigbus_sigsegv_handler(
num: c_int,
info: *mut siginfo_t,
_unused: *mut c_void,
) {
let siginfo = info as *mut _si_siginfo_t;
// Safe because we dereference a valid value.
let si_signo = unsafe { (*siginfo).si_signo };
let si_code = unsafe { (*siginfo).si_code };
// Sanity check. The condition should never be true.
if num != si_signo || (num != SIGBUS && num != SIGSEGV) {
// Safe because we're terminating the process anyway.
unsafe { _exit(i32::from(super::FC_EXIT_CODE_UNEXPECTED_ERROR)) };
}
// `SIGSEGV` and `SIGBUS` fill in `si_addr` with the address of the fault.
// http://man7.org/linux/man-pages/man2/sigaction.2.html
// Safe because we dereference a valid value.
let si_addr = unsafe { (*siginfo).si_fields._si_sigfault._si_addr as usize };
error!(
"Caught signal {}. Code: {}. Fault address: {:x?}",
si_signo, si_code, si_addr
);
match si_signo {
SIGBUS => {
match si_code {
// `BUS_MCEERR_AO` and `BUS_MCEERR_AR` also fill in `si_addr_lsb`.
// This field indicates the LSB of the reported address and therefore the extent of
// the corruption. For example, if a full page was corrupted, `si_addr_lsb` contains
// `log2(sysconf(_SC_PAGESIZE))`.
BUS_MCEERR_AO | BUS_MCEERR_AR => {
// Safe because we dereference a valid value.
let si_addr_lsb = unsafe { (*siginfo).si_fields._si_sigfault._si_addr_lsb };
error!("LSB of the reported address: {:x?}", si_addr_lsb);
}
_ => (),
}
}
SIGSEGV => {
match si_code {
SEGV_BNDERR => {
// The `SEGV_BNDERR` suberror of `SIGSEGV` populates `si_lower` and `si_upper`.
// Safe because we dereference a valid value.
let addr_bnd = unsafe { (*siginfo).si_fields._si_sigfault._si_stats._addr_bnd };
error!(
"Failed address bound checks. Bounds: lower {:x?} upper {:x?}",
addr_bnd._lower, addr_bnd._upper
);
}
SEGV_PKUERR => {
// The `SEGV_PKUERR` suberror of `SIGSEGV` populates `si_pkey`.
// Safe because we dereference a valid value.
let pkey = unsafe { (*siginfo).si_fields._si_sigfault._si_stats._pkey };
error!("Failed protection key checks: {}", pkey);
}
_ => (),
}
}
_ => (),
}
// Log the metrics before exiting.
if let Err(e) = LOGGER.log_metrics() {
error!("Failed to log metrics while stopping: {}", e);
}
// Safe because we're terminating the process anyway. We don't actually do anything when
// running unit tests.
#[cfg(not(test))]
unsafe {
_exit(i32::from(match si_signo {
SIGBUS => super::FC_EXIT_CODE_SIGBUS,
SIGSEGV => super::FC_EXIT_CODE_SIGSEGV,
_ => super::FC_EXIT_CODE_UNEXPECTED_ERROR,
}))
};
}