// 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 = result::Result; 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 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 { 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 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> { 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 Killable for JoinHandle { 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. } }