1
1
use crate :: * ;
2
+ use crate :: shims:: unix:: env:: EvalContextExt ;
2
3
3
4
/// Implementation of the SYS_futex syscall.
4
5
/// `args` is the arguments *after* the syscall number.
@@ -15,19 +16,18 @@ pub fn futex<'tcx>(
15
16
// may or may not be left out from the `syscall()` call.
16
17
// Therefore we don't use `check_arg_count` here, but only check for the
17
18
// number of arguments to fall within a range.
18
- let [ addr, op, val , ..] = args else {
19
+ let [ addr, op, ..] = args else {
19
20
throw_ub_format ! (
20
21
"incorrect number of arguments for `futex` syscall: got {}, expected at least 3" ,
21
22
args. len( )
22
23
) ;
23
24
} ;
24
25
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).
27
28
// We checked above that these definitely exist.
28
29
let addr = this. read_pointer ( addr) ?;
29
30
let op = this. read_scalar ( op) ?. to_i32 ( ) ?;
30
- let val = this. read_scalar ( val) ?. to_i32 ( ) ?;
31
31
32
32
// This is a vararg function so we have to bring our own type for this pointer.
33
33
let addr = this. ptr_to_mplace ( addr, this. machine . layouts . i32 ) ;
@@ -39,6 +39,9 @@ pub fn futex<'tcx>(
39
39
let futex_wake = this. eval_libc_i32 ( "FUTEX_WAKE" ) ;
40
40
let futex_wake_bitset = this. eval_libc_i32 ( "FUTEX_WAKE_BITSET" ) ;
41
41
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" ) ;
42
45
43
46
// FUTEX_PRIVATE enables an optimization that stops it from working across processes.
44
47
// Miri doesn't support that anyway, so we ignore that flag.
@@ -51,7 +54,7 @@ pub fn futex<'tcx>(
51
54
// This is identical to FUTEX_WAIT, except:
52
55
// - The timeout is absolute rather than relative.
53
56
// - 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 => {
55
58
let wait_bitset = op & !futex_realtime == futex_wait_bitset;
56
59
57
60
let bitset = if wait_bitset {
@@ -74,6 +77,9 @@ pub fn futex<'tcx>(
74
77
u32:: MAX
75
78
} ;
76
79
80
+
81
+ let val = this. read_scalar ( & args[ 2 ] ) ?. to_u32 ( ) ?;
82
+
77
83
if bitset == 0 {
78
84
this. set_last_error ( LibcError ( "EINVAL" ) ) ?;
79
85
this. write_scalar ( Scalar :: from_target_isize ( -1 , this) , dest) ?;
@@ -152,12 +158,16 @@ pub fn futex<'tcx>(
152
158
//
153
159
// Thankfully, preemptions cannot happen inside a Miri shim, so we do not need to
154
160
// 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
+ } {
160
167
// 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
+ }
161
171
this. futex_wait (
162
172
addr_usize,
163
173
bitset,
@@ -167,6 +177,10 @@ pub fn futex<'tcx>(
167
177
dest. clone ( ) ,
168
178
this. eval_libc ( "ETIMEDOUT" ) ,
169
179
) ;
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) ?;
170
184
} else {
171
185
// The futex value doesn't match the expected value, so we return failure
172
186
// right away without sleeping: -1 and errno set to EAGAIN.
@@ -181,7 +195,9 @@ pub fn futex<'tcx>(
181
195
// Does not access the futex value at *addr.
182
196
// FUTEX_WAKE_BITSET: (int *addr, int op = FUTEX_WAKE, int val, const timespect *_unused, int *_unused, unsigned int bitset)
183
197
// 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
+
185
201
let bitset = if op == futex_wake_bitset {
186
202
let [ _, _, _, timeout, uaddr2, bitset, ..] = args else {
187
203
throw_ub_format ! (
@@ -203,7 +219,11 @@ pub fn futex<'tcx>(
203
219
// Together with the SeqCst fence in futex_wait, this makes sure that futex_wait
204
220
// will see the latest value on addr which could be changed by our caller
205
221
// 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
+ }
207
227
let mut n = 0 ;
208
228
#[ allow( clippy:: arithmetic_side_effects) ]
209
229
for _ in 0 ..val {
@@ -213,7 +233,7 @@ pub fn futex<'tcx>(
213
233
break ;
214
234
}
215
235
}
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) ?;
217
237
}
218
238
op => throw_unsup_format ! ( "Miri does not support `futex` syscall with op={}" , op) ,
219
239
}
0 commit comments