vmm_sys_util/linux/
signal.rs

1// Copyright 2019 Intel Corporation. All Rights Reserved.
2//
3// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4//
5// Copyright 2017 The Chromium OS Authors. All rights reserved.
6//
7// SPDX-License-Identifier: BSD-3-Clause
8
9//! Enums, traits and functions for working with
10//! [`signal`](http://man7.org/linux/man-pages/man7/signal.7.html).
11
12use libc::{
13    c_int, c_void, pthread_kill, pthread_sigmask, pthread_t, sigaction, sigaddset, sigemptyset,
14    sigfillset, siginfo_t, sigismember, sigpending, sigset_t, sigtimedwait, timespec, EAGAIN,
15    EINTR, EINVAL, SIG_BLOCK, SIG_UNBLOCK,
16};
17
18use crate::errno;
19use std::fmt::{self, Display};
20use std::io;
21use std::mem;
22use std::os::unix::thread::JoinHandleExt;
23use std::ptr::{null, null_mut};
24use std::result;
25use std::thread::JoinHandle;
26
27/// The error cases enumeration for signal handling.
28#[derive(Debug, PartialEq, Eq)]
29pub enum Error {
30    /// Couldn't create a sigset.
31    CreateSigset(errno::Error),
32    /// The wrapped signal has already been blocked.
33    SignalAlreadyBlocked(c_int),
34    /// Failed to check if the requested signal is in the blocked set already.
35    CompareBlockedSignals(errno::Error),
36    /// The signal could not be blocked.
37    BlockSignal(errno::Error),
38    /// The signal mask could not be retrieved.
39    RetrieveSignalMask(c_int),
40    /// The signal could not be unblocked.
41    UnblockSignal(errno::Error),
42    /// Failed to wait for given signal.
43    ClearWaitPending(errno::Error),
44    /// Failed to get pending signals.
45    ClearGetPending(errno::Error),
46    /// Failed to check if given signal is in the set of pending signals.
47    ClearCheckPending(errno::Error),
48}
49
50impl Display for Error {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        use self::Error::*;
53
54        match self {
55            CreateSigset(e) => write!(f, "couldn't create a sigset: {}", e),
56            SignalAlreadyBlocked(num) => write!(f, "signal {} already blocked", num),
57            CompareBlockedSignals(e) => write!(
58                f,
59                "failed to check whether requested signal is in the blocked set: {}",
60                e,
61            ),
62            BlockSignal(e) => write!(f, "signal could not be blocked: {}", e),
63            RetrieveSignalMask(errno) => write!(
64                f,
65                "failed to retrieve signal mask: {}",
66                io::Error::from_raw_os_error(*errno),
67            ),
68            UnblockSignal(e) => write!(f, "signal could not be unblocked: {}", e),
69            ClearWaitPending(e) => write!(f, "failed to wait for given signal: {}", e),
70            ClearGetPending(e) => write!(f, "failed to get pending signals: {}", e),
71            ClearCheckPending(e) => write!(
72                f,
73                "failed to check whether given signal is in the pending set: {}",
74                e,
75            ),
76        }
77    }
78}
79
80/// A simplified [Result](https://doc.rust-lang.org/std/result/enum.Result.html) type
81/// for operations that can return [`Error`](Enum.error.html).
82pub type SignalResult<T> = result::Result<T, Error>;
83
84/// Public alias for a signal handler.
85/// [`sigaction`](http://man7.org/linux/man-pages/man2/sigaction.2.html).
86pub type SignalHandler =
87    extern "C" fn(num: c_int, info: *mut siginfo_t, _unused: *mut c_void) -> ();
88
89extern "C" {
90    fn __libc_current_sigrtmin() -> c_int;
91    fn __libc_current_sigrtmax() -> c_int;
92}
93
94/// Return the minimum (inclusive) real-time signal number.
95#[allow(non_snake_case)]
96pub fn SIGRTMIN() -> c_int {
97    // SAFETY: We trust this libc function.
98    unsafe { __libc_current_sigrtmin() }
99}
100
101/// Return the maximum (inclusive) real-time signal number.
102#[allow(non_snake_case)]
103pub fn SIGRTMAX() -> c_int {
104    // SAFETY: We trust this libc function.
105    unsafe { __libc_current_sigrtmax() }
106}
107
108/// Verify that a signal number is valid.
109///
110/// Supported signals range from `SIGHUP` to `SIGSYS` and from `SIGRTMIN` to `SIGRTMAX`.
111/// We recommend using realtime signals `[SIGRTMIN(), SIGRTMAX()]` for VCPU threads.
112///
113/// # Arguments
114///
115/// * `num`: the signal number to be verified.
116///
117/// # Examples
118///
119/// ```
120/// extern crate vmm_sys_util;
121/// use vmm_sys_util::signal::validate_signal_num;
122///
123/// let num = validate_signal_num(1).unwrap();
124/// ```
125pub fn validate_signal_num(num: c_int) -> errno::Result<()> {
126    if (libc::SIGHUP..=libc::SIGSYS).contains(&num) || (SIGRTMIN() <= num && num <= SIGRTMAX()) {
127        Ok(())
128    } else {
129        Err(errno::Error::new(EINVAL))
130    }
131}
132
133/// Register the signal handler of `signum`.
134///
135/// # Safety
136///
137/// This is considered unsafe because the given handler will be called
138/// asynchronously, interrupting whatever the thread was doing and therefore
139/// must only do async-signal-safe operations.
140///
141/// # Arguments
142///
143/// * `num`: the signal number to be registered.
144/// * `handler`: the signal handler function to register.
145///
146/// # Examples
147///
148/// ```
149/// # extern crate libc;
150/// extern crate vmm_sys_util;
151/// # use libc::{c_int, c_void, siginfo_t, SA_SIGINFO};
152/// use vmm_sys_util::signal::{register_signal_handler, SignalHandler};
153///
154/// extern "C" fn handle_signal(_: c_int, _: *mut siginfo_t, _: *mut c_void) {}
155/// register_signal_handler(0, handle_signal);
156/// ```
157pub fn register_signal_handler(num: c_int, handler: SignalHandler) -> errno::Result<()> {
158    validate_signal_num(num)?;
159
160    // signum specifies the signal and can be any valid signal except
161    // SIGKILL and SIGSTOP.
162    // [`sigaction`](http://man7.org/linux/man-pages/man2/sigaction.2.html).
163    if libc::SIGKILL == num || libc::SIGSTOP == num {
164        return Err(errno::Error::new(EINVAL));
165    }
166
167    // SAFETY: Safe, because this is a POD struct.
168    let mut act: sigaction = unsafe { mem::zeroed() };
169    act.sa_sigaction = handler as *const () as usize;
170    act.sa_flags = libc::SA_SIGINFO;
171
172    // Block all signals while the `handler` is running.
173    // Blocking other signals is needed to make sure the execution of
174    // the handler continues uninterrupted if another signal comes.
175    // SAFETY: The parameters are valid and we trust the sifillset function.
176    if unsafe { sigfillset(&mut act.sa_mask as *mut sigset_t) } < 0 {
177        return errno::errno_result();
178    }
179
180    // SAFETY: Safe because the parameters are valid and we check the return value.
181    match unsafe { sigaction(num, &act, null_mut()) } {
182        0 => Ok(()),
183        _ => errno::errno_result(),
184    }
185}
186
187/// Create a `sigset` with given signals.
188///
189/// An array of signal numbers are added into the signal set by
190/// [`sigaddset`](http://man7.org/linux/man-pages/man3/sigaddset.3p.html).
191/// This is a helper function used when we want to manipulate signals.
192///
193/// # Arguments
194///
195/// * `signals`: signal numbers to be added to the new `sigset`.
196///
197/// # Examples
198///
199/// ```
200/// # extern crate libc;
201/// extern crate vmm_sys_util;
202/// # use libc::sigismember;
203/// use vmm_sys_util::signal::create_sigset;
204///
205/// let sigset = create_sigset(&[1]).unwrap();
206///
207/// unsafe {
208///     assert_eq!(sigismember(&sigset, 1), 1);
209/// }
210/// ```
211pub fn create_sigset(signals: &[c_int]) -> errno::Result<sigset_t> {
212    // SAFETY: sigset will actually be initialized by sigemptyset below.
213    let mut sigset: sigset_t = unsafe { mem::zeroed() };
214
215    // SAFETY: return value is checked.
216    let ret = unsafe { sigemptyset(&mut sigset) };
217    if ret < 0 {
218        return errno::errno_result();
219    }
220
221    for signal in signals {
222        // SAFETY: return value is checked.
223        let ret = unsafe { sigaddset(&mut sigset, *signal) };
224        if ret < 0 {
225            return errno::errno_result();
226        }
227    }
228
229    Ok(sigset)
230}
231
232/// Retrieve the signal mask that is blocked of the current thread.
233///
234/// Use [`pthread_sigmask`](http://man7.org/linux/man-pages/man3/pthread_sigmask.3.html)
235/// to fetch the signal mask which is blocked for the caller, return the signal mask as
236/// a vector of c_int.
237///
238/// # Examples
239///
240/// ```
241/// extern crate vmm_sys_util;
242/// use vmm_sys_util::signal::{block_signal, get_blocked_signals};
243///
244/// block_signal(1).unwrap();
245/// assert!(get_blocked_signals().unwrap().contains(&(1)));
246/// ```
247pub fn get_blocked_signals() -> SignalResult<Vec<c_int>> {
248    let mut mask = Vec::new();
249
250    // SAFETY: return values are checked.
251    unsafe {
252        let mut old_sigset: sigset_t = mem::zeroed();
253        let ret = pthread_sigmask(SIG_BLOCK, null(), &mut old_sigset as *mut sigset_t);
254        if ret < 0 {
255            return Err(Error::RetrieveSignalMask(ret));
256        }
257
258        for num in 0..=SIGRTMAX() {
259            if sigismember(&old_sigset, num) > 0 {
260                mask.push(num);
261            }
262        }
263    }
264
265    Ok(mask)
266}
267
268/// Mask a given signal.
269///
270/// Set the given signal `num` as blocked.
271/// If signal is already blocked, the call will fail with
272/// [`SignalAlreadyBlocked`](enum.Error.html#variant.SignalAlreadyBlocked).
273///
274/// # Arguments
275///
276/// * `num`: the signal to be masked.
277///
278/// # Examples
279///
280/// ```
281/// extern crate vmm_sys_util;
282/// use vmm_sys_util::signal::block_signal;
283///
284/// block_signal(1).unwrap();
285/// ```
286// Allowing comparison chain because rewriting it with match makes the code less readable.
287// Also, the risk of having non-exhaustive checks is low.
288#[allow(clippy::comparison_chain)]
289pub fn block_signal(num: c_int) -> SignalResult<()> {
290    let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
291
292    // SAFETY: return values are checked.
293    unsafe {
294        let mut old_sigset: sigset_t = mem::zeroed();
295        let ret = pthread_sigmask(SIG_BLOCK, &sigset, &mut old_sigset as *mut sigset_t);
296        if ret < 0 {
297            return Err(Error::BlockSignal(errno::Error::last()));
298        }
299        // Check if the given signal is already blocked.
300        let ret = sigismember(&old_sigset, num);
301        if ret < 0 {
302            return Err(Error::CompareBlockedSignals(errno::Error::last()));
303        } else if ret > 0 {
304            return Err(Error::SignalAlreadyBlocked(num));
305        }
306    }
307    Ok(())
308}
309
310/// Unmask a given signal.
311///
312/// # Arguments
313///
314/// * `num`: the signal to be unmasked.
315///
316/// # Examples
317///
318/// ```
319/// extern crate vmm_sys_util;
320/// use vmm_sys_util::signal::{block_signal, get_blocked_signals, unblock_signal};
321///
322/// block_signal(1).unwrap();
323/// assert!(get_blocked_signals().unwrap().contains(&(1)));
324/// unblock_signal(1).unwrap();
325/// ```
326pub fn unblock_signal(num: c_int) -> SignalResult<()> {
327    let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
328
329    // SAFETY: return value is checked.
330    let ret = unsafe { pthread_sigmask(SIG_UNBLOCK, &sigset, null_mut()) };
331    if ret < 0 {
332        return Err(Error::UnblockSignal(errno::Error::last()));
333    }
334    Ok(())
335}
336
337/// Clear a pending signal.
338///
339/// # Arguments
340///
341/// * `num`: the signal to be cleared.
342///
343/// # Examples
344///
345/// ```
346/// # extern crate libc;
347/// extern crate vmm_sys_util;
348/// # use libc::{pthread_kill, sigismember, sigpending, sigset_t};
349/// # use std::mem;
350/// # use std::thread;
351/// # use std::time::Duration;
352/// use vmm_sys_util::signal::{block_signal, clear_signal, Killable};
353///
354/// block_signal(1).unwrap();
355/// let killable = thread::spawn(move || {
356///     thread::sleep(Duration::from_millis(100));
357///     unsafe {
358///         let mut chkset: sigset_t = mem::zeroed();
359///         sigpending(&mut chkset);
360///         assert_eq!(sigismember(&chkset, 1), 1);
361///     }
362/// });
363/// unsafe {
364///     pthread_kill(killable.pthread_handle(), 1);
365/// }
366/// clear_signal(1).unwrap();
367/// ```
368pub fn clear_signal(num: c_int) -> SignalResult<()> {
369    let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
370
371    while {
372        // SAFETY: This is safe as we are rigorously checking return values
373        // of libc calls.
374        unsafe {
375            let mut siginfo: siginfo_t = mem::zeroed();
376            let ts = timespec {
377                tv_sec: 0,
378                tv_nsec: 0,
379            };
380            // Attempt to consume one instance of pending signal. If signal
381            // is not pending, the call will fail with EAGAIN or EINTR.
382            let ret = sigtimedwait(&sigset, &mut siginfo, &ts);
383            if ret < 0 {
384                let e = errno::Error::last();
385                match e.errno() {
386                    EAGAIN | EINTR => {}
387                    _ => {
388                        return Err(Error::ClearWaitPending(errno::Error::last()));
389                    }
390                }
391            }
392
393            // This sigset will be actually filled with `sigpending` call.
394            let mut chkset: sigset_t = mem::zeroed();
395            // See if more instances of the signal are pending.
396            let ret = sigpending(&mut chkset);
397            if ret < 0 {
398                return Err(Error::ClearGetPending(errno::Error::last()));
399            }
400
401            let ret = sigismember(&chkset, num);
402            if ret < 0 {
403                return Err(Error::ClearCheckPending(errno::Error::last()));
404            }
405
406            // This is do-while loop condition.
407            ret != 0
408        }
409    } {}
410
411    Ok(())
412}
413
414/// Trait for threads that can be signalled via `pthread_kill`.
415///
416/// Note that this is only useful for signals between `SIGRTMIN()` and
417/// `SIGRTMAX()` because these are guaranteed to not be used by the C
418/// runtime.
419///
420/// # Safety
421///
422/// This is marked unsafe because the implementation of this trait must
423/// guarantee that the returned `pthread_t` is valid and has a lifetime at
424/// least that of the trait object.
425pub unsafe trait Killable {
426    /// Cast this killable thread as `pthread_t`.
427    fn pthread_handle(&self) -> pthread_t;
428
429    /// Send a signal to this killable thread.
430    ///
431    /// # Arguments
432    ///
433    /// * `num`: specify the signal
434    fn kill(&self, num: c_int) -> errno::Result<()> {
435        validate_signal_num(num)?;
436
437        // SAFETY: Safe because we ensure we are using a valid pthread handle,
438        // a valid signal number, and check the return result.
439        let ret = unsafe { pthread_kill(self.pthread_handle(), num) };
440        if ret < 0 {
441            return errno::errno_result();
442        }
443        Ok(())
444    }
445}
446
447// SAFETY: Safe because we fulfill our contract of returning a genuine pthread handle.
448unsafe impl<T> Killable for JoinHandle<T> {
449    fn pthread_handle(&self) -> pthread_t {
450        // JoinHandleExt::as_pthread_t gives c_ulong, convert it to the
451        // type that the libc crate expects
452        assert_eq!(mem::size_of::<pthread_t>(), mem::size_of::<usize>());
453        self.as_pthread_t() as usize as pthread_t
454    }
455}
456
457#[cfg(test)]
458mod tests {
459    #![allow(clippy::undocumented_unsafe_blocks)]
460    use super::*;
461    use std::thread;
462    use std::time::Duration;
463
464    // Reserve for each vcpu signal.
465    static mut SIGNAL_HANDLER_CALLED: bool = false;
466
467    extern "C" fn handle_signal(_: c_int, _: *mut siginfo_t, _: *mut c_void) {
468        unsafe {
469            // In the tests, there only uses vcpu signal.
470            SIGNAL_HANDLER_CALLED = true;
471        }
472    }
473
474    fn is_pending(signal: c_int) -> bool {
475        unsafe {
476            let mut chkset: sigset_t = mem::zeroed();
477            sigpending(&mut chkset);
478            sigismember(&chkset, signal) == 1
479        }
480    }
481
482    #[test]
483    fn test_register_signal_handler() {
484        // testing bad value
485        assert!(register_signal_handler(libc::SIGKILL, handle_signal).is_err());
486        assert!(register_signal_handler(libc::SIGSTOP, handle_signal).is_err());
487        assert!(register_signal_handler(SIGRTMAX() + 1, handle_signal).is_err());
488        assert!(register_signal_handler(SIGRTMAX(), handle_signal).is_ok());
489        assert!(register_signal_handler(SIGRTMIN(), handle_signal).is_ok());
490        assert!(register_signal_handler(libc::SIGSYS, handle_signal).is_ok());
491    }
492
493    #[test]
494    #[allow(clippy::empty_loop)]
495    fn test_killing_thread() {
496        let killable = thread::spawn(|| thread::current().id());
497        let killable_id = killable.join().unwrap();
498        assert_ne!(killable_id, thread::current().id());
499
500        // We install a signal handler for the specified signal; otherwise the whole process will
501        // be brought down when the signal is received, as part of the default behaviour. Signal
502        // handlers are global, so we install this before starting the thread.
503        register_signal_handler(SIGRTMIN(), handle_signal)
504            .expect("failed to register vcpu signal handler");
505
506        let killable = thread::spawn(|| loop {});
507
508        let res = killable.kill(SIGRTMAX() + 1);
509        assert!(res.is_err());
510
511        unsafe {
512            assert!(!SIGNAL_HANDLER_CALLED);
513        }
514
515        assert!(killable.kill(SIGRTMIN()).is_ok());
516
517        // We're waiting to detect that the signal handler has been called.
518        const MAX_WAIT_ITERS: u32 = 20;
519        let mut iter_count = 0;
520        loop {
521            thread::sleep(Duration::from_millis(100));
522
523            if unsafe { SIGNAL_HANDLER_CALLED } {
524                break;
525            }
526
527            iter_count += 1;
528            // timeout if we wait too long
529            assert!(iter_count <= MAX_WAIT_ITERS);
530        }
531
532        // Our signal handler doesn't do anything which influences the killable thread, so the
533        // previous signal is effectively ignored. If we were to join killable here, we would block
534        // forever as the loop keeps running. Since we don't join, the thread will become detached
535        // as the handle is dropped, and will be killed when the process/main thread exits.
536    }
537
538    #[test]
539    fn test_block_unblock_signal() {
540        let signal = SIGRTMIN();
541
542        // Check if it is blocked.
543        unsafe {
544            let mut sigset: sigset_t = mem::zeroed();
545            pthread_sigmask(SIG_BLOCK, null(), &mut sigset as *mut sigset_t);
546            assert_eq!(sigismember(&sigset, signal), 0);
547        }
548
549        block_signal(signal).unwrap();
550        assert!(get_blocked_signals().unwrap().contains(&(signal)));
551
552        unblock_signal(signal).unwrap();
553        assert!(!get_blocked_signals().unwrap().contains(&(signal)));
554    }
555
556    #[test]
557    fn test_clear_pending() {
558        let signal = SIGRTMIN() + 1;
559
560        block_signal(signal).unwrap();
561
562        // Block the signal, which means it won't be delivered until it is
563        // unblocked. Pending between the time when the signal which is set as blocked
564        // is generated and when is delivered.
565        let killable = thread::spawn(move || {
566            loop {
567                // Wait for the signal being killed.
568                thread::sleep(Duration::from_millis(100));
569                if is_pending(signal) {
570                    clear_signal(signal).unwrap();
571                    assert!(!is_pending(signal));
572                    break;
573                }
574            }
575        });
576
577        // Send a signal to the thread.
578        assert!(killable.kill(SIGRTMIN() + 1).is_ok());
579        killable.join().unwrap();
580    }
581}