diff options
| -rwxr-xr-x | src/tools/miri/ci/ci.sh | 8 | ||||
| -rw-r--r-- | src/tools/miri/src/shims/unix/sync.rs | 190 | ||||
| -rw-r--r-- | src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs | 3 | ||||
| -rw-r--r-- | src/tools/miri/tests/pass-dep/libc/libc_pthread_cond_timedwait.rs (renamed from src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs) | 0 | ||||
| -rw-r--r-- | src/tools/miri/tests/pass-dep/libc/libc_pthread_cond_timedwait_isolated.rs (renamed from src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs) | 0 |
5 files changed, 70 insertions, 131 deletions
diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 2e76838b001..6b57a294b65 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -150,10 +150,10 @@ case $HOST_TARGET in # Partially supported targets (tier 2) BASIC="empty_main integer vec string btreemap hello hashmap heap_alloc align" # ensures we have the basics: stdout/stderr, system allocator, randomness (for HashMap initialization) UNIX="panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there - TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs - TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs - TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX threadname pthread available-parallelism libc-time tls - TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX threadname pthread available-parallelism libc-time tls + TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname pthread libc-time fs + TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname pthread libc-time fs + TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX threadname pthread sync available-parallelism libc-time tls + TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX threadname pthread sync available-parallelism libc-time tls TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX TEST_TARGET=wasm32-wasip2 run_tests_minimal empty_main wasm heap_alloc libc-mem TEST_TARGET=wasm32-unknown-unknown run_tests_minimal empty_main wasm diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 3535eacb447..114a457d71a 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -11,7 +11,7 @@ use crate::*; #[inline] fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { Ok(match &*ecx.tcx.sess.target.os { - "linux" | "illumos" | "solaris" | "macos" => 0, + "linux" | "illumos" | "solaris" | "macos" | "freebsd" => 0, os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"), }) } @@ -43,21 +43,11 @@ fn mutexattr_set_kind<'tcx>( ) } -/// A flag that allows to distinguish `PTHREAD_MUTEX_NORMAL` from -/// `PTHREAD_MUTEX_DEFAULT`. Since in `glibc` they have the same numeric values, -/// but different behaviour, we need a way to distinguish them. We do this by -/// setting this bit flag to the `PTHREAD_MUTEX_NORMAL` mutexes. See the comment -/// in `pthread_mutexattr_settype` function. -const PTHREAD_MUTEX_NORMAL_FLAG: i32 = 0x8000000; - -fn is_mutex_kind_default<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, bool> { - Ok(kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")) -} - -fn is_mutex_kind_normal<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, bool> { - let mutex_normal_kind = ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL"); - Ok(kind == (mutex_normal_kind | PTHREAD_MUTEX_NORMAL_FLAG)) -} +/// To differentiate "the mutex kind has not been changed" from +/// "the mutex kind has been set to PTHREAD_MUTEX_DEFAULT and that is +/// equal to some other mutex kind", we make the default value of this +/// field *not* PTHREAD_MUTEX_DEFAULT but this special flag. +const PTHREAD_MUTEX_KIND_UNCHANGED: i32 = 0x8000000; /// The mutex kind. #[derive(Debug, Clone, Copy)] @@ -78,13 +68,15 @@ pub struct AdditionalMutexData { pub address: u64, } -// pthread_mutex_t is between 24 and 48 bytes, depending on the platform. +// pthread_mutex_t is between 4 and 48 bytes, depending on the platform. // We ignore the platform layout and store our own fields: // - id: u32 fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { + // When adding a new OS, make sure we also support all its static initializers in + // `mutex_kind_from_static_initializer`! let offset = match &*ecx.tcx.sess.target.os { - "linux" | "illumos" | "solaris" => 0, + "linux" | "illumos" | "solaris" | "freebsd" => 0, // macOS stores a signature in the first bytes, so we have to move to offset 4. "macos" => 4, os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), @@ -113,7 +105,7 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { check_static_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP"); check_static_initializer("PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP"); } - "illumos" | "solaris" | "macos" => { + "illumos" | "solaris" | "macos" | "freebsd" => { // No non-standard initializers. } os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), @@ -127,11 +119,10 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { fn mutex_create<'tcx>( ecx: &mut MiriInterpCx<'tcx>, mutex_ptr: &OpTy<'tcx>, - kind: i32, + kind: MutexKind, ) -> InterpResult<'tcx> { let mutex = ecx.deref_pointer(mutex_ptr)?; let address = mutex.ptr().addr().bytes(); - let kind = translate_kind(ecx, kind)?; let data = Box::new(AdditionalMutexData { address, kind }); ecx.mutex_create(&mutex, mutex_id_offset(ecx)?, Some(data))?; Ok(()) @@ -151,7 +142,7 @@ fn mutex_get_id<'tcx>( let id = ecx.mutex_get_or_create_id(&mutex, mutex_id_offset(ecx)?, |ecx| { // This is called if a static initializer was used and the lock has not been assigned // an ID yet. We have to determine the mutex kind from the static initializer. - let kind = kind_from_static_initializer(ecx, &mutex)?; + let kind = mutex_kind_from_static_initializer(ecx, &mutex)?; Ok(Some(Box::new(AdditionalMutexData { kind, address }))) })?; @@ -168,40 +159,51 @@ fn mutex_get_id<'tcx>( } /// Returns the kind of a static initializer. -fn kind_from_static_initializer<'tcx>( +fn mutex_kind_from_static_initializer<'tcx>( ecx: &MiriInterpCx<'tcx>, mutex: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx, MutexKind> { - // Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT. - let kind = match &*ecx.tcx.sess.target.os { + Ok(match &*ecx.tcx.sess.target.os { + // Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT. "linux" => { let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; let kind_place = mutex.offset(Size::from_bytes(offset), ecx.machine.layouts.i32, ecx)?; - ecx.read_scalar(&kind_place)?.to_i32()? + let kind = ecx.read_scalar(&kind_place)?.to_i32()?; + // Here we give PTHREAD_MUTEX_DEFAULT priority so that + // PTHREAD_MUTEX_INITIALIZER behaves like `pthread_mutex_init` with a NULL argument. + if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") { + MutexKind::Default + } else { + mutex_translate_kind(ecx, kind)? + } } - "illumos" | "solaris" | "macos" => ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"), - os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), - }; - - translate_kind(ecx, kind) + _ => MutexKind::Default, + }) } -fn translate_kind<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, MutexKind> { - Ok(if is_mutex_kind_default(ecx, kind)? { - MutexKind::Default - } else if is_mutex_kind_normal(ecx, kind)? { +fn mutex_translate_kind<'tcx>( + ecx: &MiriInterpCx<'tcx>, + kind: i32, +) -> InterpResult<'tcx, MutexKind> { + Ok(if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")) { MutexKind::Normal } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") { MutexKind::ErrorCheck } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") { MutexKind::Recursive + } else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") + || kind == PTHREAD_MUTEX_KIND_UNCHANGED + { + // We check this *last* since PTHREAD_MUTEX_DEFAULT may be numerically equal to one of the + // others, and we want an explicit `mutexattr_settype` to work as expected. + MutexKind::Default } else { throw_unsup_format!("unsupported type of mutex: {kind}"); }) } -// pthread_rwlock_t is between 32 and 56 bytes, depending on the platform. +// pthread_rwlock_t is between 4 and 56 bytes, depending on the platform. // We ignore the platform layout and store our own fields: // - id: u32 @@ -214,7 +216,7 @@ pub struct AdditionalRwLockData { fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { let offset = match &*ecx.tcx.sess.target.os { - "linux" | "illumos" | "solaris" => 0, + "linux" | "illumos" | "solaris" | "freebsd" => 0, // macOS stores a signature in the first bytes, so we have to move to offset 4. "macos" => 4, os => throw_unsup_format!("`pthread_rwlock` is not supported on {os}"), @@ -267,7 +269,7 @@ fn rwlock_get_id<'tcx>( #[inline] fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { Ok(match &*ecx.tcx.sess.target.os { - "linux" | "illumos" | "solaris" => 0, + "linux" | "illumos" | "solaris" | "freebsd" => 0, // macOS does not have a clock attribute. os => throw_unsup_format!("`pthread_condattr` clock field is not supported on {os}"), }) @@ -286,11 +288,11 @@ fn condattr_get_clock_id<'tcx>( .to_i32() } -fn translate_clock_id<'tcx>(ecx: &MiriInterpCx<'tcx>, raw_id: i32) -> InterpResult<'tcx, ClockId> { - // To ensure compatibility with PTHREAD_COND_INITIALIZER on all platforms, - // we can't just compare with CLOCK_REALTIME: on Solarish, PTHREAD_COND_INITIALIZER - // makes the clock 0 but CLOCK_REALTIME is 3. - Ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") || raw_id == 0 { +fn cond_translate_clock_id<'tcx>( + ecx: &MiriInterpCx<'tcx>, + raw_id: i32, +) -> InterpResult<'tcx, ClockId> { + Ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") { ClockId::Realtime } else if raw_id == ecx.eval_libc_i32("CLOCK_MONOTONIC") { ClockId::Monotonic @@ -313,14 +315,13 @@ fn condattr_set_clock_id<'tcx>( ) } -// pthread_cond_t. +// pthread_cond_t can be only 4 bytes in size, depending on the platform. // We ignore the platform layout and store our own fields: // - id: u32 -// - clock: i32 fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { let offset = match &*ecx.tcx.sess.target.os { - "linux" | "illumos" | "solaris" => 0, + "linux" | "illumos" | "solaris" | "freebsd" => 0, // macOS stores a signature in the first bytes, so we have to move to offset 4. "macos" => 4, os => throw_unsup_format!("`pthread_cond` is not supported on {os}"), @@ -344,30 +345,6 @@ fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> { Ok(offset) } -fn cond_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 { - // macOS doesn't have a clock attribute, but to keep the code uniform we store - // a clock ID in the pthread_cond_t anyway. There's enough space. - let offset = 8; - - // Sanity-check this against PTHREAD_COND_INITIALIZER (but only once): - // the clock must start out as CLOCK_REALTIME. - static SANITY: AtomicBool = AtomicBool::new(false); - if !SANITY.swap(true, Ordering::Relaxed) { - let static_initializer = ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]); - let id_field = static_initializer - .offset(Size::from_bytes(offset), ecx.machine.layouts.i32, ecx) - .unwrap(); - let id = ecx.read_scalar(&id_field).unwrap().to_i32().unwrap(); - let id = translate_clock_id(ecx, id).expect("static initializer should be valid"); - assert!( - matches!(id, ClockId::Realtime), - "PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: clock is not CLOCK_REALTIME" - ); - } - - offset -} - #[derive(Debug, Clone, Copy)] enum ClockId { Realtime, @@ -390,14 +367,9 @@ fn cond_get_id<'tcx>( ) -> InterpResult<'tcx, CondvarId> { let cond = ecx.deref_pointer(cond_ptr)?; let address = cond.ptr().addr().bytes(); - let id = ecx.condvar_get_or_create_id(&cond, cond_id_offset(ecx)?, |ecx| { - let raw_id = if ecx.tcx.sess.target.os == "macos" { - ecx.eval_libc_i32("CLOCK_REALTIME") - } else { - cond_get_clock_id(ecx, cond_ptr)? - }; - let clock_id = translate_clock_id(ecx, raw_id)?; - Ok(Some(Box::new(AdditionalCondData { address, clock_id }))) + let id = ecx.condvar_get_or_create_id(&cond, cond_id_offset(ecx)?, |_ecx| { + // This used the static initializer. The clock there is always CLOCK_REALTIME. + Ok(Some(Box::new(AdditionalCondData { address, clock_id: ClockId::Realtime }))) })?; // Check that the mutex has not been moved since last use. @@ -411,26 +383,12 @@ fn cond_get_id<'tcx>( Ok(id) } -fn cond_get_clock_id<'tcx>( - ecx: &MiriInterpCx<'tcx>, - cond_ptr: &OpTy<'tcx>, -) -> InterpResult<'tcx, i32> { - ecx.deref_pointer_and_read( - cond_ptr, - cond_clock_offset(ecx), - ecx.libc_ty_layout("pthread_cond_t"), - ecx.machine.layouts.i32, - )? - .to_i32() -} - impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); - let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"); - mutexattr_set_kind(this, attr_op, default_kind)?; + mutexattr_set_kind(this, attr_op, PTHREAD_MUTEX_KIND_UNCHANGED)?; Ok(()) } @@ -443,30 +401,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let kind = this.read_scalar(kind_op)?.to_i32()?; - if kind == this.eval_libc_i32("PTHREAD_MUTEX_NORMAL") { - // In `glibc` implementation, the numeric values of - // `PTHREAD_MUTEX_NORMAL` and `PTHREAD_MUTEX_DEFAULT` are equal. - // However, a mutex created by explicitly passing - // `PTHREAD_MUTEX_NORMAL` type has in some cases different behaviour - // from the default mutex for which the type was not explicitly - // specified. For a more detailed discussion, please see - // https://github.com/rust-lang/miri/issues/1419. - // - // To distinguish these two cases in already constructed mutexes, we - // use the same trick as glibc: for the case when - // `pthread_mutexattr_settype` is called explicitly, we set the - // `PTHREAD_MUTEX_NORMAL_FLAG` flag. - let normal_kind = kind | PTHREAD_MUTEX_NORMAL_FLAG; - // Check that after setting the flag, the kind is distinguishable - // from all other kinds. - assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")); - assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")); - assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")); - mutexattr_set_kind(this, attr_op, normal_kind)?; - } else if kind == this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") + if kind == this.eval_libc_i32("PTHREAD_MUTEX_NORMAL") + || kind == this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") || kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") || kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") { + // Make sure we do not mix this up with the "unchanged" kind. + assert_ne!(kind, PTHREAD_MUTEX_KIND_UNCHANGED); mutexattr_set_kind(this, attr_op, kind)?; } else { let einval = this.eval_libc_i32("EINVAL"); @@ -510,9 +451,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let attr = this.read_pointer(attr_op)?; let kind = if this.ptr_is_null(attr)? { - this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") + MutexKind::Default } else { - mutexattr_get_kind(this, attr_op)? + mutex_translate_kind(this, mutexattr_get_kind(this, attr_op)?)? }; mutex_create(this, mutex_op, kind)?; @@ -624,15 +565,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); + // Reading the field also has the side-effect that we detect double-`destroy` + // since we make the field unint below. let id = mutex_get_id(this, mutex_op)?; if this.mutex_is_locked(id) { throw_ub_format!("destroyed a locked mutex"); } - // Destroying an uninit pthread_mutex is UB, so check to make sure it's not uninit. - mutex_get_id(this, mutex_op)?; - // This might lead to false positives, see comment in pthread_mutexattr_destroy this.write_uninit( &this.deref_pointer_as(mutex_op, this.libc_ty_layout("pthread_mutex_t"))?, @@ -734,15 +674,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); + // Reading the field also has the side-effect that we detect double-`destroy` + // since we make the field unint below. let id = rwlock_get_id(this, rwlock_op)?; if this.rwlock_is_locked(id) { throw_ub_format!("destroyed a locked rwlock"); } - // Destroying an uninit pthread_rwlock is UB, so check to make sure it's not uninit. - rwlock_get_id(this, rwlock_op)?; - // This might lead to false positives, see comment in pthread_mutexattr_destroy this.write_uninit( &this.deref_pointer_as(rwlock_op, this.libc_ty_layout("pthread_rwlock_t"))?, @@ -832,7 +771,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } else { condattr_get_clock_id(this, attr_op)? }; - let clock_id = translate_clock_id(this, clock_id)?; + let clock_id = cond_translate_clock_id(this, clock_id)?; let cond = this.deref_pointer(cond_op)?; let address = cond.ptr().addr().bytes(); @@ -930,11 +869,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> { - //NOTE: Destroying an uninit pthread_cond is UB. Make sure it's not uninit, - // by accessing at least once all of its fields that we use. - let this = self.eval_context_mut(); + // Reading the field also has the side-effect that we detect double-`destroy` + // since we make the field unint below. let id = cond_get_id(this, cond_op)?; if this.condvar_is_awaited(id) { throw_ub_format!("destroying an awaited conditional variable"); diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs index 1038b8988f9..6723f2c6e77 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs @@ -4,7 +4,8 @@ fn main() { unsafe { - let mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed(); + let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed(); + assert_eq!(libc::pthread_mutexattr_init(&mut mutexattr as *mut _), 0); let mut mutex: libc::pthread_mutex_t = std::mem::zeroed(); assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0); assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); diff --git a/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs b/src/tools/miri/tests/pass-dep/libc/libc_pthread_cond_timedwait.rs index d758168c7c3..d758168c7c3 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc_pthread_cond_timedwait.rs diff --git a/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs b/src/tools/miri/tests/pass-dep/libc/libc_pthread_cond_timedwait_isolated.rs index f1a3c5dc10d..f1a3c5dc10d 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc_pthread_cond_timedwait_isolated.rs |
