Skip to content

Commit 0828c15

Browse files
committed
Auto merge of #117855 - RalfJung:miri, r=RalfJung
Miri subtree update r? `@ghost`
2 parents 2b603f9 + f86fa09 commit 0828c15

32 files changed

+648
-120
lines changed

Diff for: src/tools/miri/ci.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ case $HOST_TARGET in
108108
MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests
109109
MIRI_TEST_TARGET=aarch64-apple-darwin run_tests
110110
MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests
111-
MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple atomic env/var
111+
MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthreads atomic env/var
112112
MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal hello integer vec panic/panic
113113
MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal no_std integer strings wasm
114114
MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std integer strings wasm

Diff for: src/tools/miri/src/concurrency/thread.rs

+25-7
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ enum SchedulingAction {
3333
Sleep(Duration),
3434
}
3535

36+
/// What to do with TLS allocations from terminated threads
37+
pub enum TlsAllocAction {
38+
/// Deallocate backing memory of thread-local statics as usual
39+
Deallocate,
40+
/// Skip deallocating backing memory of thread-local statics and consider all memory reachable
41+
/// from them as "allowed to leak" (like global `static`s).
42+
Leak,
43+
}
44+
3645
/// Trait for callbacks that can be executed when some event happens, such as after a timeout.
3746
pub trait MachineCallback<'mir, 'tcx>: VisitTags {
3847
fn call(&self, ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>) -> InterpResult<'tcx>;
@@ -1051,7 +1060,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
10511060
// See if this thread can do something else.
10521061
match this.run_on_stack_empty()? {
10531062
Poll::Pending => {} // keep going
1054-
Poll::Ready(()) => this.terminate_active_thread()?,
1063+
Poll::Ready(()) =>
1064+
this.terminate_active_thread(TlsAllocAction::Deallocate)?,
10551065
}
10561066
}
10571067
}
@@ -1066,21 +1076,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
10661076
}
10671077

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

10791089
let current_span = this.machine.current_span();
1080-
for ptr in
1081-
this.machine.threads.thread_terminated(this.machine.data_race.as_mut(), current_span)
1082-
{
1083-
this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?;
1090+
let thread_local_allocations =
1091+
this.machine.threads.thread_terminated(this.machine.data_race.as_mut(), current_span);
1092+
for ptr in thread_local_allocations {
1093+
match tls_alloc_action {
1094+
TlsAllocAction::Deallocate =>
1095+
this.deallocate_ptr(ptr.into(), None, MiriMemoryKind::Tls.into())?,
1096+
TlsAllocAction::Leak =>
1097+
if let Some(alloc) = ptr.provenance.get_alloc_id() {
1098+
trace!("Thread-local static leaked and stored as static root: {:?}", alloc);
1099+
this.machine.static_roots.push(alloc);
1100+
},
1101+
}
10841102
}
10851103
Ok(())
10861104
}

