2 use rustc_target::abi::{Align, Size};
4 /// Implementation of the SYS_futex syscall.
6 this: &mut MiriEvalContext<'_, 'tcx>,
7 args: &[OpTy<'tcx, Tag>],
8 dest: PlaceTy<'tcx, Tag>,
9 ) -> InterpResult<'tcx> {
10 // The amount of arguments used depends on the type of futex operation.
11 // The full futex syscall takes six arguments (excluding the syscall
12 // number), which is also the maximum amount of arguments a linux syscall
13 // can take on most architectures.
14 // However, not all futex operations use all six arguments. The unused ones
15 // may or may not be left out from the `syscall()` call.
16 // Therefore we don't use `check_arg_count` here, but only check for the
17 // number of arguments to fall within a range.
18 if !(4..=7).contains(&args.len()) {
19 throw_ub_format!("incorrect number of arguments for futex syscall: got {}, expected between 4 and 7 (inclusive)", args.len());
22 // The first three arguments (after the syscall number itself) are the same to all futex operations:
23 // (int *addr, int op, int val).
24 // Although note that the first one is often passed as a different pointer type, e.g. `*const AtomicU32` or `*mut u32`.
25 let addr = this.deref_operand(args[1])?;
26 let op = this.read_scalar(args[2])?.to_i32()?;
27 let val = this.read_scalar(args[3])?.to_i32()?;
29 // The raw pointer value is used to identify the mutex.
30 // Not all mutex operations actually read from this address or even require this address to exist.
31 let futex_ptr = addr.ptr.assert_ptr();
33 let thread = this.get_active_thread();
35 let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG")?;
36 let futex_wait = this.eval_libc_i32("FUTEX_WAIT")?;
37 let futex_wake = this.eval_libc_i32("FUTEX_WAKE")?;
39 // FUTEX_PRIVATE enables an optimization that stops it from working across processes.
40 // Miri doesn't support that anyway, so we ignore that flag.
41 match op & !futex_private {
42 // FUTEX_WAIT: (int *addr, int op = FUTEX_WAIT, int val, const timespec *timeout)
43 // Blocks the thread if *addr still equals val. Wakes up when FUTEX_WAKE is called on the same address,
44 // or *timeout expires. `timeout == null` for an infinite timeout.
45 op if op == futex_wait => {
47 throw_ub_format!("incorrect number of arguments for FUTEX_WAIT syscall: got {}, expected at least 5", args.len());
49 let timeout = this.read_scalar(args[4])?.check_init()?;
50 if !this.is_null(timeout)? {
51 // FIXME: Implement timeouts. The condvar waiting code is probably a good example to start with.
52 // Note that a triggered timeout should have this syscall return with -1 and errno set to ETIMEOUT.
53 throw_ub_format!("miri does not support timeouts for futex operations");
55 // Check the pointer for alignment. Atomic operations are only available for fully aligned values.
56 this.memory.check_ptr_access(addr.ptr.into(), Size::from_bytes(4), Align::from_bytes(4).unwrap())?;
57 // Read an `i32` through the pointer, regardless of any wrapper types (e.g. `AtomicI32`).
58 let futex_val = this.read_scalar(addr.offset(Size::ZERO, MemPlaceMeta::None, this.machine.layouts.i32, this)?.into())?.to_i32()?;
60 // The value still matches, so we block the trait make it wait for FUTEX_WAKE.
61 this.block_thread(thread);
62 this.futex_wait(futex_ptr, thread);
63 // Succesfully waking up from FUTEX_WAIT always returns zero.
64 this.write_scalar(Scalar::from_i32(0), dest)?;
66 // The futex value doesn't match the expected value, so we return failure
67 // right away without sleeping: -1 and errno set to EAGAIN.
68 let eagain = this.eval_libc("EAGAIN")?;
69 this.set_last_error(eagain)?;
70 this.write_scalar(Scalar::from_i32(-1), dest)?;
73 // FUTEX_WAKE: (int *addr, int op = FUTEX_WAKE, int val)
74 // Wakes at most `val` threads waiting on the futex at `addr`.
75 // Returns the amount of threads woken up.
76 // Does not access the futex value at *addr.
77 op if op == futex_wake => {
80 if let Some(thread) = this.futex_wake(futex_ptr) {
81 this.unblock_thread(thread);
87 this.write_scalar(Scalar::from_i32(n), dest)?;
89 op => throw_unsup_format!("miri does not support SYS_futex operation {}", op),