//! linux_raw syscalls supporting `rustix::runtime`. //! //! # Safety //! //! See the `rustix::backend` module documentation for details. #![allow(unsafe_code, clippy::undocumented_unsafe_blocks)] use crate::backend::c; #[cfg(target_arch = "x86")] use crate::backend::conv::by_mut; #[cfg(target_arch = "x86_64")] use crate::backend::conv::c_uint; use crate::backend::conv::{ by_ref, c_int, ret, ret_c_int, ret_c_int_infallible, ret_error, ret_infallible, ret_void_star, size_of, zero, }; #[cfg(feature = "fs")] use crate::fd::BorrowedFd; use crate::ffi::CStr; #[cfg(feature = "fs")] use crate::fs::AtFlags; use crate::io; use crate::pid::{Pid, RawPid}; use crate::runtime::{Fork, How, Sigaction, Siginfo, Sigset, Stack}; use crate::signal::Signal; use crate::timespec::Timespec; use crate::utils::option_as_ptr; use core::ffi::c_void; use core::mem::MaybeUninit; #[cfg(target_pointer_width = "32")] use linux_raw_sys::general::__kernel_old_timespec; use linux_raw_sys::general::kernel_sigset_t; #[cfg(target_arch = "x86_64")] use linux_raw_sys::general::ARCH_SET_FS; #[inline] pub(crate) unsafe fn fork() -> io::Result { let mut child_pid = MaybeUninit::::uninit(); // Unix `fork` only returns the child PID in the parent; we'd like it in // the child too, so set `CLONE_CHILD_SETTID` and pass in the address of // a memory location to store it to in the child. // // Architectures differ on the order of the parameters. #[cfg(target_arch = "x86_64")] let pid = ret_c_int(syscall_readonly!( __NR_clone, c_int(c::SIGCHLD | c::CLONE_CHILD_SETTID), zero(), zero(), &mut child_pid, zero() ))?; #[cfg(any( target_arch = "aarch64", target_arch = "arm", target_arch = "mips", target_arch = "mips32r6", target_arch = "mips64", target_arch = "mips64r6", target_arch = "powerpc64", target_arch = "riscv64", target_arch = "x86" ))] let pid = ret_c_int(syscall_readonly!( __NR_clone, c_int(c::SIGCHLD | c::CLONE_CHILD_SETTID), zero(), zero(), zero(), &mut child_pid ))?; Ok(if let Some(pid) = Pid::from_raw(pid) { Fork::Parent(pid) } else { Fork::Child(Pid::from_raw_unchecked(child_pid.assume_init())) }) } #[cfg(feature = "fs")] pub(crate) unsafe fn execveat( dirfd: BorrowedFd<'_>, path: &CStr, args: *const *const u8, env_vars: *const *const u8, flags: AtFlags, ) -> io::Errno { ret_error(syscall_readonly!( __NR_execveat, dirfd, path, args, env_vars, flags )) } pub(crate) unsafe fn execve( path: &CStr, args: *const *const u8, env_vars: *const *const u8, ) -> io::Errno { ret_error(syscall_readonly!(__NR_execve, path, args, env_vars)) } pub(crate) mod tls { use super::*; #[cfg(target_arch = "x86")] use crate::backend::runtime::tls::UserDesc; #[cfg(target_arch = "x86")] #[inline] pub(crate) unsafe fn set_thread_area(u_info: &mut UserDesc) -> io::Result<()> { ret(syscall!(__NR_set_thread_area, by_mut(u_info))) } #[cfg(target_arch = "arm")] #[inline] pub(crate) unsafe fn arm_set_tls(data: *mut c::c_void) -> io::Result<()> { ret(syscall_readonly!(__ARM_NR_set_tls, data)) } #[cfg(target_arch = "x86_64")] #[inline] pub(crate) unsafe fn set_fs(data: *mut c::c_void) { ret_infallible(syscall_readonly!( __NR_arch_prctl, c_uint(ARCH_SET_FS), data, zero(), zero(), zero() )) } #[inline] pub(crate) unsafe fn set_tid_address(data: *mut c::c_void) -> Pid { let tid: i32 = ret_c_int_infallible(syscall_readonly!(__NR_set_tid_address, data)); Pid::from_raw_unchecked(tid) } #[inline] pub(crate) fn exit_thread(code: c::c_int) -> ! { unsafe { syscall_noreturn!(__NR_exit, c_int(code)) } } } #[inline] pub(crate) unsafe fn sigaction(signal: Signal, new: Option) -> io::Result { let mut old = MaybeUninit::::uninit(); let new = option_as_ptr(new.as_ref()); ret(syscall!( __NR_rt_sigaction, signal, new, &mut old, size_of::() ))?; Ok(old.assume_init()) } #[inline] pub(crate) unsafe fn sigaltstack(new: Option) -> io::Result { let mut old = MaybeUninit::::uninit(); let new = option_as_ptr(new.as_ref()); ret(syscall!(__NR_sigaltstack, new, &mut old))?; Ok(old.assume_init()) } #[inline] pub(crate) unsafe fn tkill(tid: Pid, sig: Signal) -> io::Result<()> { ret(syscall_readonly!(__NR_tkill, tid, sig)) } #[inline] pub(crate) unsafe fn sigprocmask(how: How, new: Option<&Sigset>) -> io::Result { let mut old = MaybeUninit::::uninit(); let new = option_as_ptr(new); ret(syscall!( __NR_rt_sigprocmask, how, new, &mut old, size_of::() ))?; Ok(old.assume_init()) } #[inline] pub(crate) fn sigpending() -> Sigset { let mut pending = MaybeUninit::::uninit(); unsafe { ret_infallible(syscall!( __NR_rt_sigpending, &mut pending, size_of::() )); pending.assume_init() } } #[inline] pub(crate) fn sigsuspend(set: &Sigset) -> io::Result<()> { unsafe { ret(syscall_readonly!( __NR_rt_sigsuspend, by_ref(set), size_of::() )) } } #[inline] pub(crate) fn sigwait(set: &Sigset) -> io::Result { unsafe { match Signal::from_raw(ret_c_int(syscall_readonly!( __NR_rt_sigtimedwait, by_ref(set), zero(), zero(), size_of::() ))?) { Some(signum) => Ok(signum), None => Err(io::Errno::NOTSUP), } } } #[inline] pub(crate) fn sigwaitinfo(set: &Sigset) -> io::Result { let mut info = MaybeUninit::::uninit(); unsafe { let _signum = ret_c_int(syscall!( __NR_rt_sigtimedwait, by_ref(set), &mut info, zero(), size_of::() ))?; Ok(info.assume_init()) } } #[inline] pub(crate) fn sigtimedwait(set: &Sigset, timeout: Option) -> io::Result { let mut info = MaybeUninit::::uninit(); let timeout_ptr = option_as_ptr(timeout.as_ref()); // `rt_sigtimedwait_time64` was introduced in Linux 5.1. The old // `rt_sigtimedwait` syscall is not y2038-compatible on 32-bit // architectures. #[cfg(target_pointer_width = "32")] unsafe { match ret_c_int(syscall!( __NR_rt_sigtimedwait_time64, by_ref(set), &mut info, timeout_ptr, size_of::() )) { Ok(_signum) => (), Err(io::Errno::NOSYS) => sigtimedwait_old(set, timeout, &mut info)?, Err(err) => return Err(err), } Ok(info.assume_init()) } #[cfg(target_pointer_width = "64")] unsafe { let _signum = ret_c_int(syscall!( __NR_rt_sigtimedwait, by_ref(set), &mut info, timeout_ptr, size_of::() ))?; Ok(info.assume_init()) } } #[cfg(target_pointer_width = "32")] unsafe fn sigtimedwait_old( set: &Sigset, timeout: Option, info: &mut MaybeUninit, ) -> io::Result<()> { let old_timeout = match timeout { Some(timeout) => Some(__kernel_old_timespec { tv_sec: timeout.tv_sec.try_into().map_err(|_| io::Errno::OVERFLOW)?, tv_nsec: timeout.tv_nsec as _, }), None => None, }; let old_timeout_ptr = option_as_ptr(old_timeout.as_ref()); let _signum = ret_c_int(syscall!( __NR_rt_sigtimedwait, by_ref(set), info, old_timeout_ptr, size_of::() ))?; Ok(()) } #[inline] pub(crate) fn exit_group(code: c::c_int) -> ! { unsafe { syscall_noreturn!(__NR_exit_group, c_int(code)) } } #[inline] pub(crate) unsafe fn brk(addr: *mut c::c_void) -> io::Result<*mut c_void> { // This is non-`readonly`, to prevent loads from being reordered past it. ret_void_star(syscall!(__NR_brk, addr)) }