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 // Some users always pass all arguments, even the unused ones, due to how they wrap this syscall in their code base.
12 // Some other users pass only the arguments the operation actually needs. So we don't use `check_arg_count` here.
13 if !(4..=7).contains(&args.len()) {
14 throw_ub_format!("incorrect number of arguments for futex syscall: got {}, expected between 4 and 7 (inclusive)", args.len());
17 // The first three arguments (after the syscall number itself) are the same to all futex operations:
18 // (int *addr, int op, int val).
19 // Although note that the first one is often passed as a different pointer type, e.g. `*const AtomicU32` or `*mut u32`.
20 let addr = this.deref_operand(args[1])?;
21 let op = this.read_scalar(args[2])?.to_i32()?;
22 let val = this.read_scalar(args[3])?.to_i32()?;
24 // The raw pointer value is used to identify the mutex.
25 // Not all mutex operations actually read from this address or even require this address to exist.
26 let futex_ptr = addr.ptr.assert_ptr();
28 let thread = this.get_active_thread();
30 let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG")?;
31 let futex_wait = this.eval_libc_i32("FUTEX_WAIT")?;
32 let futex_wake = this.eval_libc_i32("FUTEX_WAKE")?;
34 // FUTEX_PRIVATE enables an optimization that stops it from working across processes.
35 // Miri doesn't support that anyway, so we ignore that flag.
36 match op & !futex_private {
37 // FUTEX_WAIT: (int *addr, int op = FUTEX_WAIT, int val, const timespec *timeout)
38 // Blocks the thread if *addr still equals val. Wakes up when FUTEX_WAKE is called on the same address,
39 // or *timeout expires. `timeout == null` for an infinite timeout.
40 op if op == futex_wait => {
42 throw_ub_format!("incorrect number of arguments for FUTEX_WAIT syscall: got {}, expected at least 5", args.len());
44 let timeout = this.read_scalar(args[4])?.check_init()?;
45 if !this.is_null(timeout)? {
46 // FIXME: Implement timeouts. The condvar waiting code is probably a good example to start with.
47 // Note that a triggered timeout should have this syscall return with -1 and errno set to ETIMEOUT.
48 throw_ub_format!("miri does not support timeouts for futex operations");
50 // Check the pointer for alignment. Atomic operations are only available for fully aligned values.
51 this.memory.check_ptr_access(addr.ptr.into(), Size::from_bytes(4), Align::from_bytes(4).unwrap())?;
52 // Read an `i32` through the pointer, regardless of any wrapper types (e.g. `AtomicI32`).
53 let futex_val = this.read_scalar(addr.offset(Size::ZERO, MemPlaceMeta::None, this.machine.layouts.i32, this)?.into())?.to_i32()?;
55 // The value still matches, so we block the trait make it wait for FUTEX_WAKE.
56 this.block_thread(thread);
57 this.futex_wait(futex_ptr, thread);
58 // Succesfully waking up from FUTEX_WAIT always returns zero.
59 this.write_scalar(Scalar::from_i32(0), dest)?;
61 // The futex value doesn't match the expected value, so we return failure
62 // right away without sleeping: -1 and errno set to EAGAIN.
63 let eagain = this.eval_libc("EAGAIN")?;
64 this.set_last_error(eagain)?;
65 this.write_scalar(Scalar::from_i32(-1), dest)?;
68 // FUTEX_WAKE: (int *addr, int op = FUTEX_WAKE, int val)
69 // Wakes at most `val` threads waiting on the futex at `addr`.
70 // Returns the amount of threads woken up.
71 // Does not access the futex value at *addr.
72 op if op == futex_wake => {
75 if let Some(thread) = this.futex_wake(futex_ptr) {
76 this.unblock_thread(thread);
82 this.write_scalar(Scalar::from_i32(n), dest)?;
84 op => throw_unsup_format!("miri does not support SYS_futex operation {}", op),