Skip to content

update Miri #103721

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Oct 30, 2022
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions src/tools/miri/.github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ on:
branches:
- 'master'
schedule:
- cron: '5 15 * * *' # At 15:05 UTC every day.
- cron: '6 6 * * *' # At 6:06 UTC every day.

env:
CARGO_UNSTABLE_SPARSE_REGISTRY: 'true'
@@ -24,16 +24,12 @@ jobs:
strategy:
fail-fast: false
matrix:
build: [linux64, macos, win32]
include:
- build: linux64
os: ubuntu-latest
- os: ubuntu-latest
host_target: x86_64-unknown-linux-gnu
- build: macos
os: macos-latest
- os: macos-latest
host_target: x86_64-apple-darwin
- build: win32
os: windows-latest
- os: windows-latest
host_target: i686-pc-windows-msvc
steps:
- uses: actions/checkout@v3
1 change: 1 addition & 0 deletions src/tools/miri/ci.sh
Original file line number Diff line number Diff line change
@@ -93,6 +93,7 @@ case $HOST_TARGET in
;;
i686-pc-windows-msvc)
MIRI_TEST_TARGET=x86_64-unknown-linux-gnu run_tests
MIRI_TEST_TARGET=x86_64-pc-windows-gnu run_tests
;;
*)
echo "FATAL: unknown OS"
4 changes: 2 additions & 2 deletions src/tools/miri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -98,8 +98,8 @@ pub use crate::eval::{
pub use crate::helpers::{CurrentSpan, EvalContextExt as _};
pub use crate::intptrcast::ProvenanceMode;
pub use crate::machine::{
AllocExtra, FrameData, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind, Provenance,
ProvenanceExtra, PAGE_SIZE, STACK_ADDR, STACK_SIZE,
AllocExtra, FrameData, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind,
PrimitiveLayouts, Provenance, ProvenanceExtra, PAGE_SIZE, STACK_ADDR, STACK_SIZE,
};
pub use crate::mono_hash_map::MonoHashMap;
pub use crate::operator::EvalContextExt as _;
30 changes: 30 additions & 0 deletions src/tools/miri/src/machine.rs
Original file line number Diff line number Diff line change
@@ -276,10 +276,14 @@ pub struct PrimitiveLayouts<'tcx> {
pub i8: TyAndLayout<'tcx>,
pub i16: TyAndLayout<'tcx>,
pub i32: TyAndLayout<'tcx>,
pub i64: TyAndLayout<'tcx>,
pub i128: TyAndLayout<'tcx>,
pub isize: TyAndLayout<'tcx>,
pub u8: TyAndLayout<'tcx>,
pub u16: TyAndLayout<'tcx>,
pub u32: TyAndLayout<'tcx>,
pub u64: TyAndLayout<'tcx>,
pub u128: TyAndLayout<'tcx>,
pub usize: TyAndLayout<'tcx>,
pub bool: TyAndLayout<'tcx>,
pub mut_raw_ptr: TyAndLayout<'tcx>, // *mut ()
@@ -296,16 +300,42 @@ impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
i8: layout_cx.layout_of(tcx.types.i8)?,
i16: layout_cx.layout_of(tcx.types.i16)?,
i32: layout_cx.layout_of(tcx.types.i32)?,
i64: layout_cx.layout_of(tcx.types.i64)?,
i128: layout_cx.layout_of(tcx.types.i128)?,
isize: layout_cx.layout_of(tcx.types.isize)?,
u8: layout_cx.layout_of(tcx.types.u8)?,
u16: layout_cx.layout_of(tcx.types.u16)?,
u32: layout_cx.layout_of(tcx.types.u32)?,
u64: layout_cx.layout_of(tcx.types.u64)?,
u128: layout_cx.layout_of(tcx.types.u128)?,
usize: layout_cx.layout_of(tcx.types.usize)?,
bool: layout_cx.layout_of(tcx.types.bool)?,
mut_raw_ptr: layout_cx.layout_of(mut_raw_ptr)?,
const_raw_ptr: layout_cx.layout_of(const_raw_ptr)?,
})
}

