Skip to content

Commit

Permalink
Auto merge of #117855 - RalfJung:miri, r=RalfJung
Browse files Browse the repository at this point in the history
Miri subtree update

r? `@ghost`
  • Loading branch information
bors committed Nov 12, 2023
2 parents 2b603f9 + f86fa09 commit 0828c15
Show file tree
Hide file tree
Showing 32 changed files with 648 additions and 120 deletions.
2 changes: 1 addition & 1 deletion src/tools/miri/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ case $HOST_TARGET in
MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests
MIRI_TEST_TARGET=aarch64-apple-darwin run_tests
MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests
MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple atomic env/var
MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthreads atomic env/var
MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal hello integer vec panic/panic
MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal no_std integer strings wasm
MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std integer strings wasm
Expand Down
32 changes: 25 additions & 7 deletions src/tools/miri/src/concurrency/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ enum SchedulingAction {
Sleep(Duration),
}

/// What to do with TLS allocations from terminated threads
pub enum TlsAllocAction {
/// Deallocate backing memory of thread-local statics as usual
Deallocate,
/// Skip deallocating backing memory of thread-local statics and consider all memory reachable
/// from them as "allowed to leak" (like global `static`s).
Leak,
}

/// Trait for callbacks that can be executed when some event happens, such as after a timeout.
pub trait MachineCallback<'mir, 'tcx>: VisitTags {
fn call(&self, ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>) -> InterpResult<'tcx>;
Expand Down Expand Up @@ -1051,7 +1060,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// See if this thread can do something else.
match this.run_on_stack_empty()? {
Poll::Pending => {} // keep going
Poll::Ready(()) => this.terminate_active_thread()?,
Poll::Ready(()) =>
this.terminate_active_thread(TlsAllocAction::Deallocate)?,
}
}
}
Expand All @@ -1066,21 +1076,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}

/// Handles thread termination of the active thread: wakes up threads joining on this one,
/// and deallocated thread-local statics.
/// and deals with the thread's thread-local statics according to `tls_alloc_action`.
///
/// This is called by the eval loop when a thread's on_stack_empty returns `Ready`.
#[inline]
fn terminate_active_thread(&mut self) -> InterpResult<'tcx> {
fn terminate_active_thread(&mut self, tls_alloc_action: TlsAllocAction) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let thread = this.active_thread_mut();
assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated");
thread.state = ThreadState::Terminated;

