1313use std:: cell:: Cell ;
1414use std:: time:: Duration ;
1515
16- use rustc_abi:: Size ;
16+ use rustc_abi:: { Endian , FieldIdx , Size } ;
1717
1818use crate :: concurrency:: sync:: { FutexRef , SyncObj } ;
1919use crate :: * ;
2020
2121#[ derive( Clone ) ]
2222enum MacOsUnfairLock {
23- Poisoned ,
2423 Active { mutex_ref : MutexRef } ,
24+ PermanentlyLocked ,
2525}
2626
27- impl SyncObj for MacOsUnfairLock { }
27+ impl SyncObj for MacOsUnfairLock {
28+ fn delete_on_write ( & self ) -> bool {
29+ true
30+ }
31+ }
2832
2933pub enum MacOsFutexTimeout < ' a , ' tcx > {
3034 None ,
@@ -57,22 +61,35 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
5761 where
5862 ' tcx : ' a ,
5963 {
64+ // `os_unfair_lock_s` wraps a single `u32` field. We use the first byte to store the "init"
65+ // flag. Due to macOS always being little endian, that's the least significant byte.
6066 let this = self . eval_context_mut ( ) ;
67+ assert ! ( this. tcx. data_layout. endian == Endian :: Little ) ;
68+
6169 let lock = this. deref_pointer_as ( lock_ptr, this. libc_ty_layout ( "os_unfair_lock_s" ) ) ?;
62- this. lazy_sync_get_data (
70+ this. get_immovable_sync_with_static_init (
6371 & lock,
6472 Size :: ZERO , // offset for init tracking
65- || {
66- // If we get here, due to how we reset things to zero in `os_unfair_lock_unlock`,
67- // this means the lock was moved while locked. This can happen with a `std` lock,
68- // but then any future attempt to unlock will just deadlock. In practice, terrible
69- // things can probably happen if you swap two locked locks, since they'd wake up
70- // from the wrong queue... we just won't catch all UB of this library API then (we
71- // would need to store some unique identifer in-memory for this, instead of a static
72- // LAZY_INIT_COOKIE). This can't be hit via `std::sync::Mutex`.
73- interp_ok ( MacOsUnfairLock :: Poisoned )
73+ /* uninit_val */ 0 ,
74+ /* init_val */ 1 ,
75+ |this| {
76+ let field = this. project_field ( & lock, FieldIdx :: from_u32 ( 0 ) ) ?;
77+ let val = this. read_scalar ( & field) ?. to_u32 ( ) ?;
78+ if val == 0 {
79+ interp_ok ( MacOsUnfairLock :: Active { mutex_ref : MutexRef :: new ( ) } )
80+ } else if val == 1 {
81+ // This is a lock that got copied while it is initialized. We de-initialize
82+ // locks when they get released, so it got copied while locked. Unfortunately
83+ // that is something `std` needs to support (the guard could have been leaked).
84+ // So we behave like a futex-based lock whose wait queue got pruned: any attempt
85+ // to acquire the lock will just wait forever.
86+ // In practice there actually could be a wait queue there, if someone moves a
87+ // lock *while threads are queued*; this is UB we will not detect.
88+ interp_ok ( MacOsUnfairLock :: PermanentlyLocked )
89+ } else {
90+ throw_ub_format ! ( "`os_unfair_lock` was not properly initialized at this location, or it got overwritten" ) ;
91+ }
7492 } ,
75- |_| interp_ok ( MacOsUnfairLock :: Active { mutex_ref : MutexRef :: new ( ) } ) ,
7693 )
7794 }
7895}
@@ -336,7 +353,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
336353 let this = self . eval_context_mut ( ) ;
337354
338355 let MacOsUnfairLock :: Active { mutex_ref } = this. os_unfair_lock_get_data ( lock_op) ? else {
339- // The lock is poisoned, who knows who owns it... we'll pretend: someone else .
356+ // A perma-locked lock is definitely not held by us .
340357 throw_machine_stop ! ( TerminationInfo :: Abort (
341358 "attempted to unlock an os_unfair_lock not owned by the current thread" . to_owned( )
342359 ) ) ;
@@ -365,7 +382,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
365382 let this = self . eval_context_mut ( ) ;
366383
367384 let MacOsUnfairLock :: Active { mutex_ref } = this. os_unfair_lock_get_data ( lock_op) ? else {
368- // The lock is poisoned, who knows who owns it... we'll pretend: someone else .
385+ // A perma-locked lock is definitely not held by us .
369386 throw_machine_stop ! ( TerminationInfo :: Abort (
370387 "called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread" . to_owned( )
371388 ) ) ;
@@ -387,7 +404,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
387404 let this = self . eval_context_mut ( ) ;
388405
389406 let MacOsUnfairLock :: Active { mutex_ref } = this. os_unfair_lock_get_data ( lock_op) ? else {
390- // The lock is poisoned, who knows who owns it... we'll pretend: someone else .
407+ // A perma-locked lock is definitely not held by us .
391408 return interp_ok ( ( ) ) ;
392409 } ;
393410 let mutex_ref = mutex_ref. clone ( ) ;
0 commit comments