Diff for: src/tools/miri/src/diagnostics.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ pub fn report_error<'tcx, 'mir>(
270270
DataRace { op1, extra, .. } => {
271271
let mut helps = vec![(Some(op1.span), format!("and (1) occurred earlier here"))];
272272
if let Some(extra) = extra {
273-
helps.push((None, format!("{extra}")))
273+
helps.push((None, format!("{extra}")));
274+
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")));
274275
}
275276
helps.push((None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior")));
276277
helps.push((None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information")));

Diff for: src/tools/miri/src/eval.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use log::info;
1111
use rustc_middle::ty::Ty;
1212

1313
use crate::borrow_tracker::RetagFields;
14+
use crate::concurrency::thread::TlsAllocAction;
1415
use crate::diagnostics::report_leaks;
1516
use rustc_data_structures::fx::FxHashSet;
1617
use rustc_hir::def::Namespace;
@@ -244,9 +245,9 @@ impl MainThreadState {
244245
// Figure out exit code.
245246
let ret_place = this.machine.main_fn_ret_place.clone().unwrap();
246247
let exit_code = this.read_target_isize(&ret_place)?;
247-
// Need to call this ourselves since we are not going to return to the scheduler
248-
// loop, and we want the main thread TLS to not show up as memory leaks.
249-
this.terminate_active_thread()?;
248+
// Deal with our thread-local memory. We do *not* want to actually free it, instead we consider TLS
249+
// to be like a global `static`, so that all memory reached by it is considered to "not leak".
250+
this.terminate_active_thread(TlsAllocAction::Leak)?;
250251
// Stop interpreter loop.
251252
throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true });
252253
}

Diff for: src/tools/miri/src/shims/unix/freebsd/foreign_items.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
2929
"pthread_set_name_np" => {
3030
let [thread, name] =
3131
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
32-
let max_len = usize::MAX; // freebsd does not seem to have a limit.
33-
let res = this.pthread_setname_np(
32+
let max_len = usize::MAX; // FreeBSD does not seem to have a limit.
33+
// FreeBSD's pthread_set_name_np does not return anything.
34+
this.pthread_setname_np(
3435
this.read_scalar(thread)?,
3536
this.read_scalar(name)?,
3637
max_len,
3738
)?;
38-
this.write_scalar(res, dest)?;
39+
}
40+
"pthread_get_name_np" => {
41+
let [thread, name, len] =
42+
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
43+
// FreeBSD's pthread_get_name_np does not return anything.
44+
this.pthread_getname_np(
45+
this.read_scalar(thread)?,
46+
this.read_scalar(name)?,
47+
this.read_scalar(len)?,
48+
)?;
3949
}
4050

4151
// errno

Diff for: src/tools/miri/src/shims/unix/sync.rs

+70
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
277277
) -> InterpResult<'tcx, i32> {
278278
let this = self.eval_context_mut();
279279

280+
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
281+
throw_unsup_format!(
282+
"`pthread_mutexattr_init` is not supported on {}",
283+
this.tcx.sess.target.os
284+
);
285+
}
286+
280287
let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT");
281288
mutexattr_set_kind(this, attr_op, default_kind)?;
282289

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

369+
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
370+
throw_unsup_format!(
371+
"`pthread_mutex_init` is not supported on {}",
372+
this.tcx.sess.target.os
373+
);
374+
}
375+
362376
let attr = this.read_pointer(attr_op)?;
363377
let kind = if this.ptr_is_null(attr)? {
364378
this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
@@ -513,6 +527,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
513527
) -> InterpResult<'tcx, i32> {
514528
let this = self.eval_context_mut();
515529

530+
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
531+
throw_unsup_format!(
532+
"`pthread_rwlock_rdlock` is not supported on {}",
533+
this.tcx.sess.target.os
534+
);
535+
}
536+
516537
let id = rwlock_get_id(this, rwlock_op)?;
517538
let active_thread = this.get_active_thread();
518539

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

555+
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
556+
throw_unsup_format!(
557+
"`pthread_rwlock_tryrdlock` is not supported on {}",
558+
this.tcx.sess.target.os
559+
);
560+
}
561+
534562
let id = rwlock_get_id(this, rwlock_op)?;
535563
let active_thread = this.get_active_thread();
536564

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

579+
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
580+
throw_unsup_format!(
581+
"`pthread_rwlock_wrlock` is not supported on {}",
582+
this.tcx.sess.target.os
583+
);
584+
}
585+
551586
let id = rwlock_get_id(this, rwlock_op)?;
552587
let active_thread = this.get_active_thread();
553588

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

616+
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
617+
throw_unsup_format!(
618+
"`pthread_rwlock_trywrlock` is not supported on {}",
619+
this.tcx.sess.target.os
620+
);
621+
}
622+
581623
let id = rwlock_get_id(this, rwlock_op)?;
582624
let active_thread = this.get_active_thread();
583625

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

640+
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
641+
throw_unsup_format!(
642+
"`pthread_rwlock_unlock` is not supported on {}",
643+
this.tcx.sess.target.os
644+
);
645+
}
646+
598647
let id = rwlock_get_id(this, rwlock_op)?;
599648
let active_thread = this.get_active_thread();
600649

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

666+
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
667+
throw_unsup_format!(
668+
"`pthread_rwlock_destroy` is not supported on {}",
669+
this.tcx.sess.target.os
670+
);
671+
}
672+
617673
let id = rwlock_get_id(this, rwlock_op)?;
618674

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

697+
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
698+
throw_unsup_format!(
699+
"`pthread_condattr_init` is not supported on {}",
700+
this.tcx.sess.target.os
701+
);
702+
}
703+
641704
// The default value of the clock attribute shall refer to the system
642705
// clock.
643706
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_setclock.html
@@ -704,6 +767,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
704767
) -> InterpResult<'tcx, i32> {
705768
let this = self.eval_context_mut();
706769

770+
if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") {
771+
throw_unsup_format!(
772+
"`pthread_cond_init` is not supported on {}",
773+
this.tcx.sess.target.os
774+
);
775+
}
776+
707777
let attr = this.read_pointer(attr_op)?;
708778
let clock_id = if this.ptr_is_null(attr)? {
709779
this.eval_libc_i32("CLOCK_REALTIME")

Diff for: src/tools/miri/src/shims/x86/sse41.rs

+64-16
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,14 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
148148

149149
round_first::<rustc_apfloat::ieee::Single>(this, left, right, rounding, dest)?;
150150
}
151+
// Used to implement the _mm_floor_ps, _mm_ceil_ps and _mm_round_ps
152+
// functions. Rounds the elements of `op` according to `rounding`.
153+
"round.ps" => {
154+
let [op, rounding] =
155+
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
156+
157+
round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
158+
}
151159
// Used to implement the _mm_floor_sd, _mm_ceil_sd and _mm_round_sd
152160
// functions. Rounds the first element of `right` according to `rounding`
153161
// and copies the remaining elements from `left`.
@@ -157,6 +165,14 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
157165

