@@ -187,6 +187,13 @@ pub fn futex<'tcx>(
187
187
// It's not uncommon for `addr` to be passed as another type than `*mut i32`, such as `*const AtomicI32`.
188
188
let futex_val = this. read_scalar_atomic ( & addr, AtomicReadOrd :: Relaxed ) ?. to_i32 ( ) ?;
189
189
if val == futex_val {
190
+ // Check that the top waiter (if exists) is waiting using FUTEX_WAIT_*.
191
+ if this. futex_waiter_count ( addr_usize) != 0 && this. futex_top_waiter_extra ( addr_usize) . is_some ( ) {
192
+ this. set_last_error ( LibcError ( "EINVAL" ) ) ?;
193
+ this. write_scalar ( Scalar :: from_target_isize ( -1 , this) , dest) ?;
194
+ return interp_ok ( ( ) ) ;
195
+ }
196
+
190
197
// The value still matches, so we block the thread and make it wait for FUTEX_WAKE.
191
198
this. futex_wait (
192
199
addr_usize,
@@ -245,6 +252,16 @@ pub fn futex<'tcx>(
245
252
// before doing the syscall.
246
253
this. atomic_fence ( AtomicFenceOrd :: SeqCst ) ?;
247
254
let mut n = 0 ;
255
+
256
+ // Check that the waiter is waiting using FUTEX_WAIT_*.
257
+ if this. futex_waiter_count ( addr_usize) != 0 {
258
+ if this. futex_top_waiter_extra ( addr_usize) . is_some ( ) {
259
+ this. set_last_error ( LibcError ( "EINVAL" ) ) ?;
260
+ this. write_scalar ( Scalar :: from_target_isize ( -1 , this) , dest) ?;
261
+ return interp_ok ( ( ) ) ;
262
+ }
263
+ }
264
+
248
265
#[ allow( clippy:: arithmetic_side_effects) ]
249
266
for _ in 0 ..val {
250
267
if this. futex_wake ( addr_usize, bitset) ? {
@@ -280,6 +297,15 @@ pub fn futex<'tcx>(
280
297
281
298
if futex_val == 0 {
282
299
// 0 means unlocked - then lock it.
300
+
301
+ // Check that there is no waiters.
302
+ if this. futex_waiter_count ( addr_usize) != 0 {
303
+ this. set_last_error ( LibcError ( "EINVAL" ) ) ?;
304
+ this. write_scalar ( Scalar :: from_target_isize ( -1 , this) , dest) ?;
305
+ return interp_ok ( ( ) ) ;
306
+ }
307
+
308
+ // Declare ownership.
283
309
this. write_scalar_atomic ( Scalar :: from_u32 ( tid) , & addr, AtomicWriteOrd :: Relaxed ) ?;
284
310
285
311
// This ensures all loads afterwards get updated value of *addr.
@@ -293,15 +319,26 @@ pub fn futex<'tcx>(
293
319
this. write_scalar ( Scalar :: from_target_isize ( -1 , this) , dest) ?;
294
320
} else {
295
321
// Other values mean locked.
296
- // Mark the futex as contended.
297
- this. write_scalar_atomic (
298
- Scalar :: from_u32 ( futex_val | futex_waiters) ,
299
- & addr,
300
- AtomicWriteOrd :: Relaxed ,
301
- ) ?;
302
322
303
- // This ensures all loads afterwards get updated value of *addr.
304
- this. atomic_fence ( AtomicFenceOrd :: SeqCst ) ?;
323
+ if this. futex_waiter_count ( addr_usize) == 0 {
324
+ // Mark the futex as contended.
325
+ this. write_scalar_atomic (
326
+ Scalar :: from_u32 ( futex_val | futex_waiters) ,
327
+ & addr,
328
+ AtomicWriteOrd :: Relaxed ,
329
+ ) ?;
330
+
331
+ // This ensures all loads afterwards get updated value of *addr.
332
+ this. atomic_fence ( AtomicFenceOrd :: SeqCst ) ?;
333
+ } else {
334
+ // Check that the futex has been marked as contended.
335
+ // Check that the top waiter is waiting using FUTEX_LOCK_PI.
336
+ if ( futex_val & futex_waiters) == 0 || this. futex_top_waiter_extra ( addr_usize) . is_none ( ) {
337
+ this. set_last_error ( LibcError ( "EINVAL" ) ) ?;
338
+ this. write_scalar ( Scalar :: from_target_isize ( -1 , this) , dest) ?;
339
+ return interp_ok ( ( ) ) ;
340
+ }
341
+ }
305
342
306
343
// Put ourselves into the wait queue.
307
344
this. futex_wait (
0 commit comments