Skip to content

Commit b0d3681

Browse files
Futex Rework (#1098)
* Add missing futex constants * split futex into individual functions * futex: split backend into val2 and timespec versions, reorganize new functions * Add a few futex tests * prevent futex timeouts from generating dangling pointers * futex: make naming in the backend more consistent * futex: format * futex: make lock_pi2 timeout parameter match the others * return boolean from trylock_pi * mark new futex api as safe rust * use libc variables in libc backend * Revert "use libc variables in libc backend" This reverts commit d20e14d. * don't use libc variables: not set for android
1 parent d028c72 commit b0d3681

File tree

9 files changed

+919
-51
lines changed

9 files changed

+919
-51
lines changed

src/backend/libc/thread/futex.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::backend::c;
33
bitflags::bitflags! {
44
/// `FUTEX_*` flags for use with [`futex`].
55
///
6-
/// [`futex`]: crate::thread::futex
6+
/// [`futex`]: mod@crate::thread::futex
77
#[repr(transparent)]
88
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
99
pub struct FutexFlags: u32 {
@@ -16,7 +16,7 @@ bitflags::bitflags! {
1616

1717
/// `FUTEX_*` operations for use with [`futex`].
1818
///
19-
/// [`futex`]: crate::thread::futex
19+
/// [`futex`]: mod@crate::thread::futex
2020
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
2121
#[repr(u32)]
2222
pub enum FutexOperation {
@@ -40,4 +40,18 @@ pub enum FutexOperation {
4040
TrylockPi = bitcast!(c::FUTEX_TRYLOCK_PI),
4141
/// `FUTEX_WAIT_BITSET`
4242
WaitBitset = bitcast!(c::FUTEX_WAIT_BITSET),
43+
/// `FUTEX_WAKE_BITSET`
44+
WakeBitset = bitcast!(c::FUTEX_WAKE_BITSET),
45+
/// `FUTEX_WAIT_REQUEUE_PI`
46+
WaitRequeuePi = bitcast!(c::FUTEX_WAIT_REQUEUE_PI),
47+
/// `FUTEX_CMP_REQUEUE_PI`
48+
CmpRequeuePi = bitcast!(c::FUTEX_CMP_REQUEUE_PI),
49+
/// `FUTEX_LOCK_PI2`
50+
LockPi2 = bitcast!(c::FUTEX_LOCK_PI2),
4351
}
52+
53+
/// `FUTEX_WAITERS`
54+
pub const FUTEX_WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS;
55+
56+
/// `FUTEX_OWNER_DIED`
57+
pub const FUTEX_OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED;

src/backend/libc/thread/syscalls.rs

Lines changed: 107 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ use crate::thread::{NanosleepRelativeResult, Timespec};
88
#[cfg(all(target_env = "gnu", fix_y2038))]
99
use crate::timespec::LibcTimespec;
1010
use core::mem::MaybeUninit;
11+
use core::sync::atomic::AtomicU32;
1112
#[cfg(linux_kernel)]
1213
use {
14+
super::futex::FutexOperation,
1315
crate::backend::conv::{borrowed_fd, ret_c_int, ret_usize},
1416
crate::fd::BorrowedFd,
1517
crate::pid::Pid,
16-
crate::thread::{FutexFlags, FutexOperation},
18+
crate::thread::FutexFlags,
1719
crate::utils::as_mut_ptr,
1820
};
1921
#[cfg(not(any(
@@ -415,15 +417,87 @@ pub(crate) fn setresgid_thread(
415417
unsafe { ret(setresgid(rgid.as_raw(), egid.as_raw(), sgid.as_raw())) }
416418
}
417419

418-
// TODO: This could be de-multiplexed.
419420
#[cfg(linux_kernel)]
420-
pub(crate) unsafe fn futex(
421-
uaddr: *mut u32,
421+
pub(crate) unsafe fn futex_val2(
422+
uaddr: *const AtomicU32,
422423
op: FutexOperation,
423424
flags: FutexFlags,
424425
val: u32,
425-
utime: *const Timespec,
426-
uaddr2: *mut u32,
426+
val2: u32,
427+
uaddr2: *const AtomicU32,
428+
val3: u32,
429+
) -> io::Result<usize> {
430+
// the least significant four bytes of the timeout pointer are used as `val2`.
431+
// ["the kernel casts the timeout value first to unsigned long, then to uint32_t"](https://man7.org/linux/man-pages/man2/futex.2.html),
432+
// so we perform that exact conversion in reverse to create the pointer.
433+
let timeout = val2 as usize as *const Timespec;
434+
435+
#[cfg(all(
436+
target_pointer_width = "32",
437+
not(any(target_arch = "aarch64", target_arch = "x86_64"))
438+
))]
439+
{
440+
// TODO: Upstream this to the libc crate.
441+
#[allow(non_upper_case_globals)]
442+
const SYS_futex_time64: i32 = linux_raw_sys::general::__NR_futex_time64 as i32;
443+
444+
syscall! {
445+
fn futex_time64(
446+
uaddr: *const AtomicU32,
447+
futex_op: c::c_int,
448+
val: u32,
449+
timeout: *const Timespec,
450+
uaddr2: *const AtomicU32,
451+
val3: u32
452+
) via SYS_futex_time64 -> c::ssize_t
453+
}
454+
455+
ret_usize(futex_time64(
456+
uaddr,
457+
op as i32 | flags.bits() as i32,
458+
val,
459+
timeout,
460+
uaddr2,
461+
val3,
462+
))
463+
}
464+
465+
#[cfg(any(
466+
target_pointer_width = "64",
467+
target_arch = "aarch64",
468+
target_arch = "x86_64"
469+
))]
470+
{
471+
syscall! {
472+
fn futex(
473+
uaddr: *const AtomicU32,
474+
futex_op: c::c_int,
475+
val: u32,
476+
timeout: *const linux_raw_sys::general::__kernel_timespec,
477+
uaddr2: *const AtomicU32,
478+
val3: u32
479+
) via SYS_futex -> c::c_long
480+
}
481+
482+
ret_usize(futex(
483+
uaddr,
484+
op as i32 | flags.bits() as i32,
485+
val,
486+
timeout.cast(),
487+
uaddr2,
488+
val3,
489+
) as isize)
490+
}
491+
}
492+
493+
#[cfg(linux_kernel)]
494+
pub(crate) unsafe fn futex_timeout(
495+
uaddr: *const AtomicU32,
496+
op: FutexOperation,
497+
flags: FutexFlags,
498+
val: u32,
499+
timeout: *const Timespec,
500+
uaddr2: *const AtomicU32,
427501
val3: u32,
428502
) -> io::Result<usize> {
429503
#[cfg(all(
@@ -437,11 +511,11 @@ pub(crate) unsafe fn futex(
437511

438512
syscall! {
439513
fn futex_time64(
440-
uaddr: *mut u32,
514+
uaddr: *const AtomicU32,
441515
futex_op: c::c_int,
442516
val: u32,
443517
timeout: *const Timespec,
444-
uaddr2: *mut u32,
518+
uaddr2: *const AtomicU32,
445519
val3: u32
446520
) via SYS_futex_time64 -> c::ssize_t
447521
}
@@ -450,15 +524,15 @@ pub(crate) unsafe fn futex(
450524
uaddr,
451525
op as i32 | flags.bits() as i32,
452526
val,
453-
utime,
527+
timeout,
454528
uaddr2,
455529
val3,
456530
))
457531
.or_else(|err| {
458532
// See the comments in `rustix_clock_gettime_via_syscall` about
459533
// emulation.
460534
if err == io::Errno::NOSYS {
461-
futex_old(uaddr, op, flags, val, utime, uaddr2, val3)
535+
futex_old_timespec(uaddr, op, flags, val, timeout, uaddr2, val3)
462536
} else {
463537
Err(err)
464538
}
@@ -473,11 +547,11 @@ pub(crate) unsafe fn futex(
473547
{
474548
syscall! {
475549
fn futex(
476-
uaddr: *mut u32,
550+
uaddr: *const AtomicU32,
477551
futex_op: c::c_int,
478552
val: u32,
479553
timeout: *const linux_raw_sys::general::__kernel_timespec,
480-
uaddr2: *mut u32,
554+
uaddr2: *const AtomicU32,
481555
val3: u32
482556
) via SYS_futex -> c::c_long
483557
}
@@ -486,7 +560,7 @@ pub(crate) unsafe fn futex(
486560
uaddr,
487561
op as i32 | flags.bits() as i32,
488562
val,
489-
utime.cast(),
563+
timeout.cast(),
490564
uaddr2,
491565
val3,
492566
) as isize)
@@ -498,35 +572,45 @@ pub(crate) unsafe fn futex(
498572
target_pointer_width = "32",
499573
not(any(target_arch = "aarch64", target_arch = "x86_64"))
500574
))]
501-
unsafe fn futex_old(
502-
uaddr: *mut u32,
575+
unsafe fn futex_old_timespec(
576+
uaddr: *const AtomicU32,
503577
op: FutexOperation,
504578
flags: FutexFlags,
505579
val: u32,
506-
utime: *const Timespec,
507-
uaddr2: *mut u32,
580+
timeout: *const Timespec,
581+
uaddr2: *const AtomicU32,
508582
val3: u32,
509583
) -> io::Result<usize> {
510584
syscall! {
511585
fn futex(
512-
uaddr: *mut u32,
586+
uaddr: *const AtomicU32,
513587
futex_op: c::c_int,
514588
val: u32,
515589
timeout: *const linux_raw_sys::general::__kernel_old_timespec,
516-
uaddr2: *mut u32,
590+
uaddr2: *const AtomicU32,
517591
val3: u32
518592
) via SYS_futex -> c::c_long
519593
}
520594

521-
let old_utime = linux_raw_sys::general::__kernel_old_timespec {
522-
tv_sec: (*utime).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
523-
tv_nsec: (*utime).tv_nsec.try_into().map_err(|_| io::Errno::INVAL)?,
595+
let old_timeout = if timeout.is_null() {
596+
None
597+
} else {
598+
Some(linux_raw_sys::general::__kernel_old_timespec {
599+
tv_sec: (*timeout).tv_sec.try_into().map_err(|_| io::Errno::INVAL)?,
600+
tv_nsec: (*timeout)
601+
.tv_nsec
602+
.try_into()
603+
.map_err(|_| io::Errno::INVAL)?,
604+
})
524605
};
525606
ret_usize(futex(
526607
uaddr,
527608
op as i32 | flags.bits() as i32,
528609
val,
529-
&old_utime,
610+
old_timeout
611+
.as_ref()
612+
.map(|timeout| timeout as *const linux_raw_sys::general::__kernel_old_timespec)
613+
.unwrap_or(core::ptr::null()),
530614
uaddr2,
531615
val3,
532616
) as isize)

src/backend/linux_raw/conv.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -790,11 +790,19 @@ impl<'a, Num: ArgNumber> From<(crate::net::SocketType, crate::net::SocketFlags)>
790790
}
791791

792792
#[cfg(feature = "thread")]
793-
impl<'a, Num: ArgNumber> From<(crate::thread::FutexOperation, crate::thread::FutexFlags)>
794-
for ArgReg<'a, Num>
793+
impl<'a, Num: ArgNumber>
794+
From<(
795+
crate::backend::thread::futex::FutexOperation,
796+
crate::thread::FutexFlags,
797+
)> for ArgReg<'a, Num>
795798
{
796799
#[inline]
797-
fn from(pair: (crate::thread::FutexOperation, crate::thread::FutexFlags)) -> Self {
800+
fn from(
801+
pair: (
802+
crate::backend::thread::futex::FutexOperation,
803+
crate::thread::FutexFlags,
804+
),
805+
) -> Self {
798806
c_uint(pair.0 as u32 | pair.1.bits())
799807
}
800808
}

src/backend/linux_raw/thread/futex.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
bitflags::bitflags! {
22
/// `FUTEX_*` flags for use with [`futex`].
33
///
4-
/// [`futex`]: crate::thread::futex
4+
/// [`futex`]: mod@crate::thread::futex
55
#[repr(transparent)]
66
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
77
pub struct FutexFlags: u32 {
@@ -18,7 +18,7 @@ bitflags::bitflags! {
1818

1919
/// `FUTEX_*` operations for use with [`futex`].
2020
///
21-
/// [`futex`]: crate::thread::futex
21+
/// [`futex`]: mod@crate::thread::futex
2222
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
2323
#[repr(u32)]
2424
pub enum FutexOperation {
@@ -42,4 +42,18 @@ pub enum FutexOperation {
4242
TrylockPi = linux_raw_sys::general::FUTEX_TRYLOCK_PI,
4343
/// `FUTEX_WAIT_BITSET`
4444
WaitBitset = linux_raw_sys::general::FUTEX_WAIT_BITSET,
45+
/// `FUTEX_WAKE_BITSET`
46+
WakeBitset = linux_raw_sys::general::FUTEX_WAKE_BITSET,
47+
/// `FUTEX_WAIT_REQUEUE_PI`
48+
WaitRequeuePi = linux_raw_sys::general::FUTEX_WAIT_REQUEUE_PI,
49+
/// `FUTEX_CMP_REQUEUE_PI`
50+
CmpRequeuePi = linux_raw_sys::general::FUTEX_CMP_REQUEUE_PI,
51+
/// `FUTEX_LOCK_PI2`
52+
LockPi2 = linux_raw_sys::general::FUTEX_LOCK_PI2,
4553
}
54+
55+
/// `FUTEX_WAITERS`
56+
pub const FUTEX_WAITERS: u32 = linux_raw_sys::general::FUTEX_WAITERS;
57+
58+
/// `FUTEX_OWNER_DIED`
59+
pub const FUTEX_OWNER_DIED: u32 = linux_raw_sys::general::FUTEX_OWNER_DIED;

0 commit comments

Comments
 (0)