]> git.lizzy.rs Git - rust.git/blob - src/shims/posix/linux/sync.rs
Update note about number of arguments to SYS_futex.
[rust.git] / src / shims / posix / linux / sync.rs
1 use crate::*;
2 use rustc_target::abi::{Align, Size};
3
4 /// Implementation of the SYS_futex syscall.
5 pub fn futex<'tcx>(
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());
20     }
21
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()?;
28
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();
32
33     let thread = this.get_active_thread();
34
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")?;
38
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 => {
46             if args.len() < 5 {
47                 throw_ub_format!("incorrect number of arguments for FUTEX_WAIT syscall: got {}, expected at least 5", args.len());
48             }
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");
54             }
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()?;
59             if val == futex_val {
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)?;
65             } else {
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)?;
71             }
72         }
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 => {
78             let mut n = 0;
79             for _ in 0..val {
80                 if let Some(thread) = this.futex_wake(futex_ptr) {
81                     this.unblock_thread(thread);
82                     n += 1;
83                 } else {
84                     break;
85                 }
86             }
87             this.write_scalar(Scalar::from_i32(n), dest)?;
88         }
89         op => throw_unsup_format!("miri does not support SYS_futex operation {}", op),
90     }
91
92     Ok(())
93 }