pub fn uint(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
match size.bits() {
8 => Some(self.u8),
16 => Some(self.u16),
32 => Some(self.u32),
64 => Some(self.u64),
128 => Some(self.u128),
_ => None,
}
}

pub fn int(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
match size.bits() {
8 => Some(self.i8),
16 => Some(self.i16),
32 => Some(self.i32),
64 => Some(self.i64),
128 => Some(self.i128),
_ => None,
}
}
}

/// The machine itself.
13 changes: 12 additions & 1 deletion src/tools/miri/src/shims/intrinsics/mod.rs
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ use rustc_middle::{
mir,
ty::{self, FloatTy, Ty},
};
use rustc_target::abi::Integer;
use rustc_target::abi::{Integer, Size};

use crate::*;
use atomic::EvalContextExt as _;
@@ -120,6 +120,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.write_bytes_ptr(ptr, iter::repeat(val_byte).take(byte_count.bytes_usize()))?;
}

"ptr_mask" => {
let [ptr, mask] = check_arg_count(args)?;

let ptr = this.read_pointer(ptr)?;
let mask = this.read_scalar(mask)?.to_machine_usize(this)?;

let masked_addr = Size::from_bytes(ptr.addr().bytes() & mask);

this.write_pointer(Pointer::new(ptr.provenance, masked_addr), dest)?;
}

// Floating-point operations
"fabsf32" => {
let [f] = check_arg_count(args)?;
40 changes: 13 additions & 27 deletions src/tools/miri/src/shims/unix/linux/sync.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::time::SystemTime;

use crate::concurrency::thread::{MachineCallback, Time};
use crate::*;
use rustc_target::abi::{Align, Size};
use std::time::SystemTime;

/// Implementation of the SYS_futex syscall.
/// `args` is the arguments *after* the syscall number.
@@ -28,13 +28,14 @@ pub fn futex<'tcx>(
// The first three arguments (after the syscall number itself) are the same to all futex operations:
// (int *addr, int op, int val).
// We checked above that these definitely exist.
let addr = this.read_immediate(&args[0])?;
let addr = this.read_pointer(&args[0])?;
let op = this.read_scalar(&args[1])?.to_i32()?;
let val = this.read_scalar(&args[2])?.to_i32()?;

let thread = this.get_active_thread();
let addr_scalar = addr.to_scalar();
let addr_usize = addr_scalar.to_machine_usize(this)?;
// This is a vararg function so we have to bring our own type for this pointer.
let addr = MPlaceTy::from_aligned_ptr(addr, this.machine.layouts.i32);
let addr_usize = addr.ptr.addr().bytes();

let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG")?;
let futex_wait = this.eval_libc_i32("FUTEX_WAIT")?;
@@ -89,9 +90,11 @@ pub fn futex<'tcx>(
let timeout_time = if this.ptr_is_null(timeout.ptr)? {
None
} else {
this.check_no_isolation(
"`futex` syscall with `op=FUTEX_WAIT` and non-null timeout",
)?;
if op & futex_realtime != 0 {
this.check_no_isolation(
"`futex` syscall with `op=FUTEX_WAIT` and non-null timeout with `FUTEX_CLOCK_REALTIME`",
)?;
}
let duration = match this.read_timespec(&timeout)? {
Some(duration) => duration,
None => {
@@ -117,15 +120,6 @@ pub fn futex<'tcx>(
}
})
};
// Check the pointer for alignment and validity.
// The API requires `addr` to be a 4-byte aligned pointer, and will
// use the 4 bytes at the given address as an (atomic) i32.
this.check_ptr_access_align(
addr_scalar.to_pointer(this)?,
Size::from_bytes(4),
Align::from_bytes(4).unwrap(),
CheckInAllocMsg::MemoryAccessTest,
)?;
// There may be a concurrent thread changing the value of addr
// and then invoking the FUTEX_WAKE syscall. It is critical that the
// effects of this and the other thread are correctly observed,
@@ -172,14 +166,7 @@ pub fn futex<'tcx>(
this.atomic_fence(AtomicFenceOrd::SeqCst)?;
// Read an `i32` through the pointer, regardless of any wrapper types.
// It's not uncommon for `addr` to be passed as another type than `*mut i32`, such as `*const AtomicI32`.
let futex_val = this
.read_scalar_at_offset_atomic(
&addr.into(),
0,
this.machine.layouts.i32,
AtomicReadOrd::Relaxed,
)?
.to_i32()?;
let futex_val = this.read_scalar_atomic(&addr, AtomicReadOrd::Relaxed)?.to_i32()?;
if val == futex_val {
// The value still matches, so we block the thread make it wait for FUTEX_WAKE.
this.block_thread(thread);
@@ -214,11 +201,10 @@ pub fn futex<'tcx>(
}
}

