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/// ```
157
158pub fn register_signal_handler(num: c_int, handler: SignalHandler) -> errno::Result<()> {
159 validate_signal_num(num)?;
160
161 // signum specifies the signal and can be any valid signal except
162 // SIGKILL and SIGSTOP.
163 // [`sigaction`](http://man7.org/linux/man-pages/man2/sigaction.2.html).
164 if libc::SIGKILL == num || libc::SIGSTOP == num {
165 return Err(errno::Error::new(EINVAL));
166 }
167
168 // SAFETY: Safe, because this is a POD struct.
169 let mut act: sigaction = unsafe { mem::zeroed() };
170 act.sa_sigaction = handler as *const () as usize;
171 act.sa_flags = libc::SA_SIGINFO;
172
173 // Block all signals while the `handler` is running.
174 // Blocking other signals is needed to make sure the execution of
175 // the handler continues uninterrupted if another signal comes.
176 // SAFETY: The parameters are valid and we trust the sifillset function.
177 if unsafe { sigfillset(&mut act.sa_mask as *mut sigset_t) } < 0 {
178 return errno::errno_result();
179 }
180
181 // SAFETY: Safe because the parameters are valid and we check the return value.
182 match unsafe { sigaction(num, &act, null_mut()) } {
183 0 => Ok(()),
184 _ => errno::errno_result(),
185 }
186}
187
188/// Create a `sigset` with given signals.
189///
190/// An array of signal numbers are added into the signal set by
191/// [`sigaddset`](http://man7.org/linux/man-pages/man3/sigaddset.3p.html).
192/// This is a helper function used when we want to manipulate signals.
193///
194/// # Arguments
195///
196/// * `signals`: signal numbers to be added to the new `sigset`.
197///
198/// # Examples
199///
200/// ```
201/// # extern crate libc;
202/// extern crate vmm_sys_util;
203/// # use libc::sigismember;
204/// use vmm_sys_util::signal::create_sigset;
205///
206/// let sigset = create_sigset(&[1]).unwrap();
207///
208/// unsafe {
209/// assert_eq!(sigismember(&sigset, 1), 1);
210/// }
211/// ```
212pub fn create_sigset(signals: &[c_int]) -> errno::Result<sigset_t> {
213 // SAFETY: sigset will actually be initialized by sigemptyset below.
214 let mut sigset: sigset_t = unsafe { mem::zeroed() };
215
216 // SAFETY: return value is checked.
217 let ret = unsafe { sigemptyset(&mut sigset) };
218 if ret < 0 {
219 return errno::errno_result();
220 }
221
222 for signal in signals {
223 // SAFETY: return value is checked.
224 let ret = unsafe { sigaddset(&mut sigset, *signal) };
225 if ret < 0 {
226 return errno::errno_result();
227 }
228 }
229
230 Ok(sigset)
231}
232
233/// Retrieve the signal mask that is blocked of the current thread.
234///
235/// Use [`pthread_sigmask`](http://man7.org/linux/man-pages/man3/pthread_sigmask.3.html)
236/// to fetch the signal mask which is blocked for the caller, return the signal mask as
237/// a vector of c_int.
238///
239/// # Examples
240///
241/// ```
242/// extern crate vmm_sys_util;
243/// use vmm_sys_util::signal::{block_signal, get_blocked_signals};
244///
245/// block_signal(1).unwrap();
246/// assert!(get_blocked_signals().unwrap().contains(&(1)));
247/// ```
248pub fn get_blocked_signals() -> SignalResult<Vec<c_int>> {
249 let mut mask = Vec::new();
250
251 // SAFETY: return values are checked.
252 unsafe {
253 let mut old_sigset: sigset_t = mem::zeroed();
254 let ret = pthread_sigmask(SIG_BLOCK, null(), &mut old_sigset as *mut sigset_t);
255 if ret < 0 {
256 return Err(Error::RetrieveSignalMask(ret));
257 }
258
259 for num in 0..=SIGRTMAX() {
260 if sigismember(&old_sigset, num) > 0 {
261 mask.push(num);
262 }
263 }
264 }
265
266 Ok(mask)
267}
268
269/// Mask a given signal.
270///
271/// Set the given signal `num` as blocked.
272/// If signal is already blocked, the call will fail with
273/// [`SignalAlreadyBlocked`](enum.Error.html#variant.SignalAlreadyBlocked).
274///
275/// # Arguments
276///
277/// * `num`: the signal to be masked.
278///
279/// # Examples
280///
281/// ```
282/// extern crate vmm_sys_util;
283/// use vmm_sys_util::signal::block_signal;
284///
285/// block_signal(1).unwrap();
286/// ```
287// Allowing comparison chain because rewriting it with match makes the code less readable.
288// Also, the risk of having non-exhaustive checks is low.
289#[allow(clippy::comparison_chain)]
290pub fn block_signal(num: c_int) -> SignalResult<()> {
291 let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
292
293 // SAFETY: return values are checked.
294 unsafe {
295 let mut old_sigset: sigset_t = mem::zeroed();
296 let ret = pthread_sigmask(SIG_BLOCK, &sigset, &mut old_sigset as *mut sigset_t);
297 if ret < 0 {
298 return Err(Error::BlockSignal(errno::Error::last()));
299 }
300 // Check if the given signal is already blocked.
301 let ret = sigismember(&old_sigset, num);
302 if ret < 0 {
303 return Err(Error::CompareBlockedSignals(errno::Error::last()));
304 } else if ret > 0 {
305 return Err(Error::SignalAlreadyBlocked(num));
306 }
307 }
308 Ok(())
309}
310
311/// Unmask a given signal.
312///
313/// # Arguments
314///
315/// * `num`: the signal to be unmasked.
316///
317/// # Examples
318///
319/// ```
320/// extern crate vmm_sys_util;
321/// use vmm_sys_util::signal::{block_signal, get_blocked_signals, unblock_signal};
322///
323/// block_signal(1).unwrap();
324/// assert!(get_blocked_signals().unwrap().contains(&(1)));
325/// unblock_signal(1).unwrap();
326/// ```
327pub fn unblock_signal(num: c_int) -> SignalResult<()> {
328 let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
329
330 // SAFETY: return value is checked.
331 let ret = unsafe { pthread_sigmask(SIG_UNBLOCK, &sigset, null_mut()) };
332 if ret < 0 {
333 return Err(Error::UnblockSignal(errno::Error::last()));
334 }
335 Ok(())
336}
337
338/// Clear a pending signal.
339///
340/// # Arguments
341///
342/// * `num`: the signal to be cleared.
343///
344/// # Examples
345///
346/// ```
347/// # extern crate libc;
348/// extern crate vmm_sys_util;
349/// # use libc::{pthread_kill, sigismember, sigpending, sigset_t};
350/// # use std::mem;
351/// # use std::thread;
352/// # use std::time::Duration;
353/// use vmm_sys_util::signal::{block_signal, clear_signal, Killable};
354///
355/// block_signal(1).unwrap();
356/// let killable = thread::spawn(move || {
357/// thread::sleep(Duration::from_millis(100));
358/// unsafe {
359/// let mut chkset: sigset_t = mem::zeroed();
360/// sigpending(&mut chkset);
361/// assert_eq!(sigismember(&chkset, 1), 1);
362/// }
363/// });
364/// unsafe {
365/// pthread_kill(killable.pthread_handle(), 1);
366/// }
367/// clear_signal(1).unwrap();
368/// ```
369pub fn clear_signal(num: c_int) -> SignalResult<()> {
370 let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
371
372 while {
373 // SAFETY: This is safe as we are rigorously checking return values
374 // of libc calls.
375 unsafe {
376 let mut siginfo: siginfo_t = mem::zeroed();
377 let ts = timespec {
378 tv_sec: 0,
379 tv_nsec: 0,
380 };
381 // Attempt to consume one instance of pending signal. If signal
382 // is not pending, the call will fail with EAGAIN or EINTR.
383 let ret = sigtimedwait(&sigset, &mut siginfo, &ts);
384 if ret < 0 {
385 let e = errno::Error::last();
386 match e.errno() {
387 EAGAIN | EINTR => {}
388 _ => {
389 return Err(Error::ClearWaitPending(errno::Error::last()));
390 }
391 }
392 }
393
394 // This sigset will be actually filled with `sigpending` call.
395 let mut chkset: sigset_t = mem::zeroed();
396 // See if more instances of the signal are pending.
397 let ret = sigpending(&mut chkset);
398 if ret < 0 {
399 return Err(Error::ClearGetPending(errno::Error::last()));
400 }
401
402 let ret = sigismember(&chkset, num);
403 if ret < 0 {
404 return Err(Error::ClearCheckPending(errno::Error::last()));
405 }
406
407 // This is do-while loop condition.
408 ret != 0
409 }
410 } {}
411
412 Ok(())
413}
414
415/// Trait for threads that can be signalled via `pthread_kill`.
416///
417/// Note that this is only useful for signals between `SIGRTMIN()` and
418/// `SIGRTMAX()` because these are guaranteed to not be used by the C
419/// runtime.
420///
421/// # Safety
422///
423/// This is marked unsafe because the implementation of this trait must
424/// guarantee that the returned `pthread_t` is valid and has a lifetime at
425/// least that of the trait object.
426pub unsafe trait Killable {
427 /// Cast this killable thread as `pthread_t`.
428 fn pthread_handle(&self) -> pthread_t;
429
430 /// Send a signal to this killable thread.
431 ///
432 /// # Arguments
433 ///
434 /// * `num`: specify the signal
435 fn kill(&self, num: c_int) -> errno::Result<()> {
436 validate_signal_num(num)?;
437
438 // SAFETY: Safe because we ensure we are using a valid pthread handle,
439 // a valid signal number, and check the return result.
440 let ret = unsafe { pthread_kill(self.pthread_handle(), num) };
441 if ret < 0 {
442 return errno::errno_result();
443 }
444 Ok(())
445 }
446}
447
448// SAFETY: Safe because we fulfill our contract of returning a genuine pthread handle.
449unsafe impl<T> Killable for JoinHandle<T> {
450 fn pthread_handle(&self) -> pthread_t {
451 // JoinHandleExt::as_pthread_t gives c_ulong, convert it to the
452 // type that the libc crate expects
453 assert_eq!(mem::size_of::<pthread_t>(), mem::size_of::<usize>());
454 self.as_pthread_t() as usize as pthread_t
455 }
456}
457
458#[cfg(test)]
459mod tests {
460 #![allow(clippy::undocumented_unsafe_blocks)]
461 use super::*;
462 use std::thread;
463 use std::time::Duration;
464
465 // Reserve for each vcpu signal.
466 static mut SIGNAL_HANDLER_CALLED: bool = false;
467
468 extern "C" fn handle_signal(_: c_int, _: *mut siginfo_t, _: *mut c_void) {
469 unsafe {
470 // In the tests, there only uses vcpu signal.
471 SIGNAL_HANDLER_CALLED = true;
472 }
473 }
474
475 fn is_pending(signal: c_int) -> bool {
476 unsafe {
477 let mut chkset: sigset_t = mem::zeroed();
478 sigpending(&mut chkset);
479 sigismember(&chkset, signal) == 1
480 }
481 }
482
483 #[test]
484 fn test_register_signal_handler() {
485 // testing bad value
486 assert!(register_signal_handler(libc::SIGKILL, handle_signal).is_err());
487 assert!(register_signal_handler(libc::SIGSTOP, handle_signal).is_err());
488 assert!(register_signal_handler(SIGRTMAX() + 1, handle_signal).is_err());
489 format!("{:?}", register_signal_handler(SIGRTMAX(), handle_signal));
490 assert!(register_signal_handler(SIGRTMIN(), handle_signal).is_ok());
491 assert!(register_signal_handler(libc::SIGSYS, handle_signal).is_ok());
492 }
493
494 #[test]
495 #[allow(clippy::empty_loop)]
496 fn test_killing_thread() {
497 let killable = thread::spawn(|| thread::current().id());
498 let killable_id = killable.join().unwrap();
499 assert_ne!(killable_id, thread::current().id());
500
501 // We install a signal handler for the specified signal; otherwise the whole process will
502 // be brought down when the signal is received, as part of the default behaviour. Signal
503 // handlers are global, so we install this before starting the thread.
504 register_signal_handler(SIGRTMIN(), handle_signal)
505 .expect("failed to register vcpu signal handler");
506
507 let killable = thread::spawn(|| loop {});
508
509 let res = killable.kill(SIGRTMAX() + 1);
510 assert!(res.is_err());
511 format!("{:?}", res);
512
513 unsafe {
514 assert!(!SIGNAL_HANDLER_CALLED);
515 }
516
517 assert!(killable.kill(SIGRTMIN()).is_ok());
518
519 // We're waiting to detect that the signal handler has been called.
520 const MAX_WAIT_ITERS: u32 = 20;
521 let mut iter_count = 0;
522 loop {
523 thread::sleep(Duration::from_millis(100));
524
525 if unsafe { SIGNAL_HANDLER_CALLED } {
526 break;
527 }
528
529 iter_count += 1;
530 // timeout if we wait too long
531 assert!(iter_count <= MAX_WAIT_ITERS);
532 }
533
534 // Our signal handler doesn't do anything which influences the killable thread, so the
535 // previous signal is effectively ignored. If we were to join killable here, we would block
536 // forever as the loop keeps running. Since we don't join, the thread will become detached
537 // as the handle is dropped, and will be killed when the process/main thread exits.
538 }
539
540 #[test]
541 fn test_block_unblock_signal() {
542 let signal = SIGRTMIN();
543
544 // Check if it is blocked.
545 unsafe {
546 let mut sigset: sigset_t = mem::zeroed();
547 pthread_sigmask(SIG_BLOCK, null(), &mut sigset as *mut sigset_t);
548 assert_eq!(sigismember(&sigset, signal), 0);
549 }
550
551 block_signal(signal).unwrap();
552 assert!(get_blocked_signals().unwrap().contains(&(signal)));
553
554 unblock_signal(signal).unwrap();
555 assert!(!get_blocked_signals().unwrap().contains(&(signal)));
556 }
557
558 #[test]
559 fn test_clear_pending() {
560 let signal = SIGRTMIN() + 1;
561
562 block_signal(signal).unwrap();
563
564 // Block the signal, which means it won't be delivered until it is
565 // unblocked. Pending between the time when the signal which is set as blocked
566 // is generated and when is delivered.
567 let killable = thread::spawn(move || {
568 loop {
569 // Wait for the signal being killed.
570 thread::sleep(Duration::from_millis(100));
571 if is_pending(signal) {
572 clear_signal(signal).unwrap();
573 assert!(!is_pending(signal));
574 break;
575 }
576 }
577 });
578
579 // Send a signal to the thread.
580 assert!(killable.kill(SIGRTMIN() + 1).is_ok());
581 killable.join().unwrap();
582 }
583}