158166
round_first::<rustc_apfloat::ieee::Double>(this, left, right, rounding, dest)?;
159167
}
168+
// Used to implement the _mm_floor_pd, _mm_ceil_pd and _mm_round_pd
169+
// functions. Rounds the elements of `op` according to `rounding`.
170+
"round.pd" => {
171+
let [op, rounding] =
172+
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
173+
174+
round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
175+
}
160176
// Used to implement the _mm_minpos_epu16 function.
161177
// Find the minimum unsinged 16-bit integer in `op` and
162178
// returns its value and position.
@@ -283,22 +299,7 @@ fn round_first<'tcx, F: rustc_apfloat::Float>(
283299
assert_eq!(dest_len, left_len);
284300
assert_eq!(dest_len, right_len);
285301

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

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

318319
Ok(())
319320
}
321+
322+
// Rounds all elements of `op` according to `rounding`.
323+
fn round_all<'tcx, F: rustc_apfloat::Float>(
324+
this: &mut crate::MiriInterpCx<'_, 'tcx>,
325+
op: &OpTy<'tcx, Provenance>,
326+
rounding: &OpTy<'tcx, Provenance>,
327+
dest: &PlaceTy<'tcx, Provenance>,
328+
) -> InterpResult<'tcx, ()> {
329+
let (op, op_len) = this.operand_to_simd(op)?;
330+
let (dest, dest_len) = this.place_to_simd(dest)?;
331+
332+
assert_eq!(dest_len, op_len);
333+
334+
let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?;
335+
336+
for i in 0..dest_len {
337+
let op: F = this.read_scalar(&this.project_index(&op, i)?)?.to_float()?;
338+
let res = op.round_to_integral(rounding).value;
339+
this.write_scalar(
340+
Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
341+
&this.project_index(&dest, i)?,
342+
)?;
343+
}
344+
345+
Ok(())
346+
}
347+
348+
/// Gets equivalent `rustc_apfloat::Round` from rounding mode immediate of
349+
/// `round.{ss,sd,ps,pd}` intrinsics.
350+
fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::Round> {
351+
// The fourth bit of `rounding` only affects the SSE status
352+
// register, which cannot be accessed from Miri (or from Rust,
353+
// for that matter), so we can ignore it.
354+
match rounding & !0b1000 {
355+
// When the third bit is 0, the rounding mode is determined by the
356+
// first two bits.
357+
0b000 => Ok(rustc_apfloat::Round::NearestTiesToEven),
358+
0b001 => Ok(rustc_apfloat::Round::TowardNegative),
359+
0b010 => Ok(rustc_apfloat::Round::TowardPositive),
360+
0b011 => Ok(rustc_apfloat::Round::TowardZero),
361+
// When the third bit is 1, the rounding mode is determined by the
362+
// SSE status register. Since we do not support modifying it from
363+
// Miri (or Rust), we assume it to be at its default mode (round-to-nearest).
364+
0b100..=0b111 => Ok(rustc_apfloat::Round::NearestTiesToEven),
365+
rounding => throw_unsup_format!("unsupported rounding mode 0x{rounding:02x}"),
366+
}
367+
}

Diff for: src/tools/miri/tests/fail/data_race/mixed_size_read.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ help: and (1) occurred earlier here
1010
LL | a16.load(Ordering::SeqCst);
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
1212
= help: overlapping unsynchronized atomic accesses must use the same access size
13+
= 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
1314
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
1415
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
1516
= note: BACKTRACE (of the first span):

Diff for: src/tools/miri/tests/fail/data_race/mixed_size_write.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ help: and (1) occurred earlier here
1010
LL | a16.store(1, Ordering::SeqCst);
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212
= help: overlapping unsynchronized atomic accesses must use the same access size
13+
= 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
1314
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
1415
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
1516
= note: BACKTRACE (of the first span):

Diff for: src/tools/miri/tests/fail/data_race/read_read_race1.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ help: and (1) occurred earlier here
1010
LL | unsafe { ptr.read() };
1111
| ^^^^^^^^^^
1212
= help: overlapping atomic and non-atomic accesses must be synchronized, even if both are read-only
13+
= 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
1314
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
1415
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
1516
= note: BACKTRACE (of the first span):

0 commit comments

Comments
 (0)