Auto merge of #3886 - RalfJung:freebsd-pthread, r=RalfJung
support pthread primitives on FreeBSD Fixes https://github.com/rust-lang/miri/issues/3571: makes out pthread implementation support FreeBSD. FreeBSD sets PHTREAD_MUTEX_DEFAULT == PHTREAD_MUTEX_ERRORCK, so the logic for handling "default vs explicitly set mutex kind" had to be adjusted. The rest is just some general cleanup of the pthread logic, and I realized that we can enable the std::sync tests on Solarish.
This commit is contained in:
commit
75921d2ca4
@ -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
|
||||
|
@ -11,7 +11,7 @@
|
||||
#[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 @@ fn pthread_mutexattr_settype(
|
||||
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 @@ fn pthread_mutex_init(
|
||||
|
||||
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 @@ fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'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 @@ fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'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 @@ fn pthread_cond_init(
|
||||
} 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 @@ fn pthread_cond_timedwait(
|
||||
}
|
||||
|
||||
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");
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user