let dest = dest.clone();
this.register_timeout_callback(
thread,
timeout_time,
Box::new(Callback { thread, addr_usize, dest }),
Box::new(Callback { thread, addr_usize, dest: dest.clone() }),
);
}
} else {
5 changes: 4 additions & 1 deletion src/tools/miri/src/shims/unix/macos/foreign_items.rs
Original file line number Diff line number Diff line change
@@ -177,11 +177,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let thread = this.pthread_self()?;
let max_len = this.eval_libc("MAXTHREADNAMESIZE")?.to_machine_usize(this)?;
this.pthread_setname_np(
let res = this.pthread_setname_np(
thread,
this.read_scalar(name)?,
max_len.try_into().unwrap(),
)?;
// Contrary to the manpage, `pthread_setname_np` on macOS still
// returns an integer indicating success.
this.write_scalar(res, dest)?;
}
"pthread_getname_np" => {
let [thread, name, len] =
3 changes: 1 addition & 2 deletions src/tools/miri/src/shims/unix/sync.rs
Original file line number Diff line number Diff line change
@@ -743,8 +743,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();

this.check_no_isolation("`pthread_cond_timedwait`")?;

let id = this.condvar_get_or_create_id(cond_op, CONDVAR_ID_OFFSET)?;
let mutex_id = this.mutex_get_or_create_id(mutex_op, MUTEX_ID_OFFSET)?;
let active_thread = this.get_active_thread();
@@ -761,6 +759,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
};

let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME")? {
this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap())
15 changes: 15 additions & 0 deletions src/tools/miri/src/shims/windows/dlsym.rs
Original file line number Diff line number Diff line change
@@ -6,12 +6,15 @@ use log::trace;

use crate::helpers::check_arg_count;
use crate::shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle};
use crate::shims::windows::sync::EvalContextExt as _;
use crate::*;

#[derive(Debug, Copy, Clone)]
pub enum Dlsym {
NtWriteFile,
SetThreadDescription,
WaitOnAddress,
WakeByAddressSingle,
}

impl Dlsym {
@@ -22,6 +25,8 @@ impl Dlsym {
"GetSystemTimePreciseAsFileTime" => None,
"NtWriteFile" => Some(Dlsym::NtWriteFile),
"SetThreadDescription" => Some(Dlsym::SetThreadDescription),
"WaitOnAddress" => Some(Dlsym::WaitOnAddress),
"WakeByAddressSingle" => Some(Dlsym::WakeByAddressSingle),
_ => throw_unsup_format!("unsupported Windows dlsym: {}", name),
})
}
@@ -127,6 +132,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

this.write_null(dest)?;
}
Dlsym::WaitOnAddress => {
let [ptr_op, compare_op, size_op, timeout_op] = check_arg_count(args)?;

this.WaitOnAddress(ptr_op, compare_op, size_op, timeout_op, dest)?;
}
Dlsym::WakeByAddressSingle => {
let [ptr_op] = check_arg_count(args)?;

this.WakeByAddressSingle(ptr_op)?;
}
}

trace!("{:?}", this.dump_place(**dest));
106 changes: 105 additions & 1 deletion src/tools/miri/src/shims/windows/sync.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use std::time::Duration;

use rustc_target::abi::Size;