let current_span = this.machine.current_span();
for ptr in
this.machine.threads.thread_terminated(this.machine.data_race.as_mut(), current_span)
{
this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?;
let thread_local_allocations =
this.machine.threads.thread_terminated(this.machine.data_race.as_mut(), current_span);
for ptr in thread_local_allocations {
match tls_alloc_action {
TlsAllocAction::Deallocate =>
this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?,
TlsAllocAction::Leak =>
if let Some(alloc) = ptr.provenance.get_alloc_id() {
trace!("Thread-local static leaked and stored as static root: {:?}", alloc);
this.machine.static_roots.push(alloc);
},
}
}
Ok(())
}
Expand Down
3 changes: 2 additions & 1 deletion src/tools/miri/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,8 @@ pub fn report_error<'tcx, 'mir>(
DataRace { op1, extra, .. } => {
let mut helps = vec![(Some(op1.span), format!("and (1) occurred earlier here"))];
if let Some(extra) = extra {
helps.push((None, format!("{extra}")))
helps.push((None, format!("{extra}")));
helps.push((None, format!("see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model")));
}
helps.push((None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")));
helps.push((None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")));
Expand Down
7 changes: 4 additions & 3 deletions src/tools/miri/src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use log::info;
use rustc_middle::ty::Ty;

use crate::borrow_tracker::RetagFields;
use crate::concurrency::thread::TlsAllocAction;
use crate::diagnostics::report_leaks;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::Namespace;
Expand Down Expand Up @@ -244,9 +245,9 @@ impl MainThreadState {
// Figure out exit code.
let ret_place = this.machine.main_fn_ret_place.clone().unwrap();
let exit_code = this.read_target_isize(&ret_place)?;
// Need to call this ourselves since we are not going to return to the scheduler
// loop, and we want the main thread TLS to not show up as memory leaks.
this.terminate_active_thread()?;
// Deal with our thread-local memory. We do *not* want to actually free it, instead we consider TLS
// to be like a global `static`, so that all memory reached by it is considered to "not leak".
this.terminate_active_thread(TlsAllocAction::Leak)?;
// Stop interpreter loop.
throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true });
}
Expand Down
16 changes: 13 additions & 3 deletions src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
"pthread_set_name_np" => {
let [thread, name] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let max_len = usize::MAX; // freebsd does not seem to have a limit.
let res = this.pthread_setname_np(
let max_len = usize::MAX; // FreeBSD does not seem to have a limit.
// FreeBSD's pthread_set_name_np does not return anything.
this.pthread_setname_np(
this.read_scalar(thread)?,
this.read_scalar(name)?,
max_len,
)?;
this.write_scalar(res, dest)?;
}
"pthread_get_name_np" => {
let [thread, name, len] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
// FreeBSD's pthread_get_name_np does not return anything.
this.pthread_getname_np(
this.read_scalar(thread)?,
this.read_scalar(name)?,
this.read_scalar(len)?,
)?;
}

// errno
Expand Down
70 changes: 70 additions & 0 deletions src/tools/miri/src/shims/unix/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();

if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
throw_unsup_format!(
"`pthread_mutexattr_init` is not supported on {}",
this.tcx.sess.target.os
);
}

let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT");
mutexattr_set_kind(this, attr_op, default_kind)?;

Expand Down Expand Up @@ -359,6 +366,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();

if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
throw_unsup_format!(
"`pthread_mutex_init` is not supported on {}",
this.tcx.sess.target.os
);
}

let attr = this.read_pointer(attr_op)?;
let kind = if this.ptr_is_null(attr)? {
this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
Expand Down Expand Up @@ -513,6 +527,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();

if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
throw_unsup_format!(
"`pthread_rwlock_rdlock` is not supported on {}",
this.tcx.sess.target.os
);
}

let id = rwlock_get_id(this, rwlock_op)?;
let active_thread = this.get_active_thread();

Expand All @@ -531,6 +552,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();

if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
throw_unsup_format!(
"`pthread_rwlock_tryrdlock` is not supported on {}",
this.tcx.sess.target.os
);
}

let id = rwlock_get_id(this, rwlock_op)?;
let active_thread = this.get_active_thread();

Expand All @@ -548,6 +576,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();

if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
throw_unsup_format!(
"`pthread_rwlock_wrlock` is not supported on {}",
this.tcx.sess.target.os
);
}

let id = rwlock_get_id(this, rwlock_op)?;
let active_thread = this.get_active_thread();

Expand Down Expand Up @@ -578,6 +613,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();

if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
throw_unsup_format!(
"`pthread_rwlock_trywrlock` is not supported on {}",
this.tcx.sess.target.os
);
}

let id = rwlock_get_id(this, rwlock_op)?;
let active_thread = this.get_active_thread();

Expand All @@ -595,6 +637,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();

if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
throw_unsup_format!(
"`pthread_rwlock_unlock` is not supported on {}",
this.tcx.sess.target.os
);
}

let id = rwlock_get_id(this, rwlock_op)?;
let active_thread = this.get_active_thread();

Expand All @@ -614,6 +663,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();

if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
throw_unsup_format!(
"`pthread_rwlock_destroy` is not supported on {}",
this.tcx.sess.target.os
);
}

let id = rwlock_get_id(this, rwlock_op)?;

if this.rwlock_is_locked(id) {
Expand All @@ -638,6 +694,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();

if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
throw_unsup_format!(
"`pthread_condattr_init` is not supported on {}",
this.tcx.sess.target.os
);
}

// The default value of the clock attribute shall refer to the system
// clock.
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_setclock.html
Expand Down Expand Up @@ -704,6 +767,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();

if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
throw_unsup_format!(
"`pthread_cond_init` is not supported on {}",
this.tcx.sess.target.os
);
}

let attr = this.read_pointer(attr_op)?;
let clock_id = if this.ptr_is_null(attr)? {
this.eval_libc_i32("CLOCK_REALTIME")
Expand Down
80 changes: 64 additions & 16 deletions src/tools/miri/src/shims/x86/sse41.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:

round_first::<rustc_apfloat::ieee::Single>(this, left, right, rounding, dest)?;
}
// Used to implement the _mm_floor_ps, _mm_ceil_ps and _mm_round_ps
// functions. Rounds the elements of `op` according to `rounding`.
"round.ps" => {
let [op, rounding] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
}
// Used to implement the _mm_floor_sd, _mm_ceil_sd and _mm_round_sd
// functions. Rounds the first element of `right` according to `rounding`
// and copies the remaining elements from `left`.
Expand All @@ -157,6 +165,14 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:

