1+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
12use std:: time:: SystemTime ;
23
4+ use rustc_target:: abi:: Size ;
5+
36use crate :: concurrency:: thread:: MachineCallback ;
47use crate :: * ;
58
@@ -71,23 +74,54 @@ fn is_mutex_kind_normal<'mir, 'tcx: 'mir>(
7174// - id: u32
7275// - kind: i32
7376
74- #[ inline]
7577fn mutex_id_offset < ' mir , ' tcx : ' mir > ( ecx : & MiriInterpCx < ' mir , ' tcx > ) -> InterpResult < ' tcx , u64 > {
76- // When adding a new OS, make sure its PTHREAD_MUTEX_INITIALIZER is 0 for this offset
77- // (and the `mutex_kind_offset` below).
78- Ok ( match & * ecx. tcx . sess . target . os {
78+ let offset = match & * ecx. tcx . sess . target . os {
7979 "linux" | "illumos" | "solaris" => 0 ,
8080 // macOS stores a signature in the first bytes, so we have to move to offset 4.
8181 "macos" => 4 ,
8282 os => throw_unsup_format ! ( "`pthread_mutex` is not supported on {os}" ) ,
83- } )
83+ } ;
84+
85+ // Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
86+ // the id must start out as 0.
87+ static SANITY : AtomicBool = AtomicBool :: new ( false ) ;
88+ if !SANITY . swap ( true , Ordering :: Relaxed ) {
89+ let static_initializer = ecx. eval_path ( & [ "libc" , "PTHREAD_MUTEX_INITIALIZER" ] ) ;
90+ let id_field = static_initializer
91+ . offset ( Size :: from_bytes ( offset) , ecx. machine . layouts . u32 , ecx)
92+ . unwrap ( ) ;
93+ let id = ecx. read_scalar ( & id_field) . unwrap ( ) . to_u32 ( ) . unwrap ( ) ;
94+ assert_eq ! (
95+ id, 0 ,
96+ "PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: id is not 0"
97+ ) ;
98+ }
99+
100+ Ok ( offset)
84101}
85102
86- #[ inline]
87103fn mutex_kind_offset < ' mir , ' tcx : ' mir > ( ecx : & MiriInterpCx < ' mir , ' tcx > ) -> u64 {
88104 // These offsets are picked for compatibility with Linux's static initializer
89105 // macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.)
90- if ecx. pointer_size ( ) . bytes ( ) == 8 { 16 } else { 12 }
106+ let offset = if ecx. pointer_size ( ) . bytes ( ) == 8 { 16 } else { 12 } ;
107+
108+ // Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
109+ // the kind must start out as PTHREAD_MUTEX_DEFAULT.
110+ static SANITY : AtomicBool = AtomicBool :: new ( false ) ;
111+ if !SANITY . swap ( true , Ordering :: Relaxed ) {
112+ let static_initializer = ecx. eval_path ( & [ "libc" , "PTHREAD_MUTEX_INITIALIZER" ] ) ;
113+ let kind_field = static_initializer
114+ . offset ( Size :: from_bytes ( mutex_kind_offset ( ecx) ) , ecx. machine . layouts . i32 , ecx)
115+ . unwrap ( ) ;
116+ let kind = ecx. read_scalar ( & kind_field) . unwrap ( ) . to_i32 ( ) . unwrap ( ) ;
117+ assert_eq ! (
118+ kind,
119+ ecx. eval_libc_i32( "PTHREAD_MUTEX_DEFAULT" ) ,
120+ "PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: kind is not PTHREAD_MUTEX_DEFAULT"
121+ ) ;
122+ }
123+
124+ offset
91125}
92126
93127fn mutex_get_id < ' mir , ' tcx : ' mir > (
@@ -108,7 +142,7 @@ fn mutex_reset_id<'mir, 'tcx: 'mir>(
108142 ecx. deref_pointer_and_write (
109143 mutex_op,
110144 mutex_id_offset ( ecx) ?,
111- Scalar :: from_i32 ( 0 ) ,
145+ Scalar :: from_u32 ( 0 ) ,
112146 ecx. libc_ty_layout ( "pthread_mutex_t" ) ,
113147 ecx. machine . layouts . u32 ,
114148 )
@@ -145,15 +179,30 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>(
145179// We ignore the platform layout and store our own fields:
146180// - id: u32
147181
148- #[ inline]
149182fn rwlock_id_offset < ' mir , ' tcx : ' mir > ( ecx : & MiriInterpCx < ' mir , ' tcx > ) -> InterpResult < ' tcx , u64 > {
150- // When adding a new OS, make sure its PTHREAD_RWLOCK_INITIALIZER is 0 for this offset.
151- Ok ( match & * ecx. tcx . sess . target . os {
183+ let offset = match & * ecx. tcx . sess . target . os {
152184 "linux" | "illumos" | "solaris" => 0 ,
153185 // macOS stores a signature in the first bytes, so we have to move to offset 4.
154186 "macos" => 4 ,
155187 os => throw_unsup_format ! ( "`pthread_rwlock` is not supported on {os}" ) ,
156- } )
188+ } ;
189+
190+ // Sanity-check this against PTHREAD_RWLOCK_INITIALIZER (but only once):
191+ // the id must start out as 0.
192+ static SANITY : AtomicBool = AtomicBool :: new ( false ) ;
193+ if !SANITY . swap ( true , Ordering :: Relaxed ) {
194+ let static_initializer = ecx. eval_path ( & [ "libc" , "PTHREAD_RWLOCK_INITIALIZER" ] ) ;
195+ let id_field = static_initializer
196+ . offset ( Size :: from_bytes ( offset) , ecx. machine . layouts . u32 , ecx)
197+ . unwrap ( ) ;
198+ let id = ecx. read_scalar ( & id_field) . unwrap ( ) . to_u32 ( ) . unwrap ( ) ;
199+ assert_eq ! (
200+ id, 0 ,
201+ "PTHREAD_RWLOCK_INITIALIZER is incompatible with our pthread_rwlock layout: id is not 0"
202+ ) ;
203+ }
204+
205+ Ok ( offset)
157206}
158207
159208fn rwlock_get_id < ' mir , ' tcx : ' mir > (
@@ -214,21 +263,64 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>(
214263// - id: u32
215264// - clock: i32
216265
217- #[ inline]
218266fn cond_id_offset < ' mir , ' tcx : ' mir > ( ecx : & MiriInterpCx < ' mir , ' tcx > ) -> InterpResult < ' tcx , u64 > {
219- // When adding a new OS, make sure its PTHREAD_COND_INITIALIZER is 0 for this offset
220- // (and the `COND_CLOCK_OFFSET` below is initialized to `CLOCK_REALTIME`).
221- Ok ( match & * ecx. tcx . sess . target . os {
267+ let offset = match & * ecx. tcx . sess . target . os {
222268 "linux" | "illumos" | "solaris" => 0 ,
223269 // macOS stores a signature in the first bytes, so we have to move to offset 4.
224270 "macos" => 4 ,
225271 os => throw_unsup_format ! ( "`pthread_cond` is not supported on {os}" ) ,
226- } )
272+ } ;
273+
274+ // Sanity-check this against PTHREAD_COND_INITIALIZER (but only once):
275+ // the id must start out as 0.
276+ static SANITY : AtomicBool = AtomicBool :: new ( false ) ;
277+ if !SANITY . swap ( true , Ordering :: Relaxed ) {
278+ let static_initializer = ecx. eval_path ( & [ "libc" , "PTHREAD_COND_INITIALIZER" ] ) ;
279+ let id_field = static_initializer
280+ . offset ( Size :: from_bytes ( offset) , ecx. machine . layouts . u32 , ecx)
281+ . unwrap ( ) ;
282+ let id = ecx. read_scalar ( & id_field) . unwrap ( ) . to_u32 ( ) . unwrap ( ) ;
283+ assert_eq ! (
284+ id, 0 ,
285+ "PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: id is not 0"
286+ ) ;
287+ }
288+
289+ Ok ( offset)
290+ }
291+
292+ /// Determines whether this clock represents the real-time clock, CLOCK_REALTIME.
293+ fn is_cond_clock_realtime < ' mir , ' tcx : ' mir > ( ecx : & MiriInterpCx < ' mir , ' tcx > , clock_id : i32 ) -> bool {
294+ // To ensure compatibility with PTHREAD_COND_INITIALIZER on all platforms,
295+ // we can't just compare with CLOCK_REALTIME: on Solarish, PTHREAD_COND_INITIALIZER
296+ // makes the clock 0 but CLOCK_REALTIME is 3.
297+ // However, we need to always be able to distinguish this from CLOCK_MONOTONIC.
298+ clock_id == ecx. eval_libc_i32 ( "CLOCK_REALTIME" )
299+ || ( clock_id == 0 && clock_id != ecx. eval_libc_i32 ( "CLOCK_MONOTONIC" ) )
227300}
228301
229- // macOS doesn't have a clock attribute, but to keep the code uniform we store
230- // a clock ID in the pthread_cond_t anyway. There's enough space.
231- const COND_CLOCK_OFFSET : u64 = 8 ;
302+ fn cond_clock_offset < ' mir , ' tcx : ' mir > ( ecx : & MiriInterpCx < ' mir , ' tcx > ) -> u64 {
303+ // macOS doesn't have a clock attribute, but to keep the code uniform we store
304+ // a clock ID in the pthread_cond_t anyway. There's enough space.
305+ let offset = 8 ;
306+
307+ // Sanity-check this against PTHREAD_COND_INITIALIZER (but only once):
308+ // the clock must start out as CLOCK_REALTIME.
309+ static SANITY : AtomicBool = AtomicBool :: new ( false ) ;
310+ if !SANITY . swap ( true , Ordering :: Relaxed ) {
311+ let static_initializer = ecx. eval_path ( & [ "libc" , "PTHREAD_COND_INITIALIZER" ] ) ;
312+ let id_field = static_initializer
313+ . offset ( Size :: from_bytes ( offset) , ecx. machine . layouts . i32 , ecx)
314+ . unwrap ( ) ;
315+ let id = ecx. read_scalar ( & id_field) . unwrap ( ) . to_i32 ( ) . unwrap ( ) ;
316+ assert ! (
317+ is_cond_clock_realtime( ecx, id) ,
318+ "PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: clock is not CLOCK_REALTIME"
319+ ) ;
320+ }
321+
322+ offset
323+ }
232324
233325fn cond_get_id < ' mir , ' tcx : ' mir > (
234326 ecx : & mut MiriInterpCx < ' mir , ' tcx > ,
@@ -260,7 +352,7 @@ fn cond_get_clock_id<'mir, 'tcx: 'mir>(
260352) -> InterpResult < ' tcx , i32 > {
261353 ecx. deref_pointer_and_read (
262354 cond_op,
263- COND_CLOCK_OFFSET ,
355+ cond_clock_offset ( ecx ) ,
264356 ecx. libc_ty_layout ( "pthread_cond_t" ) ,
265357 ecx. machine . layouts . i32 ,
266358 ) ?
@@ -274,7 +366,7 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>(
274366) -> InterpResult < ' tcx , ( ) > {
275367 ecx. deref_pointer_and_write (
276368 cond_op,
277- COND_CLOCK_OFFSET ,
369+ cond_clock_offset ( ecx ) ,
278370 Scalar :: from_i32 ( clock_id) ,
279371 ecx. libc_ty_layout ( "pthread_cond_t" ) ,
280372 ecx. machine . layouts . i32 ,
@@ -776,7 +868,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
776868 let this = self . eval_context_mut ( ) ;
777869
778870 let attr = this. read_pointer ( attr_op) ?;
779- // Default clock if att is null, and on macOS where there is no clock attribute.
871+ // Default clock if `attr` is null, and on macOS where there is no clock attribute.
780872 let clock_id = if this. ptr_is_null ( attr) ? || this. tcx . sess . target . os == "macos" {
781873 this. eval_libc_i32 ( "CLOCK_REALTIME" )
782874 } else {
@@ -858,7 +950,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
858950 }
859951 } ;
860952
861- let timeout_time = if clock_id == this . eval_libc_i32 ( "CLOCK_REALTIME" ) {
953+ let timeout_time = if is_cond_clock_realtime ( this , clock_id ) {
862954 this. check_no_isolation ( "`pthread_cond_timedwait` with `CLOCK_REALTIME`" ) ?;
863955 CallbackTime :: RealTime ( SystemTime :: UNIX_EPOCH . checked_add ( duration) . unwrap ( ) )
864956 } else if clock_id == this. eval_libc_i32 ( "CLOCK_MONOTONIC" ) {
0 commit comments