use crate::concurrency::init_once::InitOnceStatus;
use crate::concurrency::thread::MachineCallback;
use crate::*;
@@ -6,7 +10,6 @@ const SRWLOCK_ID_OFFSET: u64 = 0;
const INIT_ONCE_ID_OFFSET: u64 = 0;

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}

#[allow(non_snake_case)]
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn AcquireSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
@@ -221,4 +224,105 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

this.eval_windows("c", "TRUE")
}

fn WaitOnAddress(
&mut self,
ptr_op: &OpTy<'tcx, Provenance>,
compare_op: &OpTy<'tcx, Provenance>,
size_op: &OpTy<'tcx, Provenance>,
timeout_op: &OpTy<'tcx, Provenance>,
dest: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();

let ptr = this.read_pointer(ptr_op)?;
let compare = this.read_pointer(compare_op)?;
let size = this.read_scalar(size_op)?.to_machine_usize(this)?;
let timeout_ms = this.read_scalar(timeout_op)?.to_u32()?;

let thread = this.get_active_thread();
let addr = ptr.addr().bytes();

if size > 8 || !size.is_power_of_two() {
let invalid_param = this.eval_windows("c", "ERROR_INVALID_PARAMETER")?;
this.set_last_error(invalid_param)?;
this.write_scalar(Scalar::from_i32(0), dest)?;
return Ok(());
};
let size = Size::from_bytes(size);

let timeout_time = if timeout_ms == this.eval_windows("c", "INFINITE")?.to_u32()? {
None
} else {
let duration = Duration::from_millis(timeout_ms.into());
Some(Time::Monotonic(this.machine.clock.now().checked_add(duration).unwrap()))
};

// See the Linux futex implementation for why this fence exists.
this.atomic_fence(AtomicFenceOrd::SeqCst)?;

let layout = this.machine.layouts.uint(size).unwrap();
let futex_val = this
.read_scalar_atomic(&MPlaceTy::from_aligned_ptr(ptr, layout), AtomicReadOrd::Relaxed)?;
let compare_val = this.read_scalar(&MPlaceTy::from_aligned_ptr(compare, layout).into())?;

if futex_val == compare_val {
// If the values are the same, we have to block.
this.block_thread(thread);
this.futex_wait(addr, thread, u32::MAX);

if let Some(timeout_time) = timeout_time {
struct Callback<'tcx> {
thread: ThreadId,
addr: u64,
dest: PlaceTy<'tcx, Provenance>,
}

impl<'tcx> VisitTags for Callback<'tcx> {
fn visit_tags(&self, visit: &mut dyn FnMut(SbTag)) {
let Callback { thread: _, addr: _, dest } = self;
dest.visit_tags(visit);
}
}

impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> {
fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
this.unblock_thread(self.thread);
this.futex_remove_waiter(self.addr, self.thread);
let error_timeout = this.eval_windows("c", "ERROR_TIMEOUT")?;
this.set_last_error(error_timeout)?;
this.write_scalar(Scalar::from_i32(0), &self.dest)?;

Ok(())
}
}

this.register_timeout_callback(
thread,
timeout_time,
Box::new(Callback { thread, addr, dest: dest.clone() }),
);
}
}

this.write_scalar(Scalar::from_i32(1), dest)?;

Ok(())
}

fn WakeByAddressSingle(&mut self, ptr_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();

let ptr = this.read_pointer(ptr_op)?;

// See the Linux futex implementation for why this fence exists.
this.atomic_fence(AtomicFenceOrd::SeqCst)?;

if let Some(thread) = this.futex_wake(ptr.addr().bytes(), u32::MAX) {
this.unblock_thread(thread);
this.unregister_timeout_callback_if_exists(thread);
}

Ok(())
}
}
1 change: 0 additions & 1 deletion src/tools/miri/tests/fail/data_race/stack_pop_race.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//@ignore-target-windows: Concurrency on Windows is not supported yet.
//@compile-flags: -Zmiri-preemption-rate=0
use std::thread;

2 changes: 1 addition & 1 deletion src/tools/miri/tests/fail/panic/no_std.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
// windows tls dtors go through libstd right now, thus this test
// cannot pass. When windows tls dtors go through the special magic
// windows linker section, we can run this test on windows again.
//@ignore-target-windows
//@ignore-target-windows: no-std not supported on Windows

