parent
ad7a1aa32a
commit
5f22103395
@ -35,8 +35,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
offset: u64,
|
||||
) -> InterpResult<'tcx, InitOnceId> {
|
||||
let this = self.eval_context_mut();
|
||||
this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.init_onces)?
|
||||
.ok_or_else(|| err_ub_format!("init_once has invalid ID").into())
|
||||
this.get_or_create_id(
|
||||
lock_op,
|
||||
lock_layout,
|
||||
offset,
|
||||
|ecx| &mut ecx.machine.sync.init_onces,
|
||||
|_| Ok(Default::default()),
|
||||
)?
|
||||
.ok_or_else(|| err_ub_format!("init_once has invalid ID").into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -66,6 +66,27 @@ pub(super) use declare_id;
|
||||
|
||||
declare_id!(MutexId);
|
||||
|
||||
/// The mutex kind.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub enum MutexKind {
|
||||
Invalid,
|
||||
Normal,
|
||||
Default,
|
||||
Recursive,
|
||||
ErrorCheck,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Additional data that may be used by shim implementations.
|
||||
pub struct AdditionalMutexData {
|
||||
/// The mutex kind, used by some mutex implementations like pthreads mutexes.
|
||||
pub kind: MutexKind,
|
||||
|
||||
/// The address of the mutex.
|
||||
pub address: u64,
|
||||
}
|
||||
|
||||
/// The mutex state.
|
||||
#[derive(Default, Debug)]
|
||||
struct Mutex {
|
||||
@ -77,6 +98,9 @@ struct Mutex {
|
||||
queue: VecDeque<ThreadId>,
|
||||
/// Mutex clock. This tracks the moment of the last unlock.
|
||||
clock: VClock,
|
||||
|
||||
/// Additional data that can be set by shim implementations.
|
||||
data: Option<AdditionalMutexData>,
|
||||
}
|
||||
|
||||
declare_id!(RwLockId);
|
||||
@ -168,13 +192,15 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
/// Returns `None` if memory stores a non-zero invalid ID.
|
||||
///
|
||||
/// `get_objs` must return the `IndexVec` that stores all the objects of this type.
|
||||
/// `create_obj` must create the new object if initialization is needed.
|
||||
#[inline]
|
||||
fn get_or_create_id<Id: SyncId + Idx, T: Default>(
|
||||
fn get_or_create_id<Id: SyncId + Idx, T>(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx>,
|
||||
lock_layout: TyAndLayout<'tcx>,
|
||||
offset: u64,
|
||||
get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
|
||||
create_obj: impl for<'a> FnOnce(&'a mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
|
||||
) -> InterpResult<'tcx, Option<Id>> {
|
||||
let this = self.eval_context_mut();
|
||||
let value_place =
|
||||
@ -196,7 +222,8 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") {
|
||||
// We set the in-memory ID to `next_index`, now also create this object in the machine
|
||||
// state.
|
||||
let new_index = get_objs(this).push(T::default());
|
||||
let obj = create_obj(this)?;
|
||||
let new_index = get_objs(this).push(obj);
|
||||
assert_eq!(next_index, new_index);
|
||||
Some(new_index)
|
||||
} else {
|
||||
@ -210,6 +237,32 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Eagerly creates a Miri sync structure.
|
||||
///
|
||||
/// `create_id` will store the index of the sync_structure in the memory pointed to by
|
||||
/// `lock_op`, so that future calls to `get_or_create_id` will see it as initialized.
|
||||
/// - `lock_op` must hold a pointer to the sync structure.
|
||||
/// - `lock_layout` must be the memory layout of the sync structure.
|
||||
/// - `offset` must be the offset inside the sync structure where its miri id will be stored.
|
||||
/// - `get_objs` is described in `get_or_create_id`.
|
||||
/// - `obj` must be the new sync object.
|
||||
fn create_id<Id: SyncId + Idx, T>(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx>,
|
||||
lock_layout: TyAndLayout<'tcx>,
|
||||
offset: u64,
|
||||
get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
|
||||
obj: T,
|
||||
) -> InterpResult<'tcx, Id> {
|
||||
let this = self.eval_context_mut();
|
||||
let value_place =
|
||||
this.deref_pointer_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?;
|
||||
|
||||
let new_index = get_objs(this).push(obj);
|
||||
this.write_scalar(Scalar::from_u32(new_index.to_u32()), &value_place)?;
|
||||
Ok(new_index)
|
||||
}
|
||||
|
||||
fn condvar_reacquire_mutex(
|
||||
&mut self,
|
||||
mutex: MutexId,
|
||||
@ -236,15 +289,53 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// situations.
|
||||
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
/// Eagerly create and initialize a new mutex.
|
||||
fn mutex_create(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx>,
|
||||
lock_layout: TyAndLayout<'tcx>,
|
||||
offset: u64,
|
||||
data: Option<AdditionalMutexData>,
|
||||
) -> InterpResult<'tcx, MutexId> {
|
||||
let this = self.eval_context_mut();
|
||||
this.create_id(
|
||||
lock_op,
|
||||
lock_layout,
|
||||
offset,
|
||||
|ecx| &mut ecx.machine.sync.mutexes,
|
||||
Mutex { data, ..Default::default() },
|
||||
)
|
||||
}
|
||||
|
||||
/// Lazily create a new mutex.
|
||||
/// `initialize_data` must return any additional data that a user wants to associate with the mutex.
|
||||
fn mutex_get_or_create_id(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx>,
|
||||
lock_layout: TyAndLayout<'tcx>,
|
||||
offset: u64,
|
||||
initialize_data: impl for<'a> FnOnce(
|
||||
&'a mut MiriInterpCx<'tcx>,
|
||||
) -> InterpResult<'tcx, Option<AdditionalMutexData>>,
|
||||
) -> InterpResult<'tcx, MutexId> {
|
||||
let this = self.eval_context_mut();
|
||||
this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.mutexes)?
|
||||
.ok_or_else(|| err_ub_format!("mutex has invalid ID").into())
|
||||
this.get_or_create_id(
|
||||
lock_op,
|
||||
lock_layout,
|
||||
offset,
|
||||
|ecx| &mut ecx.machine.sync.mutexes,
|
||||
|ecx| initialize_data(ecx).map(|data| Mutex { data, ..Default::default() }),
|
||||
)?
|
||||
.ok_or_else(|| err_ub_format!("mutex has invalid ID").into())
|
||||
}
|
||||
|
||||
/// Retrieve the additional data stored for a mutex.
|
||||
fn mutex_get_data<'a>(&'a mut self, id: MutexId) -> Option<&'a AdditionalMutexData>
|
||||
where
|
||||
'tcx: 'a,
|
||||
{
|
||||
let this = self.eval_context_ref();
|
||||
this.machine.sync.mutexes[id].data.as_ref()
|
||||
}
|
||||
|
||||
fn rwlock_get_or_create_id(
|
||||
@ -254,8 +345,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
offset: u64,
|
||||
) -> InterpResult<'tcx, RwLockId> {
|
||||
let this = self.eval_context_mut();
|
||||
this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.rwlocks)?
|
||||
.ok_or_else(|| err_ub_format!("rwlock has invalid ID").into())
|
||||
this.get_or_create_id(
|
||||
lock_op,
|
||||
lock_layout,
|
||||
offset,
|
||||
|ecx| &mut ecx.machine.sync.rwlocks,
|
||||
|_| Ok(Default::default()),
|
||||
)?
|
||||
.ok_or_else(|| err_ub_format!("rwlock has invalid ID").into())
|
||||
}
|
||||
|
||||
fn condvar_get_or_create_id(
|
||||
@ -265,8 +362,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
offset: u64,
|
||||
) -> InterpResult<'tcx, CondvarId> {
|
||||
let this = self.eval_context_mut();
|
||||
this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.condvars)?
|
||||
.ok_or_else(|| err_ub_format!("condvar has invalid ID").into())
|
||||
this.get_or_create_id(
|
||||
lock_op,
|
||||
lock_layout,
|
||||
offset,
|
||||
|ecx| &mut ecx.machine.sync.condvars,
|
||||
|_| Ok(Default::default()),
|
||||
)?
|
||||
.ok_or_else(|| err_ub_format!("condvar has invalid ID").into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -130,7 +130,10 @@ pub use crate::concurrency::{
|
||||
cpu_affinity::MAX_CPUS,
|
||||
data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _},
|
||||
init_once::{EvalContextExt as _, InitOnceId},
|
||||
sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects},
|
||||
sync::{
|
||||
AdditionalMutexData, CondvarId, EvalContextExt as _, MutexId, MutexKind, RwLockId,
|
||||
SynchronizationObjects,
|
||||
},
|
||||
thread::{
|
||||
BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager,
|
||||
TimeoutAnchor, TimeoutClock, UnblockCallback,
|
||||
|
@ -19,7 +19,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// os_unfair_lock holds a 32-bit value, is initialized with zero and
|
||||
// must be assumed to be opaque. Therefore, we can just store our
|
||||
// internal mutex ID in the structure without anyone noticing.
|
||||
this.mutex_get_or_create_id(lock_op, this.libc_ty_layout("os_unfair_lock"), 0)
|
||||
this.mutex_get_or_create_id(lock_op, this.libc_ty_layout("os_unfair_lock"), 0, |_| Ok(None))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,8 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
|
||||
|
||||
// Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
|
||||
// the id must start out as 0.
|
||||
// FIXME on some platforms (e.g linux) there are more static initializers for
|
||||
// recursive or error checking mutexes. We should also add thme in this sanity check.
|
||||
static SANITY: AtomicBool = AtomicBool::new(false);
|
||||
if !SANITY.swap(true, Ordering::Relaxed) {
|
||||
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]);
|
||||
@ -90,79 +92,92 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
fn mutex_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 {
|
||||
// These offsets are picked for compatibility with Linux's static initializer
|
||||
// macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.)
|
||||
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
|
||||
|
||||
// Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once):
|
||||
// the kind must start out as PTHREAD_MUTEX_DEFAULT.
|
||||
static SANITY: AtomicBool = AtomicBool::new(false);
|
||||
if !SANITY.swap(true, Ordering::Relaxed) {
|
||||
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]);
|
||||
let kind_field = static_initializer
|
||||
.offset(Size::from_bytes(mutex_kind_offset(ecx)), ecx.machine.layouts.i32, ecx)
|
||||
.unwrap();
|
||||
let kind = ecx.read_scalar(&kind_field).unwrap().to_i32().unwrap();
|
||||
assert_eq!(
|
||||
kind,
|
||||
ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"),
|
||||
"PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: kind is not PTHREAD_MUTEX_DEFAULT"
|
||||
);
|
||||
}
|
||||
|
||||
offset
|
||||
/// Eagerly create and initialize a new mutex.
|
||||
fn mutex_create<'tcx>(
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
mutex_op: &OpTy<'tcx>,
|
||||
kind: i32,
|
||||
) -> InterpResult<'tcx> {
|
||||
// FIXME: might be worth changing mutex_create to take the mplace
|
||||
// rather than the `OpTy`.
|
||||
let address = ecx.read_pointer(mutex_op)?.addr().bytes();
|
||||
let kind = translate_kind(ecx, kind)?;
|
||||
let data = Some(AdditionalMutexData { address, kind });
|
||||
ecx.mutex_create(mutex_op, ecx.libc_ty_layout("pthread_mutex_t"), mutex_id_offset(ecx)?, data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the `MutexId` of the mutex stored at `mutex_op`.
|
||||
///
|
||||
/// `mutex_get_id` will also check if the mutex has been moved since its first use and
|
||||
/// return an error if it has.
|
||||
fn mutex_get_id<'tcx>(
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
mutex_op: &OpTy<'tcx>,
|
||||
) -> InterpResult<'tcx, MutexId> {
|
||||
ecx.mutex_get_or_create_id(
|
||||
let address = ecx.read_pointer(mutex_op)?.addr().bytes();
|
||||
|
||||
// FIXME: might be worth changing mutex_get_or_create_id to take the mplace
|
||||
// rather than the `OpTy`.
|
||||
let id = ecx.mutex_get_or_create_id(
|
||||
mutex_op,
|
||||
ecx.libc_ty_layout("pthread_mutex_t"),
|
||||
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_op)?;
|
||||
|
||||
Ok(Some(AdditionalMutexData { kind, address }))
|
||||
},
|
||||
)?;
|
||||
|
||||
// Check that the mutex has not been moved since last use.
|
||||
let data = ecx.mutex_get_data(id).expect("data should be always exist for pthreads");
|
||||
if data.address != address {
|
||||
throw_ub_format!("pthread_mutex_t can't be moved after first use")
|
||||
}
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn mutex_reset_id<'tcx>(
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
mutex_op: &OpTy<'tcx>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
ecx.deref_pointer_and_write(
|
||||
mutex_op,
|
||||
mutex_id_offset(ecx)?,
|
||||
Scalar::from_u32(0),
|
||||
ecx.libc_ty_layout("pthread_mutex_t"),
|
||||
ecx.machine.layouts.u32,
|
||||
)
|
||||
}
|
||||
|
||||
fn mutex_get_kind<'tcx>(
|
||||
/// Returns the kind of a static initializer.
|
||||
fn kind_from_static_initializer<'tcx>(
|
||||
ecx: &MiriInterpCx<'tcx>,
|
||||
mutex_op: &OpTy<'tcx>,
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
ecx.deref_pointer_and_read(
|
||||
mutex_op,
|
||||
mutex_kind_offset(ecx),
|
||||
ecx.libc_ty_layout("pthread_mutex_t"),
|
||||
ecx.machine.layouts.i32,
|
||||
)?
|
||||
.to_i32()
|
||||
) -> InterpResult<'tcx, MutexKind> {
|
||||
// Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT.
|
||||
let kind = match &*ecx.tcx.sess.target.os {
|
||||
"linux" => {
|
||||
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
|
||||
|
||||
ecx.deref_pointer_and_read(
|
||||
mutex_op,
|
||||
offset,
|
||||
ecx.libc_ty_layout("pthread_mutex_t"),
|
||||
ecx.machine.layouts.i32,
|
||||
)?
|
||||
.to_i32()?
|
||||
}
|
||||
| "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)
|
||||
}
|
||||
|
||||
fn mutex_set_kind<'tcx>(
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
mutex_op: &OpTy<'tcx>,
|
||||
kind: i32,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
ecx.deref_pointer_and_write(
|
||||
mutex_op,
|
||||
mutex_kind_offset(ecx),
|
||||
Scalar::from_i32(kind),
|
||||
ecx.libc_ty_layout("pthread_mutex_t"),
|
||||
ecx.machine.layouts.i32,
|
||||
)
|
||||
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)? {
|
||||
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 {
|
||||
throw_unsup_format!("unsupported type of mutex: {kind}");
|
||||
})
|
||||
}
|
||||
|
||||
// pthread_rwlock_t is between 32 and 56 bytes, depending on the platform.
|
||||
@ -452,10 +467,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
mutexattr_get_kind(this, attr_op)?
|
||||
};
|
||||
|
||||
// Write 0 to use the same code path as the static initializers.
|
||||
mutex_reset_id(this, mutex_op)?;
|
||||
|
||||
mutex_set_kind(this, mutex_op, kind)?;
|
||||
mutex_create(this, mutex_op, kind)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -467,8 +479,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let kind = mutex_get_kind(this, mutex_op)?;
|
||||
let id = mutex_get_id(this, mutex_op)?;
|
||||
let kind =
|
||||
this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
|
||||
|
||||
let ret = if this.mutex_is_locked(id) {
|
||||
let owner_thread = this.mutex_get_owner(id);
|
||||
@ -477,19 +490,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
return Ok(());
|
||||
} else {
|
||||
// Trying to acquire the same mutex again.
|
||||
if is_mutex_kind_default(this, kind)? {
|
||||
throw_ub_format!("trying to acquire already locked default mutex");
|
||||
} else if is_mutex_kind_normal(this, kind)? {
|
||||
throw_machine_stop!(TerminationInfo::Deadlock);
|
||||
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
|
||||
this.eval_libc_i32("EDEADLK")
|
||||
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
|
||||
this.mutex_lock(id);
|
||||
0
|
||||
} else {
|
||||
throw_unsup_format!(
|
||||
"called pthread_mutex_lock on an unsupported type of mutex"
|
||||
);
|
||||
match kind {
|
||||
MutexKind::Default =>
|
||||
throw_ub_format!("trying to acquire already locked default mutex"),
|
||||
MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock),
|
||||
MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"),
|
||||
MutexKind::Recursive => {
|
||||
this.mutex_lock(id);
|
||||
0
|
||||
}
|
||||
_ =>
|
||||
throw_unsup_format!(
|
||||
"called pthread_mutex_lock on an unsupported type of mutex"
|
||||
),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -504,26 +517,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let kind = mutex_get_kind(this, mutex_op)?;
|
||||
let id = mutex_get_id(this, mutex_op)?;
|
||||
let kind =
|
||||
this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
|
||||
|
||||
Ok(Scalar::from_i32(if this.mutex_is_locked(id) {
|
||||
let owner_thread = this.mutex_get_owner(id);
|
||||
if owner_thread != this.active_thread() {
|
||||
this.eval_libc_i32("EBUSY")
|
||||
} else {
|
||||
if is_mutex_kind_default(this, kind)?
|
||||
|| is_mutex_kind_normal(this, kind)?
|
||||
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
|
||||
{
|
||||
this.eval_libc_i32("EBUSY")
|
||||
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
|
||||
this.mutex_lock(id);
|
||||
0
|
||||
} else {
|
||||
throw_unsup_format!(
|
||||
"called pthread_mutex_trylock on an unsupported type of mutex"
|
||||
);
|
||||
match kind {
|
||||
MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck =>
|
||||
this.eval_libc_i32("EBUSY"),
|
||||
MutexKind::Recursive => {
|
||||
this.mutex_lock(id);
|
||||
0
|
||||
}
|
||||
_ =>
|
||||
throw_unsup_format!(
|
||||
"called pthread_mutex_trylock on an unsupported type of mutex"
|
||||
),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -536,8 +549,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let kind = mutex_get_kind(this, mutex_op)?;
|
||||
let id = mutex_get_id(this, mutex_op)?;
|
||||
let kind =
|
||||
this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
|
||||
|
||||
if let Some(_old_locked_count) = this.mutex_unlock(id)? {
|
||||
// The mutex was locked by the current thread.
|
||||
@ -546,20 +560,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
// The mutex was locked by another thread or not locked at all. See
|
||||
// the “Unlock When Not Owner” column in
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_unlock.html.
|
||||
if is_mutex_kind_default(this, kind)? {
|
||||
throw_ub_format!(
|
||||
"unlocked a default mutex that was not locked by the current thread"
|
||||
);
|
||||
} else if is_mutex_kind_normal(this, kind)? {
|
||||
throw_ub_format!(
|
||||
"unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread"
|
||||
);
|
||||
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
|
||||
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")
|
||||
{
|
||||
Ok(Scalar::from_i32(this.eval_libc_i32("EPERM")))
|
||||
} else {
|
||||
throw_unsup_format!("called pthread_mutex_unlock on an unsupported type of mutex");
|
||||
match kind {
|
||||
MutexKind::Default =>
|
||||
throw_ub_format!(
|
||||
"unlocked a default mutex that was not locked by the current thread"
|
||||
),
|
||||
MutexKind::Normal =>
|
||||
throw_ub_format!(
|
||||
"unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread"
|
||||
),
|
||||
MutexKind::ErrorCheck | MutexKind::Recursive =>
|
||||
Ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))),
|
||||
_ =>
|
||||
throw_unsup_format!(
|
||||
"called pthread_mutex_unlock on an unsupported type of mutex"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -574,7 +589,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
}
|
||||
|
||||
// Destroying an uninit pthread_mutex is UB, so check to make sure it's not uninit.
|
||||
mutex_get_kind(this, mutex_op)?;
|
||||
mutex_get_id(this, mutex_op)?;
|
||||
|
||||
// This might lead to false positives, see comment in pthread_mutexattr_destroy
|
||||
|
@ -0,0 +1,20 @@
|
||||
error: Undefined Behavior: pthread_mutex_t can't be moved after first use
|
||||
--> $DIR/libc_pthread_mutex_move.rs:LL:CC
|
||||
|
|
||||
LL | libc::pthread_mutex_lock(&mut m2 as *mut _);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_mutex_t can't be moved after first use
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: BACKTRACE:
|
||||
= note: inside `check` at $DIR/libc_pthread_mutex_move.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> $DIR/libc_pthread_mutex_move.rs:LL:CC
|
||||
|
|
||||
LL | check();
|
||||
| ^^^^^^^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
@ -0,0 +1,28 @@
|
||||
//@ignore-target-windows: No pthreads on Windows
|
||||
//@revisions: static_initializer init
|
||||
|
||||
fn main() {
|
||||
check();
|
||||
}
|
||||
|
||||
#[cfg(init)]
|
||||
fn check() {
|
||||
unsafe {
|
||||
let mut m: libc::pthread_mutex_t = std::mem::zeroed();
|
||||
assert_eq!(libc::pthread_mutex_init(&mut m as *mut _, std::ptr::null()), 0);
|
||||
|
||||
let mut m2 = m; // move the mutex
|
||||
libc::pthread_mutex_lock(&mut m2 as *mut _); //~[init] ERROR: pthread_mutex_t can't be moved after first use
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(static_initializer)]
|
||||
fn check() {
|
||||
unsafe {
|
||||
let mut m: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER;
|
||||
libc::pthread_mutex_lock(&mut m as *mut _);
|
||||
|
||||
let mut m2 = m; // move the mutex
|
||||
libc::pthread_mutex_unlock(&mut m2 as *mut _); //~[static_initializer] ERROR: pthread_mutex_t can't be moved after first use
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
error: Undefined Behavior: pthread_mutex_t can't be moved after first use
|
||||
--> $DIR/libc_pthread_mutex_move.rs:LL:CC
|
||||
|
|
||||
LL | libc::pthread_mutex_unlock(&mut m2 as *mut _);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_mutex_t can't be moved after first use
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: BACKTRACE:
|
||||
= note: inside `check` at $DIR/libc_pthread_mutex_move.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> $DIR/libc_pthread_mutex_move.rs:LL:CC
|
||||
|
|
||||
LL | check();
|
||||
| ^^^^^^^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
Loading…
x
Reference in New Issue
Block a user