Skip to content

Commit 29f3341

Browse files
committed
Implements miri shims for FUTEX_LOCK_PI, FUTEX_UNLOCK_PI
1 parent d7a763d commit 29f3341

File tree

1 file changed

+33
-13
lines changed
  • src/tools/miri/src/shims/unix/linux

1 file changed

+33
-13
lines changed

src/tools/miri/src/shims/unix/linux/sync.rs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::*;
2+
use crate::shims::unix::env::EvalContextExt;
23

34
/// Implementation of the SYS_futex syscall.
45
/// `args` is the arguments *after* the syscall number.
@@ -15,19 +16,18 @@ pub fn futex<'tcx>(
1516
// may or may not be left out from the `syscall()` call.
1617
// Therefore we don't use `check_arg_count` here, but only check for the
1718
// number of arguments to fall within a range.
18-
let [addr, op, val, ..] = args else {
19+
let [addr, op, ..] = args else {
1920
throw_ub_format!(
2021
"incorrect number of arguments for `futex` syscall: got {}, expected at least 3",
2122
args.len()
2223
);
2324
};
2425

25-
// The first three arguments (after the syscall number itself) are the same to all futex operations:
26-
// (int *addr, int op, int val).
26+
// The first two arguments (after the syscall number itself) are the same to all futex operations:
27+
// (int *addr, int op).
2728
// We checked above that these definitely exist.
2829
let addr = this.read_pointer(addr)?;
2930
let op = this.read_scalar(op)?.to_i32()?;
30-
let val = this.read_scalar(val)?.to_i32()?;
3131

3232
// This is a vararg function so we have to bring our own type for this pointer.
3333
let addr = this.ptr_to_mplace(addr, this.machine.layouts.i32);
@@ -39,6 +39,9 @@ pub fn futex<'tcx>(
3939
let futex_wake = this.eval_libc_i32("FUTEX_WAKE");
4040
let futex_wake_bitset = this.eval_libc_i32("FUTEX_WAKE_BITSET");
4141
let futex_realtime = this.eval_libc_i32("FUTEX_CLOCK_REALTIME");
42+
let futex_lock_pi = this.eval_libc_i32("FUTEX_LOCK_PI");
43+
let futex_unlock_pi = this.eval_libc_i32("FUTEX_UNLOCK_PI");
44+
let futex_waiters = this.eval_libc_u32("FUTEX_WAITERS");
4245

4346
// FUTEX_PRIVATE enables an optimization that stops it from working across processes.
4447
// Miri doesn't support that anyway, so we ignore that flag.
@@ -51,7 +54,7 @@ pub fn futex<'tcx>(
5154
// This is identical to FUTEX_WAIT, except:
5255
// - The timeout is absolute rather than relative.
5356
// - You can specify the bitset to selecting what WAKE operations to respond to.
54-
op if op & !futex_realtime == futex_wait || op & !futex_realtime == futex_wait_bitset => {
57+
op if op & !futex_realtime == futex_wait || op & !futex_realtime == futex_wait_bitset || op == futex_lock_pi => {
5558
let wait_bitset = op & !futex_realtime == futex_wait_bitset;
5659

5760
let bitset = if wait_bitset {
@@ -74,6 +77,9 @@ pub fn futex<'tcx>(
7477
u32::MAX
7578
};
7679

80+
81+
let val = this.read_scalar(&args[2])?.to_u32()?;
82+
7783
if bitset == 0 {
7884
this.set_last_error(LibcError("EINVAL"))?;
7985
this.write_scalar(Scalar::from_target_isize(-1, this), dest)?;
@@ -152,12 +158,16 @@ pub fn futex<'tcx>(
152158
//
153159
// Thankfully, preemptions cannot happen inside a Miri shim, so we do not need to
154160
// do anything special to guarantee fence-load-comparison atomicity.
155-
this.atomic_fence(AtomicFenceOrd::SeqCst)?;
156-
// Read an `i32` through the pointer, regardless of any wrapper types.
157-
// It's not uncommon for `addr` to be passed as another type than `*mut i32`, such as `*const AtomicI32`.
158-
let futex_val = this.read_scalar_atomic(&addr, AtomicReadOrd::Relaxed)?.to_i32()?;
159-
if val == futex_val {
161+
let futex_val = this.read_scalar_atomic(&addr, AtomicReadOrd::SeqCst)?.to_u32()?;
162+
if if op == futex_lock_pi {
163+
futex_val != 0
164+
} else {
165+
futex_val == val
166+
} {
160167
// The value still matches, so we block the thread and make it wait for FUTEX_WAKE.
168+
if op == futex_lock_pi {
169+
this.write_scalar_atomic(Scalar::from_u32(val | futex_waiters), &addr , AtomicWriteOrd::SeqCst)?;
170+
}
161171
this.futex_wait(
162172
addr_usize,
163173
bitset,
@@ -167,6 +177,10 @@ pub fn futex<'tcx>(
167177
dest.clone(),
168178
this.eval_libc("ETIMEDOUT"),
169179
);
180+
} else if op == futex_lock_pi {
181+
let tid = this.linux_gettid()?;
182+
this.write_scalar_atomic(tid, &addr, AtomicWriteOrd::SeqCst)?;
183+
this.write_scalar(Scalar::from_target_isize(0, this), dest)?;
170184
} else {
171185
// The futex value doesn't match the expected value, so we return failure
172186
// right away without sleeping: -1 and errno set to EAGAIN.
@@ -181,7 +195,9 @@ pub fn futex<'tcx>(
181195
// Does not access the futex value at *addr.
182196
// FUTEX_WAKE_BITSET: (int *addr, int op = FUTEX_WAKE, int val, const timespect *_unused, int *_unused, unsigned int bitset)
183197
// Same as FUTEX_WAKE, but allows you to specify a bitset to select which threads to wake up.
184-
op if op == futex_wake || op == futex_wake_bitset => {
198+
op if op == futex_wake || op == futex_wake_bitset || op == futex_unlock_pi => {
199+
let val = if op == futex_unlock_pi { 1 } else { this.read_scalar(&args[2])?.to_i32()? };
200+
185201
let bitset = if op == futex_wake_bitset {
186202
let [_, _, _, timeout, uaddr2, bitset, ..] = args else {
187203
throw_ub_format!(
@@ -203,7 +219,11 @@ pub fn futex<'tcx>(
203219
// Together with the SeqCst fence in futex_wait, this makes sure that futex_wait
204220
// will see the latest value on addr which could be changed by our caller
205221
// before doing the syscall.
206-
this.atomic_fence(AtomicFenceOrd::SeqCst)?;
222+
if op == futex_unlock_pi {
223+
this.write_scalar_atomic(Scalar::from_u32(0), &addr, AtomicWriteOrd::SeqCst)?;
224+
} else {
225+
this.atomic_fence(AtomicFenceOrd::SeqCst)?;
226+
}
207227
let mut n = 0;
208228
#[allow(clippy::arithmetic_side_effects)]
209229
for _ in 0..val {
@@ -213,7 +233,7 @@ pub fn futex<'tcx>(
213233
break;
214234
}
215235
}
216-
this.write_scalar(Scalar::from_target_isize(n, this), dest)?;
236+
this.write_scalar(Scalar::from_target_isize(if op != futex_unlock_pi { n } else { 0 }, this), dest)?;
217237
}
218238
op => throw_unsup_format!("Miri does not support `futex` syscall with op={}", op),
219239
}

0 commit comments

Comments
 (0)