// Plumbing to let us use `writeln!` to host stderr:

Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//@ignore-target-windows: No libc on Windows
//@ignore-target-apple: pthread_condattr_setclock is not supported on MacOS.

/// Test that conditional variable timeouts are working properly
/// with monotonic clocks even under isolation.
use std::mem::MaybeUninit;
use std::time::Instant;

fn test_timed_wait_timeout(clock_id: i32) {
unsafe {
let mut attr: MaybeUninit<libc::pthread_condattr_t> = MaybeUninit::uninit();
assert_eq!(libc::pthread_condattr_init(attr.as_mut_ptr()), 0);
assert_eq!(libc::pthread_condattr_setclock(attr.as_mut_ptr(), clock_id), 0);

let mut cond: MaybeUninit<libc::pthread_cond_t> = MaybeUninit::uninit();
assert_eq!(libc::pthread_cond_init(cond.as_mut_ptr(), attr.as_ptr()), 0);
assert_eq!(libc::pthread_condattr_destroy(attr.as_mut_ptr()), 0);

let mut mutex: libc::pthread_mutex_t = libc::PTHREAD_MUTEX_INITIALIZER;

let mut now_mu: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
assert_eq!(libc::clock_gettime(clock_id, now_mu.as_mut_ptr()), 0);
let now = now_mu.assume_init();
// Waiting for a second... mostly because waiting less requires mich more tricky arithmetic.
// FIXME: wait less.
let timeout = libc::timespec { tv_sec: now.tv_sec + 1, tv_nsec: now.tv_nsec };

assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
let current_time = Instant::now();
assert_eq!(
libc::pthread_cond_timedwait(cond.as_mut_ptr(), &mut mutex as *mut _, &timeout),
libc::ETIMEDOUT
);
let elapsed_time = current_time.elapsed().as_millis();
assert!(900 <= elapsed_time && elapsed_time <= 1300);

// Test calling `pthread_cond_timedwait` again with an already elapsed timeout.
assert_eq!(
libc::pthread_cond_timedwait(cond.as_mut_ptr(), &mut mutex as *mut _, &timeout),
libc::ETIMEDOUT
);

// Test that invalid nanosecond values (above 10^9 or negative) are rejected with the
// correct error code.
let invalid_timeout_1 = libc::timespec { tv_sec: now.tv_sec + 1, tv_nsec: 1_000_000_000 };
assert_eq!(
libc::pthread_cond_timedwait(
cond.as_mut_ptr(),
&mut mutex as *mut _,
&invalid_timeout_1
),
libc::EINVAL
);
let invalid_timeout_2 = libc::timespec { tv_sec: now.tv_sec + 1, tv_nsec: -1 };
assert_eq!(
libc::pthread_cond_timedwait(
cond.as_mut_ptr(),
&mut mutex as *mut _,
&invalid_timeout_2
),
libc::EINVAL
);
// Test that invalid second values (negative) are rejected with the correct error code.
let invalid_timeout_3 = libc::timespec { tv_sec: -1, tv_nsec: 0 };
assert_eq!(
libc::pthread_cond_timedwait(
cond.as_mut_ptr(),
&mut mutex as *mut _,
&invalid_timeout_3
),
libc::EINVAL
);

assert_eq!(libc::pthread_mutex_unlock(&mut mutex as *mut _), 0);
assert_eq!(libc::pthread_mutex_destroy(&mut mutex as *mut _), 0);
assert_eq!(libc::pthread_cond_destroy(cond.as_mut_ptr()), 0);
}
}

fn main() {
test_timed_wait_timeout(libc::CLOCK_MONOTONIC);
}
22 changes: 10 additions & 12 deletions src/tools/miri/tests/pass-dep/shims/libc-misc.rs
Original file line number Diff line number Diff line change
@@ -87,7 +87,7 @@ fn test_posix_realpath_errors() {
assert_eq!(e.kind(), ErrorKind::NotFound);
}

