Skip to content

Commit 735fc12

Browse files
committed
Handle variation in layout of pthread_mutex_t
1 parent e5e3256 commit 735fc12

File tree

1 file changed

+74
-12
lines changed

1 file changed

+74
-12
lines changed

src/shims/sync.rs

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
use std::sync::atomic::{AtomicU64, Ordering};
2+
13
use rustc_middle::ty::{TyKind, TypeAndMut};
2-
use rustc_target::abi::{LayoutOf, Size};
4+
use rustc_target::abi::{FieldsShape, LayoutOf, Size};
35

46
use crate::stacked_borrows::Tag;
57
use crate::*;
@@ -399,15 +401,67 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>(
399401
// bytes 0-3: reserved for signature on macOS
400402
// (need to avoid this because it is set by static initializer macros)
401403
// bytes 4-7: count of how many times this mutex has been locked, as a u32
402-
// bytes 12-15: mutex kind, as an i32
403-
// (the kind has to be at this offset for compatibility with static initializer macros)
404+
// bytes 12-15 or 16-19 (depending on platform): mutex kind, as an i32
405+
// (the kind has to be at its offset for compatibility with static initializer macros)
406+
407+
static LIBC_MUTEX_KIND_OFFSET_CACHE: AtomicU64 = AtomicU64::new(0);
408+
409+
fn libc_mutex_kind_offset<'mir, 'tcx: 'mir>(
410+
ecx: &mut MiriEvalContext<'mir, 'tcx>,
411+
) -> InterpResult<'tcx, u64> {
412+
// Check if this offset has already been found and memoized
413+
let cached_value = LIBC_MUTEX_KIND_OFFSET_CACHE.load(Ordering::Relaxed);
414+
if cached_value != 0 {
415+
return Ok(cached_value);
416+
}
417+
418+
// This function infers the offset of the `kind` field of libc's pthread_mutex_t
419+
// C struct by examining the array inside libc::PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.
420+
// At time of writing, it is always all zero bytes except for a one byte at one of
421+
// four positions, depending on the target OS's C struct layout and the endianness of the
422+
// target architecture. This offset will then be used in getters and setters below, so that
423+
// mutexes created from static initializers can be emulated with the correct behavior.
424+
let initializer_path = ["libc", "PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP"];
425+
let initializer_instance = ecx.resolve_path(&initializer_path);
426+
let initializer_cid = GlobalId { instance: initializer_instance, promoted: None };
427+
let initializer_const_val = ecx.const_eval_raw(initializer_cid)?;
428+
let array_mplacety = ecx.mplace_field(initializer_const_val, 0)?;
429+
let array_length = match array_mplacety.layout.fields {
430+
FieldsShape::Array { count, .. } => count,
431+
_ => bug!("Couldn't get array length from type {:?}", array_mplacety.layout.ty),
432+
};
433+
434+
let kind_offset = if array_length < 20 {
435+
bug!("libc::PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP array was shorter than expected");
436+
} else if ecx.read_scalar(ecx.mplace_field(array_mplacety, 16)?.into())?.to_u8()? != 0 {
437+
// for little-endian architectures
438+
16
439+
} else if ecx.read_scalar(ecx.mplace_field(array_mplacety, 19)?.into())?.to_u8()? != 0 {
440+
// for big-endian architectures
441+
// (note that the i32 spans bytes 16 through 19, so the offset of the kind field is 16)
442+
16
443+
} else if ecx.read_scalar(ecx.mplace_field(array_mplacety, 12)?.into())?.to_u8()? != 0 {
444+
// for little-endian architectures
445+
12
446+
} else if ecx.read_scalar(ecx.mplace_field(array_mplacety, 15)?.into())?.to_u8()? != 0 {
447+
// for big-endian architectures
448+
// (note that the i32 spans bytes 12 through 15, so the offset of the kind field is 12)
449+
12
450+
} else {
451+
bug!("Couldn't determine offset of `kind` in pthread_mutex_t");
452+
};
453+
454+
// Save offset to memoization cache for future calls
455+
LIBC_MUTEX_KIND_OFFSET_CACHE.store(kind_offset, Ordering::Relaxed);
456+
Ok(kind_offset)
457+
}
404458

