@@ -206,7 +206,7 @@ fn translate_kind<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tc
206206// - id: u32
207207
208208#[ derive( Debug ) ]
209- /// Additional data that may be used by shim implementations .
209+ /// Additional data that we attach with each rwlock instance .
210210pub struct AdditionalRwLockData {
211211 /// The address of the rwlock.
212212 pub address : u64 ,
@@ -286,6 +286,19 @@ fn condattr_get_clock_id<'tcx>(
286286 . to_i32 ( )
287287}
288288
289+ fn translate_clock_id < ' tcx > ( ecx : & MiriInterpCx < ' tcx > , raw_id : i32 ) -> InterpResult < ' tcx , ClockId > {
290+ // To ensure compatibility with PTHREAD_COND_INITIALIZER on all platforms,
291+ // we can't just compare with CLOCK_REALTIME: on Solarish, PTHREAD_COND_INITIALIZER
292+ // makes the clock 0 but CLOCK_REALTIME is 3.
293+ Ok ( if raw_id == ecx. eval_libc_i32 ( "CLOCK_REALTIME" ) || raw_id == 0 {
294+ ClockId :: Realtime
295+ } else if raw_id == ecx. eval_libc_i32 ( "CLOCK_MONOTONIC" ) {
296+ ClockId :: Monotonic
297+ } else {
298+ throw_unsup_format ! ( "unsupported clock id: {raw_id}" ) ;
299+ } )
300+ }
301+
289302fn condattr_set_clock_id < ' tcx > (
290303 ecx : & mut MiriInterpCx < ' tcx > ,
291304 attr_ptr : & OpTy < ' tcx > ,
@@ -331,16 +344,6 @@ fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
331344 Ok ( offset)
332345}
333346
334- /// Determines whether this clock represents the real-time clock, CLOCK_REALTIME.
335- fn is_cond_clock_realtime < ' tcx > ( ecx : & MiriInterpCx < ' tcx > , clock_id : i32 ) -> bool {
336- // To ensure compatibility with PTHREAD_COND_INITIALIZER on all platforms,
337- // we can't just compare with CLOCK_REALTIME: on Solarish, PTHREAD_COND_INITIALIZER
338- // makes the clock 0 but CLOCK_REALTIME is 3.
339- // However, we need to always be able to distinguish this from CLOCK_MONOTONIC.
340- clock_id == ecx. eval_libc_i32 ( "CLOCK_REALTIME" )
341- || ( clock_id == 0 && clock_id != ecx. eval_libc_i32 ( "CLOCK_MONOTONIC" ) )
342- }
343-
344347fn cond_clock_offset < ' tcx > ( ecx : & MiriInterpCx < ' tcx > ) -> u64 {
345348 // macOS doesn't have a clock attribute, but to keep the code uniform we store
346349 // a clock ID in the pthread_cond_t anyway. There's enough space.
@@ -355,34 +358,57 @@ fn cond_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 {
355358 . offset ( Size :: from_bytes ( offset) , ecx. machine . layouts . i32 , ecx)
356359 . unwrap ( ) ;
357360 let id = ecx. read_scalar ( & id_field) . unwrap ( ) . to_i32 ( ) . unwrap ( ) ;
361+ let id = translate_clock_id ( ecx, id) . expect ( "static initializer should be valid" ) ;
358362 assert ! (
359- is_cond_clock_realtime ( ecx , id ) ,
363+ matches! ( id , ClockId :: Realtime ) ,
360364 "PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: clock is not CLOCK_REALTIME"
361365 ) ;
362366 }
363367
364368 offset
365369}
366370
371+ #[ derive( Debug , Clone , Copy ) ]
372+ enum ClockId {
373+ Realtime ,
374+ Monotonic ,
375+ }
376+
377+ #[ derive( Debug ) ]
378+ /// Additional data that we attach with each cond instance.
379+ struct AdditionalCondData {
380+ /// The address of the cond.
381+ address : u64 ,
382+
383+ /// The clock id of the cond.
384+ clock_id : ClockId ,
385+ }
386+
367387fn cond_get_id < ' tcx > (
368388 ecx : & mut MiriInterpCx < ' tcx > ,
369389 cond_ptr : & OpTy < ' tcx > ,
370390) -> InterpResult < ' tcx , CondvarId > {
371391 let cond = ecx. deref_pointer ( cond_ptr) ?;
372- ecx. condvar_get_or_create_id ( & cond, cond_id_offset ( ecx) ?)
373- }
392+ let address = cond. ptr ( ) . addr ( ) . bytes ( ) ;
393+ let id = ecx. condvar_get_or_create_id ( & cond, cond_id_offset ( ecx) ?, |ecx| {
394+ let raw_id = if ecx. tcx . sess . target . os == "macos" {
395+ ecx. eval_libc_i32 ( "CLOCK_REALTIME" )
396+ } else {
397+ cond_get_clock_id ( ecx, cond_ptr) ?
398+ } ;
399+ let clock_id = translate_clock_id ( ecx, raw_id) ?;
400+ Ok ( Some ( Box :: new ( AdditionalCondData { address, clock_id } ) ) )
401+ } ) ?;
374402
375- fn cond_reset_id < ' tcx > (
376- ecx : & mut MiriInterpCx < ' tcx > ,
377- cond_ptr : & OpTy < ' tcx > ,
378- ) -> InterpResult < ' tcx , ( ) > {
379- ecx. deref_pointer_and_write (
380- cond_ptr,
381- cond_id_offset ( ecx) ?,
382- Scalar :: from_i32 ( 0 ) ,
383- ecx. libc_ty_layout ( "pthread_cond_t" ) ,
384- ecx. machine . layouts . u32 ,
385- )
403+ // Check that the mutex has not been moved since last use.
404+ let data = ecx
405+ . condvar_get_data :: < AdditionalCondData > ( id)
406+ . expect ( "data should always exist for pthreads" ) ;
407+ if data. address != address {
408+ throw_ub_format ! ( "pthread_cond_t can't be moved after first use" )
409+ }
410+
411+ Ok ( id)
386412}
387413
388414fn cond_get_clock_id < ' tcx > (
@@ -398,20 +424,6 @@ fn cond_get_clock_id<'tcx>(
398424 . to_i32 ( )
399425}
400426
401- fn cond_set_clock_id < ' tcx > (
402- ecx : & mut MiriInterpCx < ' tcx > ,
403- cond_ptr : & OpTy < ' tcx > ,
404- clock_id : i32 ,
405- ) -> InterpResult < ' tcx , ( ) > {
406- ecx. deref_pointer_and_write (
407- cond_ptr,
408- cond_clock_offset ( ecx) ,
409- Scalar :: from_i32 ( clock_id) ,
410- ecx. libc_ty_layout ( "pthread_cond_t" ) ,
411- ecx. machine . layouts . i32 ,
412- )
413- }
414-
415427impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
416428pub trait EvalContextExt < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
417429 fn pthread_mutexattr_init ( & mut self , attr_op : & OpTy < ' tcx > ) -> InterpResult < ' tcx , ( ) > {
@@ -820,11 +832,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
820832 } else {
821833 condattr_get_clock_id ( this, attr_op) ?
822834 } ;
823-
824- // Write 0 to use the same code path as the static initializers.
825- cond_reset_id ( this, cond_op) ?;
826-
827- cond_set_clock_id ( this, cond_op, clock_id) ?;
835+ let clock_id = translate_clock_id ( this, clock_id) ?;
836+
837+ let cond = this. deref_pointer ( cond_op) ?;
838+ let address = cond. ptr ( ) . addr ( ) . bytes ( ) ;
839+ this. condvar_create (
840+ & cond,
841+ cond_id_offset ( this) ?,
842+ Some ( Box :: new ( AdditionalCondData { address, clock_id } ) ) ,
843+ ) ?;
828844
829845 Ok ( ( ) )
830846 }
@@ -879,7 +895,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
879895 let mutex_id = mutex_get_id ( this, mutex_op) ?;
880896
881897 // Extract the timeout.
882- let clock_id = cond_get_clock_id ( this, cond_op) ?;
898+ let clock_id = this
899+ . condvar_get_data :: < AdditionalCondData > ( id)
900+ . expect ( "additional data should always be present for pthreads" )
901+ . clock_id ;
883902 let duration = match this
884903 . read_timespec ( & this. deref_pointer_as ( abstime_op, this. libc_ty_layout ( "timespec" ) ) ?) ?
885904 {
@@ -890,13 +909,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
890909 return Ok ( ( ) ) ;
891910 }
892911 } ;
893- let timeout_clock = if is_cond_clock_realtime ( this, clock_id) {
894- this. check_no_isolation ( "`pthread_cond_timedwait` with `CLOCK_REALTIME`" ) ?;
895- TimeoutClock :: RealTime
896- } else if clock_id == this. eval_libc_i32 ( "CLOCK_MONOTONIC" ) {
897- TimeoutClock :: Monotonic
898- } else {
899- throw_unsup_format ! ( "unsupported clock id: {}" , clock_id) ;
912+ let timeout_clock = match clock_id {
913+ ClockId :: Realtime => {
914+ this. check_no_isolation ( "`pthread_cond_timedwait` with `CLOCK_REALTIME`" ) ?;
915+ TimeoutClock :: RealTime
916+ }
917+ ClockId :: Monotonic => TimeoutClock :: Monotonic ,
900918 } ;
901919
902920 this. condvar_wait (
@@ -912,17 +930,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
912930 }
913931
914932 fn pthread_cond_destroy ( & mut self , cond_op : & OpTy < ' tcx > ) -> InterpResult < ' tcx , ( ) > {
933+ //NOTE: Destroying an uninit pthread_cond is UB. Make sure it's not uninit,
934+ // by accessing at least once all of its fields that we use.
935+
915936 let this = self . eval_context_mut ( ) ;
916937
917938 let id = cond_get_id ( this, cond_op) ?;
918939 if this. condvar_is_awaited ( id) {
919940 throw_ub_format ! ( "destroying an awaited conditional variable" ) ;
920941 }
921942
922- // Destroying an uninit pthread_cond is UB, so check to make sure it's not uninit.
923- cond_get_id ( this, cond_op) ?;
924- cond_get_clock_id ( this, cond_op) ?;
925-
926943 // This might lead to false positives, see comment in pthread_mutexattr_destroy
927944 this. write_uninit ( & this. deref_pointer_as ( cond_op, this. libc_ty_layout ( "pthread_cond_t" ) ) ?) ?;
928945 // FIXME: delete interpreter state associated with this condvar.
0 commit comments