Skip to content

Commit 4000027

Browse files
committed
pthread: add FreeBSD, remove PTHREAD_MUTEX_NORMAL_FLAG hack
On FreeBSD, DEFAULT maps to ERRORCK. This clashes with the existing PTHREAD_MUTEX_NORMAL_FLAG: hack so we replace it by a different hack that works better cross-platform. Also fix a case of "accidental early UB" in a UB test -- pthread_mutexattr_t must be initialized.
1 parent efdc01b commit 4000027

File tree

3 files changed

+43
-52
lines changed

3 files changed

+43
-52
lines changed

src/tools/miri/ci/ci.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ case $HOST_TARGET in
150150
# Partially supported targets (tier 2)
151151
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)
152152
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
153-
TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs
154-
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs
153+
TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname pthread libc-time fs
154+
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname pthread libc-time fs
155155
TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX threadname pthread available-parallelism libc-time tls
156156
TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX threadname pthread available-parallelism libc-time tls
157157
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX

src/tools/miri/src/shims/unix/sync.rs

Lines changed: 39 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::*;
1111
#[inline]
1212
fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
1313
Ok(match &*ecx.tcx.sess.target.os {
14-
"linux" | "illumos" | "solaris" | "macos" => 0,
14+
"linux" | "illumos" | "solaris" | "macos" | "freebsd" => 0,
1515
os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"),
1616
})
1717
}
@@ -43,12 +43,11 @@ fn mutexattr_set_kind<'tcx>(
4343
)
4444
}
4545

46-
/// A flag that allows to distinguish `PTHREAD_MUTEX_NORMAL` from
47-
/// `PTHREAD_MUTEX_DEFAULT`. Since in `glibc` they have the same numeric values,
48-
/// but different behaviour, we need a way to distinguish them. We do this by
49-
/// setting this bit flag to the `PTHREAD_MUTEX_NORMAL` mutexes. See the comment
50-
/// in `pthread_mutexattr_settype` function.
51-
const PTHREAD_MUTEX_NORMAL_FLAG: i32 = 0x8000000;
46+
/// To differentiate "the mutex kind has not been changed" from
47+
/// "the mutex kind has been set to PTHREAD_MUTEX_DEFAULT and that is
48+
/// equal to some other mutex kind", we make the default value of this
49+
/// field *not* PTHREAD_MUTEX_DEFAULT but this special flag.
50+
const PTHREAD_MUTEX_KIND_UNCHANGED: i32 = 0x8000000;
5251

5352
/// The mutex kind.
5453
#[derive(Debug, Clone, Copy)]
@@ -74,8 +73,10 @@ pub struct AdditionalMutexData {
7473
// - id: u32
7574

7675
fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
76+
// When adding a new OS, make sure we also support all its static initializers in
77+
// `mutex_kind_from_static_initializer`!
7778
let offset = match &*ecx.tcx.sess.target.os {
78-
"linux" | "illumos" | "solaris" => 0,
79+
"linux" | "illumos" | "solaris" | "freebsd" => 0,
7980
// macOS stores a signature in the first bytes, so we have to move to offset 4.
8081
"macos" => 4,
8182
os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
@@ -104,7 +105,7 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
104105
check_static_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP");
105106
check_static_initializer("PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP");
106107
}
107-
"illumos" | "solaris" | "macos" => {
108+
"illumos" | "solaris" | "macos" | "freebsd" => {
108109
// No non-standard initializers.
109110
}
110111
os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
@@ -118,11 +119,10 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
118119
fn mutex_create<'tcx>(
119120
ecx: &mut MiriInterpCx<'tcx>,
120121
mutex_ptr: &OpTy<'tcx>,
121-
kind: i32,
122+
kind: MutexKind,
122123
) -> InterpResult<'tcx> {
123124
let mutex = ecx.deref_pointer(mutex_ptr)?;
124125
let address = mutex.ptr().addr().bytes();
125-
let kind = mutex_translate_kind(ecx, kind)?;
126126
let data = Box::new(AdditionalMutexData { address, kind });
127127
ecx.mutex_create(&mutex, mutex_id_offset(ecx)?, Some(data))?;
128128
Ok(())
@@ -163,33 +163,41 @@ fn mutex_kind_from_static_initializer<'tcx>(
163163
ecx: &MiriInterpCx<'tcx>,
164164
mutex: &MPlaceTy<'tcx>,
165165
) -> InterpResult<'tcx, MutexKind> {
166-
let kind = match &*ecx.tcx.sess.target.os {
166+
Ok(match &*ecx.tcx.sess.target.os {
167167
// Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT.
168168
"linux" => {
169169
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
170170
let kind_place =
171171
mutex.offset(Size::from_bytes(offset), ecx.machine.layouts.i32, ecx)?;
172-
ecx.read_scalar(&kind_place)?.to_i32()?
172+
let kind = ecx.read_scalar(&kind_place)?.to_i32()?;
173+
// Here we give PTHREAD_MUTEX_DEFAULT priority so that
174+
// PTHREAD_MUTEX_INITIALIZER behaves like `pthread_mutex_init` with a NULL argument.
175+
if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") {
176+
MutexKind::Default
177+
} else {
178+
mutex_translate_kind(ecx, kind)?
179+
}
173180
}
174-
"illumos" | "solaris" | "macos" => ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"),
175-
os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
176-
};
177-
178-
mutex_translate_kind(ecx, kind)
181+
_ => MutexKind::Default,
182+
})
179183
}
180184

