mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-08 00:51:07 +02:00
Vendor the pinned Rustix package with an Android-only weak libc lookup so missing statx returns ENOSYS and uses the existing stat64 fallback. Add an isolated Android smoke test with a hidden symbol and a seccomp filter that kills raw statx probes. Verified 177 workspace tests, host Clippy, and ARM64 Android sandbox smoke test. ARM32 cross-target Clippy passes with the existing needless_return warning in binary.rs exempted; physical Android 10 ARM32 validation remains required.
327 lines
9.4 KiB
Rust
Vendored
327 lines
9.4 KiB
Rust
Vendored
//! 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
|
|
);
|
|
}
|
|
}
|
|
}
|