Skip to content

Commit 9888c00

Browse files
author
Alexandra Iordache
committed
vmm/sys_util: refactor signal handling
* added a single function in vmm that installs all relevant signal handlers; * left signal handler functions in vmm (currently only for SIGSYS); * moved signal handling logic to sys_util; * split sys_util's register_signal_handler into 2 separate functions, one to be called exclusively for vCPUs and a second general purpose one. The vCPU-specific one doen't change the signal mask and alters the signal number, forcing it between SIGRTMIN and SIGRTMAX. The general purpose one is setup_sigsys_handler renamed, and will morph into a generic one in a following commit. Signed-off-by: Alexandra Iordache <[email protected]>
1 parent be6fbb2 commit 9888c00

File tree

5 files changed

+64
-110
lines changed

5 files changed

+64
-110
lines changed

src/main.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use api_server::{ApiServer, Error};
3030
use fc_util::validators::validate_instance_id;
3131
use logger::{Metric, LOGGER, METRICS};
3232
use mmds::MMDS;
33+
use vmm::signal_handler::register_signal_handlers;
3334
use vmm::vmm_config::instance_info::{InstanceInfo, InstanceState};
3435

3536
const DEFAULT_API_SOCK_PATH: &str = "/tmp/firecracker.socket";
@@ -40,11 +41,10 @@ fn main() {
4041
.preinit(Some(DEFAULT_INSTANCE_ID.to_string()))
4142
.expect("Failed to register logger");
4243

43-
if let Err(e) = vmm::setup_sigsys_handler() {
44-
error!("Failed to register signal handler: {}", e);
44+
if let Err(e) = register_signal_handlers() {
45+
error!("Failed to register signal handlers: {}", e);
4546
process::exit(i32::from(vmm::FC_EXIT_CODE_GENERIC_ERROR));
4647
}
47-
4848
// Start firecracker by setting up a panic hook, which will be called before
4949
// terminating as we're building with panic = "abort".
5050
// It's worth noting that the abort is caused by sending a SIG_ABORT signal to the process.

sys_util/src/signal.rs

+43-66
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,17 @@
77

88
use super::SyscallReturnCode;
99
use libc::{
10-
c_int, c_void, pthread_kill, pthread_t, sigaction, siginfo_t, EINVAL, SA_SIGINFO, SIGHUP,
11-
SIGSYS,
10+
c_int, c_void, pthread_kill, pthread_t, sigaction, sigfillset, siginfo_t, sigset_t, EINVAL,
1211
};
1312
use std::io;
1413
use std::mem;
1514
use std::os::unix::thread::JoinHandleExt;
15+
use std::ptr::null_mut;
1616
use std::thread::JoinHandle;
1717

18-
type SiginfoHandler = extern "C" fn(num: c_int, info: *mut siginfo_t, _unused: *mut c_void) -> ();
19-
20-
pub enum SignalHandler {
21-
Siginfo(SiginfoHandler),
22-
// TODO add a`SimpleHandler` when `libc` adds `sa_handler` support to `sigaction`.
23-
}
24-
25-
/// Fills a `sigaction` structure from of the signal handler.
26-
impl Into<sigaction> for SignalHandler {
27-
fn into(self) -> sigaction {
28-
let mut act: sigaction = unsafe { mem::zeroed() };
29-
match self {
30-
SignalHandler::Siginfo(function) => {
31-
act.sa_flags = SA_SIGINFO;
32-
act.sa_sigaction = function as *const () as usize;
33-
}
34-
}
35-
act
36-
}
37-
}
18+
/// Type that represents a signal handler function.
19+
pub type SignalHandler =
20+
extern "C" fn(num: c_int, info: *mut siginfo_t, _unused: *mut c_void) -> ();
3821

3922
extern "C" {
4023
fn __libc_current_sigrtmin() -> c_int;
@@ -55,36 +38,47 @@ fn SIGRTMAX() -> c_int {
5538

5639
/// Verifies that a signal number is valid.
5740
///
58-
/// VCPU signals need to have values enclosed within the OS limits for realtime signals,
59-
/// and the remaining ones need to be between the minimum (SIGHUP) and maximum (SIGSYS) values.
41+
/// VCPU signals need to have values enclosed within the OS limits for realtime signals.
6042
///
6143
/// Will return Ok(num) or Err(EINVAL).
62-
pub fn validate_signal_num(num: c_int, for_vcpu: bool) -> io::Result<c_int> {
63-
if for_vcpu {
64-
let actual_num = num + SIGRTMIN();
65-
if actual_num <= SIGRTMAX() {
66-
return Ok(actual_num);
67-
}
68-
} else if SIGHUP <= num && num <= SIGSYS {
69-
return Ok(num);
44+
pub fn validate_vcpu_signal_num(num: c_int) -> io::Result<c_int> {
45+
let actual_num = num + SIGRTMIN();
46+
if actual_num <= SIGRTMAX() {
47+
Ok(actual_num)
48+
} else {
49+
Err(io::Error::from_raw_os_error(EINVAL))
7050
}
71-
Err(io::Error::from_raw_os_error(EINVAL))
7251
}
7352

74-
/// Registers `handler` as the signal handler of signum `num`.
75-
///
76-
/// Uses `sigaction` to register the handler.
53+
/// Registers `handler` as the vCPU's signal handler of signum `num`.
7754
///
7855
/// This is considered unsafe because the given handler will be called asynchronously, interrupting
7956
/// whatever the thread was doing and therefore must only do async-signal-safe operations.
80-
pub unsafe fn register_signal_handler(
81-
num: i32,
82-
handler: SignalHandler,
83-
for_vcpu: bool,
84-
) -> io::Result<()> {
85-
let num = validate_signal_num(num, for_vcpu)?;
86-
let act: sigaction = handler.into();
87-
SyscallReturnCode(sigaction(num, &act, ::std::ptr::null_mut())).into_empty_result()
57+
pub unsafe fn register_vcpu_signal_handler(num: i32, handler: SignalHandler) -> io::Result<()> {
58+
let num = validate_vcpu_signal_num(num)?;
59+
// Safe, because this is a POD struct.
60+
let mut sigact: sigaction = mem::zeroed();
61+
sigact.sa_flags = libc::SA_SIGINFO;
62+
sigact.sa_sigaction = handler as usize;
63+
SyscallReturnCode(sigaction(num, &sigact, null_mut())).into_empty_result()
64+
}
65+
66+
/// Registers the specified signal handler for `SIGSYS`.
67+
pub fn register_sigsys_handler(sigsys_handler: SignalHandler) -> Result<(), io::Error> {
68+
// Safe, because this is a POD struct.
69+
let mut sigact: sigaction = unsafe { mem::zeroed() };
70+
sigact.sa_flags = libc::SA_SIGINFO;
71+
sigact.sa_sigaction = sigsys_handler as usize;
72+
73+
// We set all the bits of sa_mask, so all signals are blocked on the current thread while the
74+
// SIGSYS handler is executing. Safe because the parameter is valid and we check the return
75+
// value.
76+
if unsafe { sigfillset(&mut sigact.sa_mask as *mut sigset_t) } < 0 {
77+
return Err(io::Error::last_os_error());
78+
}
79+
80+
// Safe because the parameters are valid and we check the return value.
81+
unsafe { SyscallReturnCode(sigaction(libc::SIGSYS, &sigact, null_mut())).into_empty_result() }
8882
}
8983

9084
/// Trait for threads that can be signalled via `pthread_kill`.
@@ -101,7 +95,7 @@ pub unsafe trait Killable {
10195
///
10296
/// The value of `num + SIGRTMIN` must not exceed `SIGRTMAX`.
10397
fn kill(&self, num: i32) -> io::Result<()> {
104-
let num = validate_signal_num(num, true)?;
98+
let num = validate_vcpu_signal_num(num)?;
10599

106100
// Safe because we ensure we are using a valid pthread handle, a valid signal number, and
107101
// check the return result.
@@ -119,7 +113,6 @@ unsafe impl<T> Killable for JoinHandle<T> {
119113
#[cfg(test)]
120114
mod tests {
121115
use super::*;
122-
use libc;
123116
use std::thread;
124117
use std::time::Duration;
125118

@@ -135,25 +128,9 @@ mod tests {
135128
fn test_register_signal_handler() {
136129
unsafe {
137130
// testing bad value
138-
assert!(register_signal_handler(
139-
SIGRTMAX(),
140-
SignalHandler::Siginfo(handle_signal),
141-
true
142-
)
143-
.is_err());
144-
format!(
145-
"{:?}",
146-
register_signal_handler(SIGRTMAX(), SignalHandler::Siginfo(handle_signal), true)
147-
);
148-
assert!(
149-
register_signal_handler(0, SignalHandler::Siginfo(handle_signal), true).is_ok()
150-
);
151-
assert!(register_signal_handler(
152-
libc::SIGSYS,
153-
SignalHandler::Siginfo(handle_signal),
154-
false
155-
)
156-
.is_ok());
131+
assert!(register_vcpu_signal_handler(SIGRTMAX(), handle_signal).is_err());
132+
assert!(register_vcpu_signal_handler(0, handle_signal).is_ok());
133+
assert!(register_sigsys_handler(handle_signal).is_ok());
157134
}
158135
}
159136

@@ -168,7 +145,7 @@ mod tests {
168145
// be brought down when the signal is received, as part of the default behaviour. Signal
169146
// handlers are global, so we install this before starting the thread.
170147
unsafe {
171-
register_signal_handler(0, SignalHandler::Siginfo(handle_signal), true)
148+
register_vcpu_signal_handler(0, handle_signal)
172149
.expect("failed to register vcpu signal handler");
173150
}
174151

vmm/src/lib.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ extern crate sys_util;
4040
pub mod default_syscalls;
4141
mod device_manager;
4242
/// Signal handling utilities.
43-
mod signal_handler;
43+
pub mod signal_handler;
4444
/// Wrappers over structures used to configure the VMM.
4545
pub mod vmm_config;
4646
mod vstate;
@@ -77,7 +77,6 @@ use memory_model::{GuestAddress, GuestMemory};
7777
use net_util::TapError;
7878
#[cfg(target_arch = "aarch64")]
7979
use serde_json::Value;
80-
pub use signal_handler::setup_sigsys_handler;
8180
use sys_util::{EventFd, Terminal};
8281
use vmm_config::boot_source::{BootSourceConfig, BootSourceConfigError};
8382
use vmm_config::drive::{BlockDeviceConfig, BlockDeviceConfigs, DriveError};

vmm/src/signal_handler.rs

+15-37
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@ extern crate logger;
55
extern crate sys_util;
66

77
use std::io;
8-
use std::mem;
9-
use std::ptr::null_mut;
108
use std::result::Result;
119

12-
use libc::{sigaction, sigfillset, sigset_t};
10+
use libc::{_exit, c_int, c_void, siginfo_t, SIGSYS};
1311

1412
use logger::{Metric, LOGGER, METRICS};
13+
use sys_util::register_sigsys_handler;
1514

1615
// The offset of `si_syscall` (offending syscall identifier) within the siginfo structure
1716
// expressed as an `(u)int*`.
@@ -22,44 +21,15 @@ const SI_OFF_SYSCALL: isize = 6;
2221

2322
const SYS_SECCOMP_CODE: i32 = 1;
2423

25-
// This no longer relies on sys_util::register_signal_handler(), which is a lot weirder than it
26-
// should be (at least for this use case). Also, we want to change the sa_mask field of the
27-
// sigaction struct.
28-
/// Sets up the specified signal handler for `SIGSYS`.
29-
pub fn setup_sigsys_handler() -> Result<(), io::Error> {
30-
// Safe, because this is a POD struct.
31-
let mut sigact: sigaction = unsafe { mem::zeroed() };
32-
sigact.sa_flags = libc::SA_SIGINFO;
33-
sigact.sa_sigaction = sigsys_handler as usize;
34-
35-
// We set all the bits of sa_mask, so all signals are blocked on the current thread while the
36-
// SIGSYS handler is executing. Safe because the parameter is valid and we check the return
37-
// value.
38-
if unsafe { sigfillset(&mut sigact.sa_mask as *mut sigset_t) } < 0 {
39-
return Err(io::Error::last_os_error());
40-
}
41-
42-
// Safe because the parameters are valid and we check the return value.
43-
if unsafe { sigaction(libc::SIGSYS, &sigact, null_mut()) } < 0 {
44-
return Err(io::Error::last_os_error());
45-
}
46-
47-
Ok(())
48-
}
49-
50-
extern "C" fn sigsys_handler(
51-
num: libc::c_int,
52-
info: *mut libc::siginfo_t,
53-
_unused: *mut libc::c_void,
54-
) {
24+
extern "C" fn sigsys_handler(num: c_int, info: *mut siginfo_t, _unused: *mut c_void) {
5525
// Safe because we're just reading some fields from a supposedly valid argument.
5626
let si_signo = unsafe { (*info).si_signo };
5727
let si_code = unsafe { (*info).si_code };
5828

5929
// Sanity check. The condition should never be true.
60-
if num != si_signo || num != libc::SIGSYS || si_code != SYS_SECCOMP_CODE as i32 {
30+
if num != si_signo || num != SIGSYS || si_code != SYS_SECCOMP_CODE as i32 {
6131
// Safe because we're terminating the process anyway.
62-
unsafe { libc::_exit(i32::from(super::FC_EXIT_CODE_UNEXPECTED_ERROR)) };
32+
unsafe { _exit(i32::from(super::FC_EXIT_CODE_UNEXPECTED_ERROR)) };
6333
}
6434

6535
// Other signals which might do async unsafe things incompatible with the rest of this
@@ -79,10 +49,18 @@ extern "C" fn sigsys_handler(
7949
// running unit tests.
8050
#[cfg(not(test))]
8151
unsafe {
82-
libc::_exit(i32::from(super::FC_EXIT_CODE_BAD_SYSCALL))
52+
_exit(i32::from(super::FC_EXIT_CODE_BAD_SYSCALL))
8353
};
8454
}
8555

56+
/// Registers all the required signal handlers.
57+
///
58+
/// Custom handlers are installed for: `SIGSYS`.
59+
///
60+
pub fn register_signal_handlers() -> Result<(), io::Error> {
61+
register_sigsys_handler(sigsys_handler)
62+
}
63+
8664
#[cfg(test)]
8765
mod tests {
8866
use super::*;
@@ -120,7 +98,7 @@ mod tests {
12098

12199
#[test]
122100
fn test_signal_handler() {
123-
assert!(setup_sigsys_handler().is_ok());
101+
assert!(register_signal_handlers().is_ok());
124102

125103
let filter = SeccompFilter::new(
126104
vec![

vmm/src/vstate.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ mod tests {
464464
use super::*;
465465

466466
use libc::{c_int, c_void, siginfo_t};
467-
use sys_util::{register_signal_handler, Killable, SignalHandler};
467+
use sys_util::{register_vcpu_signal_handler, Killable};
468468

469469
// Auxiliary function being used throughout the tests.
470470
fn setup_vcpu() -> (Vm, Vcpu) {
@@ -704,7 +704,7 @@ mod tests {
704704
// be brought down when the signal is received, as part of the default behaviour. Signal
705705
// handlers are global, so we install this before starting the thread.
706706
unsafe {
707-
register_signal_handler(signum, SignalHandler::Siginfo(handle_signal), true)
707+
register_vcpu_signal_handler(signum, handle_signal)
708708
.expect("failed to register vcpu signal handler");
709709
}
710710

0 commit comments

Comments
 (0)