xref: /aosp_15_r20/external/crosvm/base/src/sys/linux/signalfd.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2017 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::fs::File;
6 use std::mem;
7 use std::os::raw::c_int;
8 use std::os::unix::io::AsRawFd;
9 use std::os::unix::io::FromRawFd;
10 use std::os::unix::io::RawFd;
11 use std::result;
12 
13 use libc::c_void;
14 use libc::read;
15 use libc::signalfd;
16 use libc::signalfd_siginfo;
17 use libc::EAGAIN;
18 use libc::SFD_CLOEXEC;
19 use libc::SFD_NONBLOCK;
20 use log::error;
21 use remain::sorted;
22 use thiserror::Error;
23 
24 use super::signal;
25 use super::Error as ErrnoError;
26 use super::RawDescriptor;
27 use crate::descriptor::AsRawDescriptor;
28 
29 #[sorted]
30 #[derive(Error, Debug)]
31 pub enum Error {
32     /// Failed to block the signal when creating signalfd.
33     #[error("failed to block the signal when creating signalfd: {0}")]
34     CreateBlockSignal(signal::Error),
35     /// Failed to create a new signalfd.
36     #[error("failed to create a new signalfd: {0}")]
37     CreateSignalFd(ErrnoError),
38     /// Failed to construct sigset when creating signalfd.
39     #[error("failed to construct sigset when creating signalfd: {0}")]
40     CreateSigset(ErrnoError),
41     /// Signalfd could be read, but didn't return a full siginfo struct.
42     /// This wraps the number of bytes that were actually read.
43     #[error("signalfd failed to return a full siginfo struct, read only {0} bytes")]
44     SignalFdPartialRead(usize),
45     /// Unable to read from signalfd.
46     #[error("unable to read from signalfd: {0}")]
47     SignalFdRead(ErrnoError),
48 }
49 
50 pub type Result<T> = result::Result<T, Error>;
51 
52 /// A safe wrapper around a Linux signalfd (man 2 signalfd).
53 ///
54 /// A signalfd can be used for non-synchronous signals (such as SIGCHLD) so that
55 /// signals can be processed without the use of a signal handler.
56 pub struct SignalFd {
57     signalfd: File,
58     signal: c_int,
59 }
60 
61 impl SignalFd {
62     /// Creates a new SignalFd for the given signal, blocking the normal handler
63     /// for the signal as well. Since we mask out the normal handler, this is
64     /// a risky operation - signal masking will persist across fork and even
65     /// **exec** so the user of SignalFd should think long and hard about
66     /// when to mask signals.
new(signal: c_int) -> Result<SignalFd>67     pub fn new(signal: c_int) -> Result<SignalFd> {
68         let sigset = signal::create_sigset(&[signal]).map_err(Error::CreateSigset)?;
69 
70         // SAFETY:
71         // This is safe as we check the return value and know that fd is valid.
72         let fd = unsafe { signalfd(-1, &sigset, SFD_CLOEXEC | SFD_NONBLOCK) };
73         if fd < 0 {
74             return Err(Error::CreateSignalFd(ErrnoError::last()));
75         }
76 
77         // Mask out the normal handler for the signal.
78         signal::block_signal(signal).map_err(Error::CreateBlockSignal)?;
79 
80         // SAFETY:
81         // This is safe because we checked fd for success and know the
82         // kernel gave us an fd that we own.
83         unsafe {
84             Ok(SignalFd {
85                 signalfd: File::from_raw_fd(fd),
86                 signal,
87             })
88         }
89     }
90 
91     /// Read a siginfo struct from the signalfd, if available.
read(&self) -> Result<Option<signalfd_siginfo>>92     pub fn read(&self) -> Result<Option<signalfd_siginfo>> {
93         // SAFETY:
94         // signalfd_siginfo doesn't have a default, so just zero it.
95         let mut siginfo: signalfd_siginfo = unsafe { mem::zeroed() };
96         let siginfo_size = mem::size_of::<signalfd_siginfo>();
97 
98         // SAFETY:
99         // This read is safe since we've got the space allocated for a
100         // single signalfd_siginfo, and that's exactly how much we're
101         // reading. Handling of EINTR is not required since SFD_NONBLOCK
102         // was specified. signalfds will always read in increments of
103         // sizeof(signalfd_siginfo); see man 2 signalfd.
104         let ret = unsafe {
105             read(
106                 self.signalfd.as_raw_fd(),
107                 &mut siginfo as *mut signalfd_siginfo as *mut c_void,
108                 siginfo_size,
109             )
110         };
111 
112         if ret < 0 {
113             let err = ErrnoError::last();
114             if err.errno() == EAGAIN {
115                 Ok(None)
116             } else {
117                 Err(Error::SignalFdRead(err))
118             }
119         } else if ret == (siginfo_size as isize) {
120             Ok(Some(siginfo))
121         } else {
122             Err(Error::SignalFdPartialRead(ret as usize))
123         }
124     }
125 }
126 
127 impl AsRawFd for SignalFd {
as_raw_fd(&self) -> RawFd128     fn as_raw_fd(&self) -> RawFd {
129         self.signalfd.as_raw_fd()
130     }
131 }
132 
133 impl AsRawDescriptor for SignalFd {
as_raw_descriptor(&self) -> RawDescriptor134     fn as_raw_descriptor(&self) -> RawDescriptor {
135         self.signalfd.as_raw_descriptor()
136     }
137 }
138 
139 impl Drop for SignalFd {
drop(&mut self)140     fn drop(&mut self) {
141         // This is thread-safe and safe in the sense that we're doing what
142         // was promised - unmasking the signal when we go out of scope.
143         let res = signal::unblock_signal(self.signal);
144         if let Err(e) = res {
145             error!("signalfd failed to unblock signal {}: {}", self.signal, e);
146         }
147     }
148 }
149 
150 #[cfg(test)]
151 mod tests {
152     use std::mem;
153     use std::ptr::null;
154 
155     use libc::pthread_sigmask;
156     use libc::raise;
157     use libc::sigismember;
158     use libc::sigset_t;
159 
160     use super::super::signal::SIGRTMIN;
161     use super::*;
162 
163     #[test]
new()164     fn new() {
165         SignalFd::new(SIGRTMIN()).unwrap();
166     }
167 
168     #[test]
read()169     fn read() {
170         let sigid = SIGRTMIN() + 1;
171         let sigrt_fd = SignalFd::new(sigid).unwrap();
172 
173         // SAFETY: Safe because sigid is valid and return value is checked.
174         let ret = unsafe { raise(sigid) };
175         assert_eq!(ret, 0);
176 
177         let siginfo = sigrt_fd.read().unwrap().unwrap();
178         assert_eq!(siginfo.ssi_signo, sigid as u32);
179     }
180 
181     #[test]
drop()182     fn drop() {
183         let sigid = SIGRTMIN() + 2;
184 
185         let sigrt_fd = SignalFd::new(sigid).unwrap();
186         // SAFETY: Safe because sigset and sigid are valid and return value is checked.
187         unsafe {
188             let mut sigset: sigset_t = mem::zeroed();
189             pthread_sigmask(0, null(), &mut sigset as *mut sigset_t);
190             assert_eq!(sigismember(&sigset, sigid), 1);
191         }
192 
193         mem::drop(sigrt_fd);
194 
195         // The signal should no longer be masked.
196         // SAFETY: Safe because sigset and sigid are valid and return value is checked.
197         unsafe {
198             let mut sigset: sigset_t = mem::zeroed();
199             pthread_sigmask(0, null(), &mut sigset as *mut sigset_t);
200             assert_eq!(sigismember(&sigset, sigid), 0);
201         }
202     }
203 }
204