diff --git a/src/backend/libc/thread/syscalls.rs b/src/backend/libc/thread/syscalls.rs index 036b60d48..27836ebe9 100644 --- a/src/backend/libc/thread/syscalls.rs +++ b/src/backend/libc/thread/syscalls.rs @@ -7,7 +7,14 @@ use crate::io; use crate::thread::{NanosleepRelativeResult, Timespec}; #[cfg(all(target_env = "gnu", fix_y2038))] use crate::timespec::LibcTimespec; +#[cfg(all( + linux_kernel, + target_pointer_width = "32", + not(any(target_arch = "aarch64", target_arch = "x86_64")) +))] +use crate::utils::option_as_ptr; use core::mem::MaybeUninit; +#[cfg(linux_kernel)] use core::sync::atomic::AtomicU32; #[cfg(linux_kernel)] use { @@ -416,6 +423,9 @@ pub(crate) fn setresgid_thread( unsafe { ret(setresgid(rgid.as_raw(), egid.as_raw(), sgid.as_raw())) } } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[cfg(linux_kernel)] pub(crate) unsafe fn futex_val2( uaddr: *const AtomicU32, @@ -426,9 +436,12 @@ pub(crate) unsafe fn futex_val2( uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { - // The least-significant four bytes of the timeout pointer are used as `val2`. - // ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html), - // so we perform that exact conversion in reverse to create the pointer. + // Pass `val2` in the least-significant bytes of the `timeout` argument. + // [“the kernel casts the timeout value first to unsigned long, then to + // uint32_t”], so we perform that exact conversion in reverse to create + // the pointer. + // + // [“the kernel casts the timeout value first to unsigned long, then to uint32_t”]: https://man7.org/linux/man-pages/man2/futex.2.html let timeout = val2 as usize as *const Timespec; #[cfg(all( @@ -489,6 +502,9 @@ pub(crate) unsafe fn futex_val2( } } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[cfg(linux_kernel)] pub(crate) unsafe fn futex_timeout( uaddr: *const AtomicU32, @@ -566,6 +582,9 @@ pub(crate) unsafe fn futex_timeout( } } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[cfg(linux_kernel)] #[cfg(all( target_pointer_width = "32", @@ -606,10 +625,7 @@ unsafe fn futex_old_timespec( uaddr, op as i32 | flags.bits() as i32, val, - old_timeout - .as_ref() - .map(|timeout| timeout as *const linux_raw_sys::general::__kernel_old_timespec) - .unwrap_or(core::ptr::null()), + option_as_ptr(old_timeout.as_ref()), uaddr2, val3, ) as isize) diff --git a/src/backend/linux_raw/thread/syscalls.rs b/src/backend/linux_raw/thread/syscalls.rs index 336453131..a88a8e51e 100644 --- a/src/backend/linux_raw/thread/syscalls.rs +++ b/src/backend/linux_raw/thread/syscalls.rs @@ -16,9 +16,9 @@ use crate::pid::Pid; use crate::thread::{futex, ClockId, NanosleepRelativeResult, Timespec}; use core::mem::MaybeUninit; use core::sync::atomic::AtomicU32; -#[cfg(target_pointer_width = "32")] -use linux_raw_sys::general::timespec as __kernel_old_timespec; use linux_raw_sys::general::{__kernel_timespec, TIMER_ABSTIME}; +#[cfg(target_pointer_width = "32")] +use {crate::utils::option_as_ptr, linux_raw_sys::general::timespec as __kernel_old_timespec}; #[inline] pub(crate) fn clock_nanosleep_relative( @@ -204,6 +204,9 @@ pub(crate) fn gettid() -> Pid { } } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[inline] pub(crate) unsafe fn futex_val2( uaddr: *const AtomicU32, @@ -214,9 +217,12 @@ pub(crate) unsafe fn futex_val2( uaddr2: *const AtomicU32, val3: u32, ) -> io::Result { - // The least-significant four bytes of the timeout pointer are used as `val2`. - // ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html), - // so we perform that exact conversion in reverse to create the pointer. + // Pass `val2` in the least-significant bytes of the `timeout` argument. + // [“the kernel casts the timeout value first to unsigned long, then to + // uint32_t”], so we perform that exact conversion in reverse to create + // the pointer. + // + // [“the kernel casts the timeout value first to unsigned long, then to uint32_t”]: https://man7.org/linux/man-pages/man2/futex.2.html let timeout = val2 as usize as *const Timespec; #[cfg(target_pointer_width = "32")] @@ -243,6 +249,9 @@ pub(crate) unsafe fn futex_val2( )) } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[inline] pub(crate) unsafe fn futex_timeout( uaddr: *const AtomicU32, @@ -286,6 +295,9 @@ pub(crate) unsafe fn futex_timeout( )) } +/// # Safety +/// +/// The raw pointers must point to valid aligned memory. #[cfg(target_pointer_width = "32")] unsafe fn futex_old_timespec( uaddr: *const AtomicU32, @@ -312,10 +324,7 @@ unsafe fn futex_old_timespec( uaddr, (op, flags), c_uint(val), - old_timeout - .as_ref() - .map(|timeout| timeout as *const __kernel_old_timespec) - .unwrap_or(core::ptr::null()), + option_as_ptr(old_timeout.as_ref()), uaddr2, c_uint(val3) )) diff --git a/src/thread/futex.rs b/src/thread/futex.rs index 0cb6687b9..97aa084fd 100644 --- a/src/thread/futex.rs +++ b/src/thread/futex.rs @@ -1,14 +1,25 @@ //! Linux `futex`. //! +//! Futex is a very low-level mechanism for implementing concurrency primitives +//! such as mutexes, rwlocks, and condvars. +//! +//! # Examples +//! +//! ``` +//! use rustix::thread::futex; +//! use std::sync::atomic::AtomicU32; +//! +//! # fn test(futex: &AtomicU32) -> rustix::io::Result<()> { +//! // Wake up one waiter. +//! futex::wake(futex, futex::Flags::PRIVATE, 1)?; +//! # Ok(()) +//! # } +//! ``` + //! # References //! - [Linux `futex` system call] //! - [Linux `futex` feature] //! -//! # Safety -//! -//! Futex is a very low-level mechanism for implementing concurrency -//! primitives. -//! //! [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html //! [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #![allow(unsafe_code)] @@ -20,28 +31,22 @@ use core::sync::atomic::AtomicU32; use crate::backend::thread::futex::Operation; use crate::backend::thread::syscalls::{futex_timeout, futex_val2}; use crate::fd::{FromRawFd, OwnedFd, RawFd}; +use crate::utils::option_as_ptr; use crate::{backend, io}; pub use crate::timespec::Timespec; -pub use backend::thread::futex::Flags; - -/// `FUTEX_WAITERS` -pub const WAITERS: u32 = backend::thread::futex::WAITERS; -/// `FUTEX_OWNER_DIED` -pub const OWNER_DIED: u32 = backend::thread::futex::OWNER_DIED; +pub use backend::thread::futex::{Flags, OWNER_DIED, WAITERS}; /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT, val, timeout, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -51,16 +56,14 @@ pub fn wait( val: u32, timeout: Option, ) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. unsafe { futex_timeout( uaddr, Operation::Wait, flags, val, - timeout - .as_ref() - .map(|timeout| timeout as *const Timespec) - .unwrap_or(ptr::null()), + option_as_ptr(timeout.as_ref()), ptr::null(), 0, ) @@ -75,37 +78,35 @@ pub fn wait( /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE, val, NULL, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pub fn wake(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::Wake, flags, val, 0, ptr::null(), 0) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_FD, val, NULL, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pub fn fd(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::Fd, flags, val, 0, ptr::null(), 0).map(|val| { let fd = val as RawFd; @@ -117,15 +118,13 @@ pub fn fd(uaddr: &AtomicU32, flags: Flags, val: u32) -> io::Result { /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_REQUEUE, val, val2, uaddr2, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -136,20 +135,19 @@ pub fn requeue( val2: u32, uaddr2: &AtomicU32, ) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::Requeue, flags, val, val2, uaddr2, 0) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE, val, val2, uaddr2, val3)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -161,12 +159,14 @@ pub fn cmp_requeue( uaddr2: &AtomicU32, val3: u32, ) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::CmpRequeue, flags, val, val2, uaddr2, val3) } } /// `FUTEX_OP_*` operations for use with [`wake_op`]. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u32)] +#[allow(clippy::identity_op)] pub enum WakeOp { /// `FUTEX_OP_SET`: `uaddr2 = oparg;` Set = 0, @@ -210,18 +210,17 @@ pub enum WakeOpCmp { /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE_OP, val, val2, uaddr2, val3)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] +#[allow(clippy::too_many_arguments)] pub fn wake_op( uaddr: &AtomicU32, flags: Flags, @@ -240,34 +239,31 @@ pub fn wake_op( let val3 = ((op as u32) << 28) | ((cmp as u32) << 24) | ((oparg as u32) << 12) | (cmparg as u32); + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::WakeOp, flags, val, val2, uaddr2, val3) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI, 0, timeout, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pub fn lock_pi(uaddr: &AtomicU32, flags: Flags, timeout: Option) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. unsafe { futex_timeout( uaddr, Operation::LockPi, flags, 0, - timeout - .as_ref() - .map(|timeout| timeout as *const Timespec) - .unwrap_or(ptr::null()), + option_as_ptr(timeout.as_ref()), ptr::null(), 0, ) @@ -282,19 +278,18 @@ pub fn lock_pi(uaddr: &AtomicU32, flags: Flags, timeout: Option) -> io /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_UNLOCK_PI, 0, NULL, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pub fn unlock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::UnlockPi, flags, 0, 0, ptr::null(), 0).map(|val| { debug_assert_eq!( @@ -307,19 +302,18 @@ pub fn unlock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result<()> { /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_TRYLOCK_PI, 0, NULL, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pub fn trylock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::TrylockPi, flags, 0, 0, ptr::null(), 0).map(|ret| ret == 0) } @@ -327,15 +321,13 @@ pub fn trylock_pi(uaddr: &AtomicU32, flags: Flags) -> io::Result { /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_BITSET, val, timeout, NULL, val3)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -346,16 +338,14 @@ pub fn wait_bitset( timeout: Option, val3: NonZeroU32, ) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. unsafe { futex_timeout( uaddr, Operation::WaitBitset, flags, val, - timeout - .as_ref() - .map(|timeout| timeout as *const Timespec) - .unwrap_or(ptr::null()), + option_as_ptr(timeout.as_ref()), ptr::null(), val3.get(), ) @@ -370,15 +360,13 @@ pub fn wait_bitset( /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAKE_BITSET, val, NULL, NULL, val3)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -388,6 +376,7 @@ pub fn wake_bitset( val: u32, val3: NonZeroU32, ) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2( uaddr, @@ -403,15 +392,13 @@ pub fn wake_bitset( /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_WAIT_REQUEUE_PI, val, timeout, uaddr2, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -422,16 +409,14 @@ pub fn wait_requeue_pi( timeout: Option, uaddr2: &AtomicU32, ) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. unsafe { futex_timeout( uaddr, Operation::WaitRequeuePi, flags, val, - timeout - .as_ref() - .map(|timeout| timeout as *const Timespec) - .unwrap_or(ptr::null()), + option_as_ptr(timeout.as_ref()), uaddr2, 0, ) @@ -446,15 +431,13 @@ pub fn wait_requeue_pi( /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_CMP_REQUEUE_PI, 1, val2, uaddr2, val3)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] @@ -465,34 +448,31 @@ pub fn cmp_requeue_pi( uaddr2: &AtomicU32, val3: u32, ) -> io::Result { + // SAFETY: The raw pointers come from references or null. unsafe { futex_val2(uaddr, Operation::CmpRequeuePi, flags, 1, val2, uaddr2, val3) } } /// Equivalent to `syscall(SYS_futex, uaddr, FUTEX_LOCK_PI2, 0, timeout, NULL, 0)` /// +/// This is a very low-level feature for implementing synchronization +/// primitives. See the references links. +/// /// # References /// - [Linux `futex` system call] /// - [Linux `futex` feature] /// -/// # Safety -/// -/// This is a very low-level feature for implementing synchronization -/// primitives. See the references links above. -/// /// [Linux `futex` system call]: https://man7.org/linux/man-pages/man2/futex.2.html /// [Linux `futex` feature]: https://man7.org/linux/man-pages/man7/futex.7.html #[inline] pub fn lock_pi2(uaddr: &AtomicU32, flags: Flags, timeout: Option) -> io::Result<()> { + // SAFETY: The raw pointers come from references or null. unsafe { futex_timeout( uaddr, Operation::LockPi2, flags, 0, - timeout - .as_ref() - .map(|timeout| timeout as *const Timespec) - .unwrap_or(ptr::null()), + option_as_ptr(timeout.as_ref()), ptr::null(), 0, ) diff --git a/src/thread/mod.rs b/src/thread/mod.rs index ce62d8f68..cb22dd271 100644 --- a/src/thread/mod.rs +++ b/src/thread/mod.rs @@ -34,7 +34,8 @@ pub use prctl::*; #[cfg(linux_kernel)] pub use setns::*; -/// DEPRECATED: There are now individual functions available to perform futex operations with improved type safety. See the [futex module]. +/// DEPRECATED: There are now individual functions available to perform futex +/// operations with improved type safety. See the [futex module]. /// /// `futex(uaddr, op, val, utime, uaddr2, val3)` ///