181185
fn mutex_translate_kind<'tcx>(
182186
ecx: &MiriInterpCx<'tcx>,
183187
kind: i32,
184188
) -> InterpResult<'tcx, MutexKind> {
185-
Ok(if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") {
186-
MutexKind::Default
187-
} else if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL") | PTHREAD_MUTEX_NORMAL_FLAG) {
189+
Ok(if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")) {
188190
MutexKind::Normal
189191
} else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
190192
MutexKind::ErrorCheck
191193
} else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
192194
MutexKind::Recursive
195+
} else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
196+
|| kind == PTHREAD_MUTEX_KIND_UNCHANGED
197+
{
198+
// We check this *last* since PTHREAD_MUTEX_DEFAULT may be numerically equal to one of the
199+
// others, and we want an explicit `mutexattr_settype` to work as expected.
200+
MutexKind::Default
193201
} else {
194202
throw_unsup_format!("unsupported type of mutex: {kind}");
195203
})
@@ -208,7 +216,7 @@ pub struct AdditionalRwLockData {
208216

209217
fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
210218
let offset = match &*ecx.tcx.sess.target.os {
211-
"linux" | "illumos" | "solaris" => 0,
219+
"linux" | "illumos" | "solaris" | "freebsd" => 0,
212220
// macOS stores a signature in the first bytes, so we have to move to offset 4.
213221
"macos" => 4,
214222
os => throw_unsup_format!("`pthread_rwlock` is not supported on {os}"),
@@ -261,7 +269,7 @@ fn rwlock_get_id<'tcx>(
261269
#[inline]
262270
fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
263271
Ok(match &*ecx.tcx.sess.target.os {
264-
"linux" | "illumos" | "solaris" => 0,
272+
"linux" | "illumos" | "solaris" | "freebsd" => 0,
265273
// macOS does not have a clock attribute.
266274
os => throw_unsup_format!("`pthread_condattr` clock field is not supported on {os}"),
267275
})
@@ -313,7 +321,7 @@ fn condattr_set_clock_id<'tcx>(
313321

314322
fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
315323
let offset = match &*ecx.tcx.sess.target.os {
316-
"linux" | "illumos" | "solaris" => 0,
324+
"linux" | "illumos" | "solaris" | "freebsd" => 0,
317325
// macOS stores a signature in the first bytes, so we have to move to offset 4.
318326
"macos" => 4,
319327
os => throw_unsup_format!("`pthread_cond` is not supported on {os}"),
@@ -380,8 +388,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
380388
fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
381389
let this = self.eval_context_mut();
382390

383-
let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT");
384-
mutexattr_set_kind(this, attr_op, default_kind)?;
391+
mutexattr_set_kind(this, attr_op, PTHREAD_MUTEX_KIND_UNCHANGED)?;
385392

386393
Ok(())
387394
}
@@ -394,30 +401,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
394401
let this = self.eval_context_mut();
395402

396403
let kind = this.read_scalar(kind_op)?.to_i32()?;
397-
if kind == this.eval_libc_i32("PTHREAD_MUTEX_NORMAL") {
398-
// In `glibc` implementation, the numeric values of
399-
// `PTHREAD_MUTEX_NORMAL` and `PTHREAD_MUTEX_DEFAULT` are equal.
400-
// However, a mutex created by explicitly passing
401-
// `PTHREAD_MUTEX_NORMAL` type has in some cases different behaviour
402-
// from the default mutex for which the type was not explicitly
403-
// specified. For a more detailed discussion, please see
404-
// https://github.com/rust-lang/miri/issues/1419.
405-
//
406-
// To distinguish these two cases in already constructed mutexes, we
407-
// use the same trick as glibc: for the case when
408-
// `pthread_mutexattr_settype` is called explicitly, we set the
409-
// `PTHREAD_MUTEX_NORMAL_FLAG` flag.
410-
let normal_kind = kind | PTHREAD_MUTEX_NORMAL_FLAG;
411-
// Check that after setting the flag, the kind is distinguishable
412-
// from all other kinds.
413-
assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"));
414-
assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK"));
415-
assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE"));
416-
mutexattr_set_kind(this, attr_op, normal_kind)?;
417-
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
404+
if kind == this.eval_libc_i32("PTHREAD_MUTEX_NORMAL")
405+
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
418406
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
419407
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")
420408
{
409+
// Make sure we do not mix this up with the "unchanged" kind.
410+
assert_ne!(kind, PTHREAD_MUTEX_KIND_UNCHANGED);
421411
mutexattr_set_kind(this, attr_op, kind)?;
422412
} else {
423413
let einval = this.eval_libc_i32("EINVAL");
@@ -461,9 +451,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
461451

462452
let attr = this.read_pointer(attr_op)?;
463453
let kind = if this.ptr_is_null(attr)? {
464-
this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
454+
MutexKind::Default
465455
} else {
466-
mutexattr_get_kind(this, attr_op)?
456+
mutex_translate_kind(this, mutexattr_get_kind(this, attr_op)?)?
467457
};
468458

469459
mutex_create(this, mutex_op, kind)?;

src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
fn main() {
66
unsafe {
7-
let mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
7+
let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
8+
assert_eq!(libc::pthread_mutexattr_init(&mut mutexattr as *mut _), 0);
89
let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
910
assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0);
1011
assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);

0 commit comments

Comments
 (0)