mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-08-25 01:11:16 +00:00
We use cargo vendor to generate a .cargo/config file and the vendor directory. Vendoring allows us to lock our dependencies and to modify them easily from the top level Cargo.toml. We vendor all dependencies, including the crates.io ones, which allows for network isolated builds. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
421 lines
14 KiB
Rust
421 lines
14 KiB
Rust
// Copyright 2019 Intel Corporation. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE-BSD-3-Clause file.
|
|
|
|
use libc::{
|
|
c_int, c_void, pthread_kill, pthread_sigmask, pthread_t, sigaction, sigaddset, sigemptyset,
|
|
siginfo_t, sigismember, sigpending, sigset_t, sigtimedwait, timespec, EAGAIN, EINTR, EINVAL,
|
|
SIGHUP, SIGSYS, SIG_BLOCK, SIG_UNBLOCK,
|
|
};
|
|
|
|
use errno;
|
|
use std::fmt::{self, Display};
|
|
use std::io;
|
|
use std::mem;
|
|
use std::os::unix::thread::JoinHandleExt;
|
|
use std::ptr::{null, null_mut};
|
|
use std::result;
|
|
use std::thread::JoinHandle;
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
/// Couldn't create a sigset.
|
|
CreateSigset(errno::Error),
|
|
/// The wrapped signal has already been blocked.
|
|
SignalAlreadyBlocked(c_int),
|
|
/// Failed to check if the requested signal is in the blocked set already.
|
|
CompareBlockedSignals(errno::Error),
|
|
/// The signal could not be blocked.
|
|
BlockSignal(errno::Error),
|
|
/// The signal mask could not be retrieved.
|
|
RetrieveSignalMask(i32),
|
|
/// The signal could not be unblocked.
|
|
UnblockSignal(errno::Error),
|
|
/// Failed to wait for given signal.
|
|
ClearWaitPending(errno::Error),
|
|
/// Failed to get pending signals.
|
|
ClearGetPending(errno::Error),
|
|
/// Failed to check if given signal is in the set of pending signals.
|
|
ClearCheckPending(errno::Error),
|
|
}
|
|
|
|
impl Display for Error {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
use self::Error::*;
|
|
|
|
match self {
|
|
CreateSigset(e) => write!(f, "couldn't create a sigset: {}", e),
|
|
SignalAlreadyBlocked(num) => write!(f, "signal {} already blocked", num),
|
|
CompareBlockedSignals(e) => write!(
|
|
f,
|
|
"failed to check whether requested signal is in the blocked set: {}",
|
|
e,
|
|
),
|
|
BlockSignal(e) => write!(f, "signal could not be blocked: {}", e),
|
|
RetrieveSignalMask(errno) => write!(
|
|
f,
|
|
"failed to retrieve signal mask: {}",
|
|
io::Error::from_raw_os_error(*errno),
|
|
),
|
|
UnblockSignal(e) => write!(f, "signal could not be unblocked: {}", e),
|
|
ClearWaitPending(e) => write!(f, "failed to wait for given signal: {}", e),
|
|
ClearGetPending(e) => write!(f, "failed to get pending signals: {}", e),
|
|
ClearCheckPending(e) => write!(
|
|
f,
|
|
"failed to check whether given signal is in the pending set: {}",
|
|
e,
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub type SignalResult<T> = result::Result<T, Error>;
|
|
type SiginfoHandler = extern "C" fn(num: c_int, info: *mut siginfo_t, _unused: *mut c_void) -> ();
|
|
|
|
pub enum SignalHandler {
|
|
Siginfo(SiginfoHandler),
|
|
// TODO add a`SimpleHandler` when `libc` adds `sa_handler` support to `sigaction`.
|
|
}
|
|
|
|
impl SignalHandler {
|
|
fn set_flags(act: &mut sigaction, flag: c_int) {
|
|
act.sa_flags = flag;
|
|
}
|
|
}
|
|
/// Fills a `sigaction` structure from of the signal handler.
|
|
/// Refer to http://man7.org/linux/man-pages/man7/signal.7.html
|
|
impl Into<sigaction> for SignalHandler {
|
|
fn into(self) -> sigaction {
|
|
let mut act: sigaction = unsafe { mem::zeroed() };
|
|
match self {
|
|
SignalHandler::Siginfo(function) => {
|
|
act.sa_sigaction = function as *const () as usize;
|
|
}
|
|
}
|
|
act
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
fn __libc_current_sigrtmin() -> c_int;
|
|
fn __libc_current_sigrtmax() -> c_int;
|
|
}
|
|
|
|
/// Returns the minimum (inclusive) real-time signal number.
|
|
#[allow(non_snake_case)]
|
|
fn SIGRTMIN() -> c_int {
|
|
unsafe { __libc_current_sigrtmin() }
|
|
}
|
|
|
|
/// Returns the maximum (inclusive) real-time signal number.
|
|
#[allow(non_snake_case)]
|
|
fn SIGRTMAX() -> c_int {
|
|
unsafe { __libc_current_sigrtmax() }
|
|
}
|
|
|
|
/// Verifies that a signal number is valid: for VCPU signals, it needs to be enclosed within the OS
|
|
/// limits for realtime signals, and the remaining ones need to be between the minimum (SIGHUP) and
|
|
/// maximum (SIGSYS) values.
|
|
pub fn validate_signal_num(num: c_int, for_vcpu: bool) -> errno::Result<c_int> {
|
|
if for_vcpu {
|
|
let actual_num = num + SIGRTMIN();
|
|
if actual_num <= SIGRTMAX() {
|
|
return Ok(actual_num);
|
|
}
|
|
} else if SIGHUP <= num && num <= SIGSYS {
|
|
return Ok(num);
|
|
}
|
|
Err(errno::Error::new(EINVAL))
|
|
}
|
|
|
|
/// Registers `handler` as the signal handler of signum `num`.
|
|
///
|
|
/// Uses `sigaction` to register the handler.
|
|
///
|
|
/// This is considered unsafe because the given handler will be called asynchronously, interrupting
|
|
/// whatever the thread was doing and therefore must only do async-signal-safe operations.
|
|
/// flags: SA_SIGINFO or SA_RESTART if wants to restart after signal received.
|
|
pub unsafe fn register_signal_handler(
|
|
num: i32,
|
|
handler: SignalHandler,
|
|
for_vcpu: bool,
|
|
flag: c_int,
|
|
) -> errno::Result<()> {
|
|
let num = validate_signal_num(num, for_vcpu)?;
|
|
let mut act: sigaction = handler.into();
|
|
SignalHandler::set_flags(&mut act, flag);
|
|
match sigaction(num, &act, null_mut()) {
|
|
0 => Ok(()),
|
|
_ => errno::errno_result(),
|
|
}
|
|
}
|
|
|
|
/// Creates `sigset` from an array of signal numbers.
|
|
///
|
|
/// This is a helper function used when we want to manipulate signals.
|
|
pub fn create_sigset(signals: &[c_int]) -> errno::Result<sigset_t> {
|
|
// sigset will actually be initialized by sigemptyset below.
|
|
let mut sigset: sigset_t = unsafe { mem::zeroed() };
|
|
|
|
// Safe - return value is checked.
|
|
let ret = unsafe { sigemptyset(&mut sigset) };
|
|
if ret < 0 {
|
|
return errno::errno_result();
|
|
}
|
|
|
|
for signal in signals {
|
|
// Safe - return value is checked.
|
|
let ret = unsafe { sigaddset(&mut sigset, *signal) };
|
|
if ret < 0 {
|
|
return errno::errno_result();
|
|
}
|
|
}
|
|
|
|
Ok(sigset)
|
|
}
|
|
|
|
/// Retrieves the signal mask of the current thread as a vector of c_ints.
|
|
pub fn get_blocked_signals() -> SignalResult<Vec<c_int>> {
|
|
let mut mask = Vec::new();
|
|
|
|
// Safe - return values are checked.
|
|
unsafe {
|
|
let mut old_sigset: sigset_t = mem::zeroed();
|
|
let ret = pthread_sigmask(SIG_BLOCK, null(), &mut old_sigset as *mut sigset_t);
|
|
if ret < 0 {
|
|
return Err(Error::RetrieveSignalMask(ret));
|
|
}
|
|
|
|
for num in 0..=SIGRTMAX() {
|
|
if sigismember(&old_sigset, num) > 0 {
|
|
mask.push(num);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(mask)
|
|
}
|
|
|
|
/// Masks given signal.
|
|
///
|
|
/// If signal is already blocked the call will fail with Error::SignalAlreadyBlocked
|
|
/// result.
|
|
pub fn block_signal(num: c_int) -> SignalResult<()> {
|
|
let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
|
|
|
|
// Safe - return values are checked.
|
|
unsafe {
|
|
let mut old_sigset: sigset_t = mem::zeroed();
|
|
let ret = pthread_sigmask(SIG_BLOCK, &sigset, &mut old_sigset as *mut sigset_t);
|
|
if ret < 0 {
|
|
return Err(Error::BlockSignal(errno::Error::last()));
|
|
}
|
|
let ret = sigismember(&old_sigset, num);
|
|
if ret < 0 {
|
|
return Err(Error::CompareBlockedSignals(errno::Error::last()));
|
|
} else if ret > 0 {
|
|
return Err(Error::SignalAlreadyBlocked(num));
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Unmasks given signal.
|
|
pub fn unblock_signal(num: c_int) -> SignalResult<()> {
|
|
let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
|
|
|
|
// Safe - return value is checked.
|
|
let ret = unsafe { pthread_sigmask(SIG_UNBLOCK, &sigset, null_mut()) };
|
|
if ret < 0 {
|
|
return Err(Error::UnblockSignal(errno::Error::last()));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Clears pending signal.
|
|
pub fn clear_signal(num: c_int) -> SignalResult<()> {
|
|
let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
|
|
|
|
while {
|
|
// This is safe as we are rigorously checking return values
|
|
// of libc calls.
|
|
unsafe {
|
|
let mut siginfo: siginfo_t = mem::zeroed();
|
|
let ts = timespec {
|
|
tv_sec: 0,
|
|
tv_nsec: 0,
|
|
};
|
|
// Attempt to consume one instance of pending signal. If signal
|
|
// is not pending, the call will fail with EAGAIN or EINTR.
|
|
let ret = sigtimedwait(&sigset, &mut siginfo, &ts);
|
|
if ret < 0 {
|
|
let e = errno::Error::last();
|
|
match e.errno() {
|
|
EAGAIN | EINTR => {}
|
|
_ => {
|
|
return Err(Error::ClearWaitPending(errno::Error::last()));
|
|
}
|
|
}
|
|
}
|
|
|
|
// This sigset will be actually filled with `sigpending` call.
|
|
let mut chkset: sigset_t = mem::zeroed();
|
|
// See if more instances of the signal are pending.
|
|
let ret = sigpending(&mut chkset);
|
|
if ret < 0 {
|
|
return Err(Error::ClearGetPending(errno::Error::last()));
|
|
}
|
|
|
|
let ret = sigismember(&chkset, num);
|
|
if ret < 0 {
|
|
return Err(Error::ClearCheckPending(errno::Error::last()));
|
|
}
|
|
|
|
// This is do-while loop condition.
|
|
ret != 0
|
|
}
|
|
} {}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Trait for threads that can be signalled via `pthread_kill`.
|
|
///
|
|
/// Note that this is only useful for signals between SIGRTMIN and SIGRTMAX because these are
|
|
/// guaranteed to not be used by the C runtime.
|
|
///
|
|
/// This is marked unsafe because the implementation of this trait must guarantee that the returned
|
|
/// pthread_t is valid and has a lifetime at least that of the trait object.
|
|
pub unsafe trait Killable {
|
|
fn pthread_handle(&self) -> pthread_t;
|
|
|
|
/// Sends the signal `num + SIGRTMIN` to this killable thread.
|
|
///
|
|
/// The value of `num + SIGRTMIN` must not exceed `SIGRTMAX`.
|
|
fn kill(&self, num: i32) -> errno::Result<()> {
|
|
let num = validate_signal_num(num, true)?;
|
|
|
|
// Safe because we ensure we are using a valid pthread handle, a valid signal number, and
|
|
// check the return result.
|
|
let ret = unsafe { pthread_kill(self.pthread_handle(), num) };
|
|
if ret < 0 {
|
|
return errno::errno_result();
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
// Safe because we fulfill our contract of returning a genuine pthread handle.
|
|
unsafe impl<T> Killable for JoinHandle<T> {
|
|
fn pthread_handle(&self) -> pthread_t {
|
|
self.as_pthread_t()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use libc::SA_SIGINFO;
|
|
use std::thread;
|
|
use std::time::Duration;
|
|
|
|
static mut SIGNAL_HANDLER_CALLED: bool = false;
|
|
|
|
extern "C" fn handle_signal(_: c_int, _: *mut siginfo_t, _: *mut c_void) {
|
|
unsafe {
|
|
SIGNAL_HANDLER_CALLED = true;
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_register_signal_handler() {
|
|
unsafe {
|
|
// testing bad value
|
|
assert!(register_signal_handler(
|
|
SIGRTMAX(),
|
|
SignalHandler::Siginfo(handle_signal),
|
|
true,
|
|
SA_SIGINFO
|
|
)
|
|
.is_err());
|
|
format!(
|
|
"{:?}",
|
|
register_signal_handler(
|
|
SIGRTMAX(),
|
|
SignalHandler::Siginfo(handle_signal),
|
|
true,
|
|
SA_SIGINFO
|
|
)
|
|
);
|
|
assert!(register_signal_handler(
|
|
0,
|
|
SignalHandler::Siginfo(handle_signal),
|
|
true,
|
|
SA_SIGINFO
|
|
)
|
|
.is_ok());
|
|
assert!(register_signal_handler(
|
|
libc::SIGSYS,
|
|
SignalHandler::Siginfo(handle_signal),
|
|
false,
|
|
SA_SIGINFO
|
|
)
|
|
.is_ok());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
#[allow(clippy::empty_loop)]
|
|
fn test_killing_thread() {
|
|
let killable = thread::spawn(|| thread::current().id());
|
|
let killable_id = killable.join().unwrap();
|
|
assert_ne!(killable_id, thread::current().id());
|
|
|
|
// We install a signal handler for the specified signal; otherwise the whole process will
|
|
// be brought down when the signal is received, as part of the default behaviour. Signal
|
|
// handlers are global, so we install this before starting the thread.
|
|
unsafe {
|
|
register_signal_handler(0, SignalHandler::Siginfo(handle_signal), true, SA_SIGINFO)
|
|
.expect("failed to register vcpu signal handler");
|
|
}
|
|
|
|
let killable = thread::spawn(|| loop {});
|
|
|
|
let res = killable.kill(SIGRTMAX());
|
|
assert!(res.is_err());
|
|
format!("{:?}", res);
|
|
|
|
unsafe {
|
|
assert!(!SIGNAL_HANDLER_CALLED);
|
|
}
|
|
|
|
assert!(killable.kill(0).is_ok());
|
|
|
|
// We're waiting to detect that the signal handler has been called.
|
|
const MAX_WAIT_ITERS: u32 = 20;
|
|
let mut iter_count = 0;
|
|
loop {
|
|
thread::sleep(Duration::from_millis(100));
|
|
|
|
if unsafe { SIGNAL_HANDLER_CALLED } {
|
|
break;
|
|
}
|
|
|
|
iter_count += 1;
|
|
// timeout if we wait too long
|
|
assert!(iter_count <= MAX_WAIT_ITERS);
|
|
}
|
|
|
|
// Our signal handler doesn't do anything which influences the killable thread, so the
|
|
// previous signal is effectively ignored. If we were to join killable here, we would block
|
|
// forever as the loop keeps running. Since we don't join, the thread will become detached
|
|
// as the handle is dropped, and will be killed when the process/main thread exits.
|
|
}
|
|
}
|