round_first::<rustc_apfloat::ieee::Double>(this, left, right, rounding, dest)?;
}
// Used to implement the _mm_floor_pd, _mm_ceil_pd and _mm_round_pd
// functions. Rounds the elements of `op` according to `rounding`.
"round.pd" => {
let [op, rounding] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
}
// Used to implement the _mm_minpos_epu16 function.
// Find the minimum unsinged 16-bit integer in `op` and
// returns its value and position.
Expand Down Expand Up @@ -283,22 +299,7 @@ fn round_first<'tcx, F: rustc_apfloat::Float>(
assert_eq!(dest_len, left_len);
assert_eq!(dest_len, right_len);

// The fourth bit of `rounding` only affects the SSE status
// register, which cannot be accessed from Miri (or from Rust,
// for that matter), so we can ignore it.
let rounding = match this.read_scalar(rounding)?.to_i32()? & !0b1000 {
// When the third bit is 0, the rounding mode is determined by the
// first two bits.
0b000 => rustc_apfloat::Round::NearestTiesToEven,
0b001 => rustc_apfloat::Round::TowardNegative,
0b010 => rustc_apfloat::Round::TowardPositive,
0b011 => rustc_apfloat::Round::TowardZero,
// When the third bit is 1, the rounding mode is determined by the
// SSE status register. Since we do not support modifying it from
// Miri (or Rust), we assume it to be at its default mode (round-to-nearest).
0b100..=0b111 => rustc_apfloat::Round::NearestTiesToEven,
rounding => throw_unsup_format!("unsupported rounding mode 0x{rounding:02x}"),
};
let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?;

let op0: F = this.read_scalar(&this.project_index(&right, 0)?)?.to_float()?;
let res = op0.round_to_integral(rounding).value;
Expand All @@ -317,3 +318,50 @@ fn round_first<'tcx, F: rustc_apfloat::Float>(

Ok(())
}

// Rounds all elements of `op` according to `rounding`.
fn round_all<'tcx, F: rustc_apfloat::Float>(
this: &mut crate::MiriInterpCx<'_, 'tcx>,
op: &OpTy<'tcx, Provenance>,
rounding: &OpTy<'tcx, Provenance>,
dest: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, ()> {
let (op, op_len) = this.operand_to_simd(op)?;
let (dest, dest_len) = this.place_to_simd(dest)?;

assert_eq!(dest_len, op_len);

let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?;

for i in 0..dest_len {
let op: F = this.read_scalar(&this.project_index(&op, i)?)?.to_float()?;
let res = op.round_to_integral(rounding).value;
this.write_scalar(
Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
&this.project_index(&dest, i)?,
)?;
}

Ok(())
}

/// Gets equivalent `rustc_apfloat::Round` from rounding mode immediate of
/// `round.{ss,sd,ps,pd}` intrinsics.
fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::Round> {
// The fourth bit of `rounding` only affects the SSE status
// register, which cannot be accessed from Miri (or from Rust,
// for that matter), so we can ignore it.
match rounding & !0b1000 {
// When the third bit is 0, the rounding mode is determined by the
// first two bits.
0b000 => Ok(rustc_apfloat::Round::NearestTiesToEven),
0b001 => Ok(rustc_apfloat::Round::TowardNegative),
0b010 => Ok(rustc_apfloat::Round::TowardPositive),
0b011 => Ok(rustc_apfloat::Round::TowardZero),
// When the third bit is 1, the rounding mode is determined by the
// SSE status register. Since we do not support modifying it from
// Miri (or Rust), we assume it to be at its default mode (round-to-nearest).
0b100..=0b111 => Ok(rustc_apfloat::Round::NearestTiesToEven),
rounding => throw_unsup_format!("unsupported rounding mode 0x{rounding:02x}"),
}
}
1 change: 1 addition & 0 deletions src/tools/miri/tests/fail/data_race/mixed_size_read.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ help: and (1) occurred earlier here
LL | a16.load(Ordering::SeqCst);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: overlapping unsynchronized atomic accesses must use the same access size
= help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model
= 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 (of the first span):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ help: and (1) occurred earlier here
LL | a16.store(1, Ordering::SeqCst);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: overlapping unsynchronized atomic accesses must use the same access size
= help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model
= 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 (of the first span):
Expand Down
1 change: 1 addition & 0 deletions src/tools/miri/tests/fail/data_race/read_read_race1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ help: and (1) occurred earlier here
LL | unsafe { ptr.read() };
| ^^^^^^^^^^
= help: overlapping atomic and non-atomic accesses must be synchronized, even if both are read-only
= help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model
= 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 (of the first span):
Expand Down
Loading

0 comments on commit 0828c15

Please sign in to comment.