@@ -206,7 +206,7 @@ fn translate_kind<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tc
206
206
// - id: u32
207
207
208
208
#[ derive( Debug ) ]
209
- /// Additional data that may be used by shim implementations .
209
+ /// Additional data that we attach with each rwlock instance .
210
210
pub struct AdditionalRwLockData {
211
211
/// The address of the rwlock.
212
212
pub address : u64 ,
@@ -286,6 +286,19 @@ fn condattr_get_clock_id<'tcx>(
286
286
. to_i32 ( )
287
287
}
288
288
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
+
289
302
fn condattr_set_clock_id < ' tcx > (
290
303
ecx : & mut MiriInterpCx < ' tcx > ,
291
304
attr_ptr : & OpTy < ' tcx > ,
@@ -331,16 +344,6 @@ fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
331
344
Ok ( offset)
332
345
}
333
346
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
-
344
347
fn cond_clock_offset < ' tcx > ( ecx : & MiriInterpCx < ' tcx > ) -> u64 {
345
348
// macOS doesn't have a clock attribute, but to keep the code uniform we store
346
349
// 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 {
355
358
. offset ( Size :: from_bytes ( offset) , ecx. machine . layouts . i32 , ecx)
356
359
. unwrap ( ) ;
357
360
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" ) ;
358
362
assert ! (
359
- is_cond_clock_realtime ( ecx , id ) ,
363
+ matches! ( id , ClockId :: Realtime ) ,
360
364
"PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: clock is not CLOCK_REALTIME"
361
365
) ;
362
366
}
363
367
364
368
offset
365
369
}
366
370
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
+
367
387
fn cond_get_id < ' tcx > (
368
388
ecx : & mut MiriInterpCx < ' tcx > ,
369
389
cond_ptr : & OpTy < ' tcx > ,
370
390
) -> InterpResult < ' tcx , CondvarId > {
371
391
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
+ } ) ?;
374
402
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)
386
412
}
387
413
388
414
fn cond_get_clock_id < ' tcx > (
@@ -398,20 +424,6 @@ fn cond_get_clock_id<'tcx>(
398
424
. to_i32 ( )
399
425
}
400
426
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
-
415
427
impl < ' tcx > EvalContextExt < ' tcx > for crate :: MiriInterpCx < ' tcx > { }
416
428
pub trait EvalContextExt < ' tcx > : crate :: MiriInterpCxExt < ' tcx > {
417
429
fn pthread_mutexattr_init ( & mut self , attr_op : & OpTy < ' tcx > ) -> InterpResult < ' tcx , ( ) > {
@@ -820,11 +832,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
820
832
} else {
821
833
condattr_get_clock_id ( this, attr_op) ?
822
834
} ;
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
+ ) ?;
828
844
829
845
Ok ( ( ) )
830
846
}
@@ -879,7 +895,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
879
895
let mutex_id = mutex_get_id ( this, mutex_op) ?;
880
896
881
897
// 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 ;
883
902
let duration = match this
884
903
. read_timespec ( & this. deref_pointer_as ( abstime_op, this. libc_ty_layout ( "timespec" ) ) ?) ?
885
904
{
@@ -890,13 +909,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
890
909
return Ok ( ( ) ) ;
891
910
}
892
911
} ;
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 ,
900
918
} ;
901
919
902
920
this. condvar_wait (
@@ -912,17 +930,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
912
930
}
913
931
914
932
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
+
915
936
let this = self . eval_context_mut ( ) ;
916
937
917
938
let id = cond_get_id ( this, cond_op) ?;
918
939
if this. condvar_is_awaited ( id) {
919
940
throw_ub_format ! ( "destroying an awaited conditional variable" ) ;
920
941
}
921
942
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
-
926
943
// This might lead to false positives, see comment in pthread_mutexattr_destroy
927
944
this. write_uninit ( & this. deref_pointer_as ( cond_op, this. libc_ty_layout ( "pthread_cond_t" ) ) ?) ?;
928
945
// FIXME: delete interpreter state associated with this condvar.
0 commit comments