diff options
Diffstat (limited to 'vendor/rustix/src/kernel_sigset.rs')
| -rw-r--r-- | vendor/rustix/src/kernel_sigset.rs | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/vendor/rustix/src/kernel_sigset.rs b/vendor/rustix/src/kernel_sigset.rs new file mode 100644 index 00000000..706bcb8c --- /dev/null +++ b/vendor/rustix/src/kernel_sigset.rs @@ -0,0 +1,326 @@ +//! The [`KernelSigSet`] type. + +#![allow(unsafe_code)] +#![allow(non_camel_case_types)] + +use crate::backend::c; +use crate::signal::Signal; +use core::fmt; +use linux_raw_sys::general::{kernel_sigset_t, _NSIG}; + +/// `kernel_sigset_t`—A set of signal numbers, as used by some syscalls. +/// +/// This is similar to `libc::sigset_t`, but with only enough space for the +/// signals currently known to be used by the kernel. libc implementations +/// reserve extra space so that if Linux defines new signals in the future +/// they can add support without breaking their dynamic linking ABI. Rustix +/// doesn't support a dynamic linking ABI, so if we need to increase the +/// size of `KernelSigSet` in the future, we can do so. +/// +/// It's also the case that the last time Linux changed the size of its +/// `kernel_sigset_t` was when it added support for POSIX.1b signals in 1999. +/// +/// `KernelSigSet` is guaranteed to have a subset of the layout of +/// `libc::sigset_t`. +/// +/// libc implementations typically also reserve some signal values for internal +/// use. In a process that contains a libc, some unsafe functions invoke +/// undefined behavior if passed a `KernelSigSet` that contains one of the +/// signals that the libc reserves. +#[repr(transparent)] +#[derive(Clone)] +pub struct KernelSigSet(kernel_sigset_t); + +impl KernelSigSet { + /// Create a new empty `KernelSigSet`. + pub const fn empty() -> Self { + const fn zeros<const N: usize>() -> [c::c_ulong; N] { + [0; N] + } + Self(kernel_sigset_t { sig: zeros() }) + } + + /// Create a new `KernelSigSet` with all signals set. + /// + /// This includes signals which are typically reserved for libc. + pub const fn all() -> Self { + const fn ones<const N: usize>() -> [c::c_ulong; N] { + [!0; N] + } + Self(kernel_sigset_t { sig: ones() }) + } + + /// Remove all signals. + pub fn clear(&mut self) { + *self = Self(kernel_sigset_t { + sig: Default::default(), + }); + } + + /// Insert a signal. + pub fn insert(&mut self, sig: Signal) { + let sigs_per_elt = core::mem::size_of_val(&self.0.sig[0]) * 8; + + let raw = (sig.as_raw().wrapping_sub(1)) as usize; + self.0.sig[raw / sigs_per_elt] |= 1 << (raw % sigs_per_elt); + } + + /// Insert all signals. + pub fn insert_all(&mut self) { + self.0.sig.fill(!0); + } + + /// Remove a signal. + pub fn remove(&mut self, sig: Signal) { + let sigs_per_elt = core::mem::size_of_val(&self.0.sig[0]) * 8; + + let raw = (sig.as_raw().wrapping_sub(1)) as usize; + self.0.sig[raw / sigs_per_elt] &= !(1 << (raw % sigs_per_elt)); + } + + /// Test whether a given signal is present. + pub fn contains(&self, sig: Signal) -> bool { + let sigs_per_elt = core::mem::size_of_val(&self.0.sig[0]) * 8; + + let raw = (sig.as_raw().wrapping_sub(1)) as usize; + (self.0.sig[raw / sigs_per_elt] & (1 << (raw % sigs_per_elt))) != 0 + } +} + +impl Default for KernelSigSet { + #[inline] + fn default() -> Self { + Self::empty() + } +} + +impl fmt::Debug for KernelSigSet { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_set(); + + // Surprisingly, `_NSIG` is inclusive. + for i in 1..=_NSIG { + // SAFETY: This value is non-zero, in range, and only used for + // debug output. + let sig = unsafe { Signal::from_raw_unchecked(i as _) }; + + if self.contains(sig) { + d.entry(&sig); + } + } + + d.finish() + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[cfg(linux_raw)] + use crate::runtime::{KERNEL_SIGRTMAX, KERNEL_SIGRTMIN}; + use core::mem::{align_of, size_of}; + + #[test] + fn test_assumptions() { + #[cfg(linux_raw)] + assert!(KERNEL_SIGRTMAX as usize - 1 < size_of::<KernelSigSet>() * 8); + } + + #[test] + fn test_layouts() { + assert!(size_of::<KernelSigSet>() <= size_of::<libc::sigset_t>()); + assert!(align_of::<KernelSigSet>() <= align_of::<libc::sigset_t>()); + } + + /// A bunch of signals for testing. + fn sigs() -> Vec<Signal> { + #[allow(unused_mut)] + let mut sigs = vec![ + Signal::HUP, + Signal::INT, + Signal::QUIT, + Signal::ILL, + Signal::TRAP, + Signal::ABORT, + Signal::BUS, + Signal::FPE, + Signal::KILL, + Signal::USR1, + Signal::SEGV, + Signal::USR2, + Signal::PIPE, + Signal::ALARM, + Signal::TERM, + Signal::CHILD, + Signal::CONT, + Signal::STOP, + Signal::TSTP, + Signal::TTIN, + Signal::TTOU, + Signal::URG, + Signal::XCPU, + Signal::XFSZ, + Signal::VTALARM, + Signal::PROF, + Signal::WINCH, + Signal::SYS, + unsafe { Signal::from_raw_unchecked(libc::SIGRTMIN()) }, + unsafe { Signal::from_raw_unchecked(libc::SIGRTMIN() + 7) }, + unsafe { Signal::from_raw_unchecked(libc::SIGRTMAX()) }, + ]; + + #[cfg(linux_raw)] + { + sigs.push(unsafe { Signal::from_raw_unchecked(KERNEL_SIGRTMIN) }); + sigs.push(unsafe { Signal::from_raw_unchecked(KERNEL_SIGRTMIN + 7) }); + sigs.push(unsafe { Signal::from_raw_unchecked(KERNEL_SIGRTMAX) }); + } + + sigs + } + + /// A bunch of non-reserved signals for testing. + fn libc_sigs() -> [Signal; 31] { + [ + Signal::HUP, + Signal::INT, + Signal::QUIT, + Signal::ILL, + Signal::TRAP, + Signal::ABORT, + Signal::BUS, + Signal::FPE, + Signal::KILL, + Signal::USR1, + Signal::SEGV, + Signal::USR2, + Signal::PIPE, + Signal::ALARM, + Signal::TERM, + Signal::CHILD, + Signal::CONT, + Signal::STOP, + Signal::TSTP, + Signal::TTIN, + Signal::TTOU, + Signal::URG, + Signal::XCPU, + Signal::XFSZ, + Signal::VTALARM, + Signal::PROF, + Signal::WINCH, + Signal::SYS, + unsafe { Signal::from_raw_unchecked(libc::SIGRTMIN()) }, + unsafe { Signal::from_raw_unchecked(libc::SIGRTMIN() + 7) }, + unsafe { Signal::from_raw_unchecked(libc::SIGRTMAX()) }, + ] + } + + #[test] + fn test_ops_plain() { + for sig in sigs() { + let mut set = KernelSigSet::empty(); + for sig in sigs() { + assert!(!set.contains(sig)); + } + + set.insert(sig); + assert!(set.contains(sig)); + for sig in sigs().iter().filter(|s| **s != sig) { + assert!(!set.contains(*sig)); + } + + set.remove(sig); + for sig in sigs() { + assert!(!set.contains(sig)); + } + } + } + + #[test] + fn test_clear() { + let mut set = KernelSigSet::empty(); + for sig in sigs() { + set.insert(sig); + } + + set.clear(); + + for sig in sigs() { + assert!(!set.contains(sig)); + } + } + + // io_uring libraries assume that libc's `sigset_t` matches the layout + // of the Linux kernel's `kernel_sigset_t`. Test that rustix's layout + // matches as well. + #[test] + fn test_libc_layout_compatibility() { + use crate::utils::as_ptr; + + let mut lc = unsafe { core::mem::zeroed::<libc::sigset_t>() }; + let mut ru = KernelSigSet::empty(); + let r = unsafe { libc::sigemptyset(&mut lc) }; + + assert_eq!(r, 0); + assert_eq!( + unsafe { + libc::memcmp( + as_ptr(&lc).cast(), + as_ptr(&ru).cast(), + core::mem::size_of::<KernelSigSet>(), + ) + }, + 0 + ); + + for sig in libc_sigs() { + ru.insert(sig); + assert_ne!( + unsafe { + libc::memcmp( + as_ptr(&lc).cast(), + as_ptr(&ru).cast(), + core::mem::size_of::<KernelSigSet>(), + ) + }, + 0 + ); + let r = unsafe { libc::sigaddset(&mut lc, sig.as_raw()) }; + assert_eq!(r, 0); + assert_eq!( + unsafe { + libc::memcmp( + as_ptr(&lc).cast(), + as_ptr(&ru).cast(), + core::mem::size_of::<KernelSigSet>(), + ) + }, + 0 + ); + ru.remove(sig); + assert_ne!( + unsafe { + libc::memcmp( + as_ptr(&lc).cast(), + as_ptr(&ru).cast(), + core::mem::size_of::<KernelSigSet>(), + ) + }, + 0 + ); + let r = unsafe { libc::sigdelset(&mut lc, sig.as_raw()) }; + assert_eq!(r, 0); + assert_eq!( + unsafe { + libc::memcmp( + as_ptr(&lc).cast(), + as_ptr(&ru).cast(), + core::mem::size_of::<KernelSigSet>(), + ) + }, + 0 + ); + } + } +} |