405459
fn mutex_get_locked_count<'mir, 'tcx: 'mir>(
406460
ecx: &MiriEvalContext<'mir, 'tcx>,
407461
mutex_op: OpTy<'tcx, Tag>,
408462
) -> InterpResult<'tcx, ScalarMaybeUndef<Tag>> {
409463
// Ensure that the following read at an offset to the mutex pointer is within bounds
410-
assert_ptr_target_min_size(ecx, mutex_op, 16)?;
464+
assert_ptr_target_min_size(ecx, mutex_op, 20)?;
411465
let mutex_place = ecx.deref_operand(mutex_op)?;
412466
let u32_layout = ecx.layout_of(ecx.tcx.types.u32)?;
413467
let locked_count_place =
@@ -421,7 +475,7 @@ fn mutex_set_locked_count<'mir, 'tcx: 'mir>(
421475
locked_count: impl Into<ScalarMaybeUndef<Tag>>,
422476
) -> InterpResult<'tcx, ()> {
423477
// Ensure that the following write at an offset to the mutex pointer is within bounds
424-
assert_ptr_target_min_size(ecx, mutex_op, 16)?;
478+
assert_ptr_target_min_size(ecx, mutex_op, 20)?;
425479
let mutex_place = ecx.deref_operand(mutex_op)?;
426480
let u32_layout = ecx.layout_of(ecx.tcx.types.u32)?;
427481
let locked_count_place =
@@ -430,15 +484,19 @@ fn mutex_set_locked_count<'mir, 'tcx: 'mir>(
430484
}
431485

432486
fn mutex_get_kind<'mir, 'tcx: 'mir>(
433-
ecx: &MiriEvalContext<'mir, 'tcx>,
487+
ecx: &mut MiriEvalContext<'mir, 'tcx>,
434488
mutex_op: OpTy<'tcx, Tag>,
435489
) -> InterpResult<'tcx, ScalarMaybeUndef<Tag>> {
436490
// Ensure that the following read at an offset to the mutex pointer is within bounds
437-
assert_ptr_target_min_size(ecx, mutex_op, 16)?;
491+
assert_ptr_target_min_size(ecx, mutex_op, 20)?;
438492
let mutex_place = ecx.deref_operand(mutex_op)?;
439493
let i32_layout = ecx.layout_of(ecx.tcx.types.i32)?;
440-
let kind_place =
441-
mutex_place.offset(Size::from_bytes(12), MemPlaceMeta::None, i32_layout, ecx)?;
494+
let kind_place = mutex_place.offset(
495+
Size::from_bytes(libc_mutex_kind_offset(ecx)?),
496+
MemPlaceMeta::None,
497+
i32_layout,
498+
ecx,
499+
)?;
442500
ecx.read_scalar(kind_place.into())
443501
}
444502

@@ -448,11 +506,15 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>(
448506
kind: impl Into<ScalarMaybeUndef<Tag>>,
449507
) -> InterpResult<'tcx, ()> {
450508
// Ensure that the following write at an offset to the mutex pointer is within bounds
451-
assert_ptr_target_min_size(ecx, mutex_op, 16)?;
509+
assert_ptr_target_min_size(ecx, mutex_op, 20)?;
452510
let mutex_place = ecx.deref_operand(mutex_op)?;
453511
let i32_layout = ecx.layout_of(ecx.tcx.types.i32)?;
454-
let kind_place =
455-
mutex_place.offset(Size::from_bytes(12), MemPlaceMeta::None, i32_layout, ecx)?;
512+
let kind_place = mutex_place.offset(
513+
Size::from_bytes(libc_mutex_kind_offset(ecx)?),
514+
MemPlaceMeta::None,
515+
i32_layout,
516+
ecx,
517+
)?;
456518
ecx.write_scalar(kind.into(), kind_place.into())
457519
}
458520

0 commit comments

Comments
 (0)