#[cfg(any(target_os = "linux"))]
#[cfg(target_os = "linux")]
fn test_posix_fadvise() {
use std::convert::TryInto;
use std::io::Write;
@@ -115,7 +115,7 @@ fn test_posix_fadvise() {
assert_eq!(result, 0);
}

#[cfg(any(target_os = "linux"))]
#[cfg(target_os = "linux")]
fn test_sync_file_range() {
use std::io::Write;

@@ -181,7 +181,7 @@ fn test_thread_local_errno() {
}

/// Tests whether clock support exists at all
#[cfg(any(target_os = "linux"))]
#[cfg(target_os = "linux")]
fn test_clocks() {
let mut tp = std::mem::MaybeUninit::<libc::timespec>::uninit();
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) };
@@ -283,23 +283,21 @@ fn test_posix_mkstemp() {
}

fn main() {
#[cfg(any(target_os = "linux"))]
test_posix_fadvise();

test_posix_gettimeofday();
test_posix_mkstemp();

test_posix_realpath_alloc();
test_posix_realpath_noalloc();
test_posix_realpath_errors();

#[cfg(any(target_os = "linux"))]
test_sync_file_range();

test_thread_local_errno();

#[cfg(any(target_os = "linux"))]
test_clocks();

test_isatty();

#[cfg(target_os = "linux")]
{
test_posix_fadvise();
test_sync_file_range();
test_clocks();
}
}
20 changes: 16 additions & 4 deletions src/tools/miri/tests/pass-dep/shims/pthreads.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//@ignore-target-windows: No libc on Windows
#![feature(cstr_from_bytes_until_nul)]
use std::ffi::CStr;
use std::ffi::{CStr, CString};
use std::thread;

fn main() {
@@ -10,7 +10,7 @@ fn main() {
test_rwlock_libc_static_initializer();
test_named_thread_truncation();

#[cfg(any(target_os = "linux"))]
#[cfg(target_os = "linux")]
test_mutex_libc_static_initializer_recursive();
}

@@ -135,18 +135,30 @@ fn test_named_thread_truncation() {
.chain(std::iter::repeat(" yada").take(100))
.collect::<String>();

fn set_thread_name(name: &CStr) -> i32 {
#[cfg(target_os = "linux")]
return unsafe { libc::pthread_setname_np(libc::pthread_self(), name.as_ptr().cast()) };
#[cfg(target_os = "macos")]
return unsafe { libc::pthread_setname_np(name.as_ptr().cast()) };
}

let result = thread::Builder::new().name(long_name.clone()).spawn(move || {
// Rust remembers the full thread name itself.
assert_eq!(thread::current().name(), Some(long_name.as_str()));

// But the system is limited -- make sure we successfully set a truncation.
let mut buf = vec![0u8; long_name.len() + 1];
unsafe {
libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len());
}
libc::pthread_getname_np(libc::pthread_self(), buf.as_mut_ptr().cast(), buf.len())
};
let cstr = CStr::from_bytes_until_nul(&buf).unwrap();
assert!(cstr.to_bytes().len() >= 15); // POSIX seems to promise at least 15 chars
assert!(long_name.as_bytes().starts_with(cstr.to_bytes()));

// Also test directly calling pthread_setname to check its return value.
assert_eq!(set_thread_name(&cstr), 0);
// But with a too long name it should fail.
assert_ne!(set_thread_name(&CString::new(long_name).unwrap()), 0);
});
result.unwrap().join().unwrap();
}
1 change: 0 additions & 1 deletion src/tools/miri/tests/pass/concurrency/channels.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//@ignore-target-windows: Channels on Windows are not supported yet.
//@compile-flags: -Zmiri-strict-provenance

use std::sync::mpsc::{channel, sync_channel};
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//@ignore-target-windows: Channels on Windows are not supported yet.
// This specifically tests behavior *without* preemption.
//@compile-flags: -Zmiri-preemption-rate=0

21 changes: 16 additions & 5 deletions src/tools/miri/tests/pass/concurrency/sync.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//@ignore-target-windows: Concurrency on Windows is not supported yet.
//@compile-flags: -Zmiri-disable-isolation -Zmiri-strict-provenance

use std::sync::{Arc, Barrier, Condvar, Mutex, Once, RwLock};
@@ -225,14 +224,26 @@ fn park_unpark() {
}

fn main() {
check_barriers();
check_conditional_variables_notify_one();
check_conditional_variables_timed_wait_timeout();
check_conditional_variables_timed_wait_notimeout();
check_mutex();
check_rwlock_write();
check_rwlock_read_no_deadlock();
check_once();
park_timeout();
park_unpark();

if !cfg!(windows) {
// ignore-target-windows: Condvars on Windows are not supported yet
check_barriers();
check_conditional_variables_notify_one();
check_conditional_variables_timed_wait_timeout();
check_conditional_variables_timed_wait_notimeout();
} else {
// We need to fake the same output...
for _ in 0..10 {
println!("before wait");
}
for _ in 0..10 {
println!("after wait");
}
}
}
2 changes: 1 addition & 1 deletion src/tools/miri/tests/pass/concurrency/sync_nopreempt.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//@ignore-target-windows: Concurrency on Windows is not supported yet.
//@ignore-target-windows: Condvars on Windows are not supported yet.
// We are making scheduler assumptions here.
//@compile-flags: -Zmiri-strict-provenance -Zmiri-preemption-rate=0

12 changes: 12 additions & 0 deletions src/tools/miri/tests/pass/concurrency/thread_park_isolated.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//@ignore-target-apple: park_timeout on macOS uses the system clock
use std::thread;
use std::time::{Duration, Instant};

fn main() {
let start = Instant::now();

thread::park_timeout(Duration::from_millis(200));

// Thanks to deterministic execution, this will wiat *exactly* 200ms (rounded to 1ms).
assert!((200..201).contains(&start.elapsed().as_millis()));
}
2 changes: 1 addition & 1 deletion src/tools/miri/tests/pass/no_std.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
// windows tls dtors go through libstd right now, thus this test
// cannot pass. When windows tls dtors go through the special magic
// windows linker section, we can run this test on windows again.
//@ignore-target-windows
//@ignore-target-windows: no-std not supported on Windows

// Plumbing to let us use `writeln!` to host stdout:

2 changes: 1 addition & 1 deletion src/tools/miri/tests/pass/shims/env/current_exe.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//@ignore-target-windows
//@ignore-target-windows: current_exe not supported on Windows
//@only-on-host: the Linux std implementation opens /proc/self/exe, which doesn't work cross-target
//@compile-flags: -Zmiri-disable-isolation
use std::env;
18 changes: 18 additions & 0 deletions src/tools/miri/tests/pass/shims/ptr_mask.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#![feature(ptr_mask)]
#![feature(strict_provenance)]

fn main() {
let v: u32 = 0xABCDABCD;
let ptr: *const u32 = &v;

// u32 is 4 aligned,
// so the lower `log2(4) = 2` bits of the address are always 0
assert_eq!(ptr.addr() & 0b11, 0);

let tagged_ptr = ptr.map_addr(|a| a | 0b11);
let tag = tagged_ptr.addr() & 0b11;
let masked_ptr = tagged_ptr.mask(!0b11);

assert_eq!(tag, 0b11);
assert_eq!(unsafe { *masked_ptr }, 0xABCDABCD);
}
1 change: 0 additions & 1 deletion src/tools/miri/tests/pass/shims/sleep_long.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
//@ignore-target-windows: no threads nor sleep on Windows
//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-isolation
use std::sync::{Arc, Mutex};
use std::thread;
4 changes: 1 addition & 3 deletions src/tools/miri/tests/pass/threadleak_ignored.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
//@ignore-target-windows: Channels on Windows are not supported yet.
// FIXME: disallow preemption to work around https://github.com/rust-lang/rust/issues/55005
//@compile-flags: -Zmiri-ignore-leaks -Zmiri-preemption-rate=0
//@compile-flags: -Zmiri-ignore-leaks

//! Test that leaking threads works, and that their destructors are not executed.