Skip to content

Commit d9ca9bd

Browse files
committed
Auto merge of rust-lang#142556 - RalfJung:miri-sync, r=RalfJung
Miri subtree update r? `@ghost`
2 parents 68ac5ab + 5770b90 commit d9ca9bd

File tree

568 files changed

+882
-1013
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

568 files changed

+882
-1013
lines changed

src/tools/miri/rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
c6768de2d63de7a41124a0fb8fc78f9e26111c01
1+
0cbc0764380630780a275c437260e4d4d5f28c92

src/tools/miri/src/concurrency/sync.rs

Lines changed: 105 additions & 85 deletions
Large diffs are not rendered by default.

src/tools/miri/src/concurrency/thread.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ pub enum BlockReason {
9999
/// Blocked on a condition variable.
100100
Condvar(CondvarId),
101101
/// Blocked on a reader-writer lock.
102-
RwLock(RwLockId),
102+
RwLock,
103103
/// Blocked on a Futex variable.
104104
Futex,
105105
/// Blocked on an InitOnce.

src/tools/miri/src/diagnostics.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ impl fmt::Display for TerminationInfo {
8585
DataRace { involves_non_atomic, ptr, op1, op2, .. } =>
8686
write!(
8787
f,
88-
"{} detected between (1) {} on {} and (2) {} on {} at {ptr:?}. (2) just happened here",
88+
"{} detected between (1) {} on {} and (2) {} on {} at {ptr:?}",
8989
if *involves_non_atomic { "Data race" } else { "Race condition" },
9090
op1.action,
9191
op1.thread_info,
@@ -224,7 +224,7 @@ pub fn report_error<'tcx>(
224224
use InterpErrorKind::*;
225225
use UndefinedBehaviorInfo::*;
226226

227-
let mut msg = vec![];
227+
let mut labels = vec![];
228228

229229
let (title, helps) = if let MachineStop(info) = e.kind() {
230230
let info = info.downcast_ref::<TerminationInfo>().expect("invalid MachineStop payload");
@@ -237,7 +237,10 @@ pub fn report_error<'tcx>(
237237
Some("unsupported operation"),
238238
StackedBorrowsUb { .. } | TreeBorrowsUb { .. } | DataRace { .. } =>
239239
Some("Undefined Behavior"),
240-
Deadlock => Some("deadlock"),
240+
Deadlock => {
241+
labels.push(format!("this thread got stuck here"));
242+
None
243+
}
241244
GenmcStuckExecution => {
242245
// This case should only happen in GenMC mode. We treat it like a normal program exit.
243246
assert!(ecx.machine.data_race.as_genmc_ref().is_some());
@@ -259,7 +262,7 @@ pub fn report_error<'tcx>(
259262
]
260263
}
261264
StackedBorrowsUb { help, history, .. } => {
262-
msg.extend(help.clone());
265+
labels.extend(help.clone());
263266
let mut helps = vec![
264267
note!("this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental"),
265268
note!("see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information"),
@@ -297,6 +300,7 @@ pub fn report_error<'tcx>(
297300
Int2PtrWithStrictProvenance =>
298301
vec![note!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead")],
299302
DataRace { op1, extra, retag_explain, .. } => {
303+
labels.push(format!("(2) just happened here"));
300304
let mut helps = vec![note_span!(op1.span, "and (1) occurred earlier here")];
301305
if let Some(extra) = extra {
302306
helps.push(note!("{extra}"));
@@ -426,12 +430,20 @@ pub fn report_error<'tcx>(
426430
_ => {}
427431
}
428432

429-
msg.insert(0, format_interp_error(ecx.tcx.dcx(), e));
433+
let mut primary_msg = String::new();
434+
if let Some(title) = title {
435+
write!(primary_msg, "{title}: ").unwrap();
436+
}
437+
write!(primary_msg, "{}", format_interp_error(ecx.tcx.dcx(), e)).unwrap();
438+
439+
if labels.is_empty() {
440+
labels.push(format!("{} occurred here", title.unwrap_or("error")));
441+
}
430442

431443
report_msg(
432444
DiagLevel::Error,
433-
if let Some(title) = title { format!("{title}: {}", msg[0]) } else { msg[0].clone() },
434-
msg,
445+
primary_msg,
446+
labels,
435447
vec![],
436448
helps,
437449
&stacktrace,
@@ -449,8 +461,8 @@ pub fn report_error<'tcx>(
449461
any_pruned |= was_pruned;
450462
report_msg(
451463
DiagLevel::Error,
452-
format!("deadlock: the evaluated program deadlocked"),
453-
vec![format!("the evaluated program deadlocked")],
464+
format!("the evaluated program deadlocked"),
465+
vec![format!("this thread got stuck here")],
454466
vec![],
455467
vec![],
456468
&stacktrace,
@@ -611,7 +623,7 @@ impl<'tcx> MiriMachine<'tcx> {
611623
let stacktrace = Frame::generate_stacktrace_from_stack(self.threads.active_thread_stack());
612624
let (stacktrace, _was_pruned) = prune_stacktrace(stacktrace, self);
613625

614-
let (title, diag_level) = match &e {
626+
let (label, diag_level) = match &e {
615627
RejectedIsolatedOp(_) =>
616628
("operation rejected by isolation".to_string(), DiagLevel::Warning),
617629
Int2Ptr { .. } => ("integer-to-pointer cast".to_string(), DiagLevel::Warning),
@@ -626,10 +638,10 @@ impl<'tcx> MiriMachine<'tcx> {
626638
| FreedAlloc(..)
627639
| ProgressReport { .. }
628640
| WeakMemoryOutdatedLoad { .. } =>
629-
("tracking was triggered".to_string(), DiagLevel::Note),
641+
("tracking was triggered here".to_string(), DiagLevel::Note),
630642
};
631643

632-
let msg = match &e {
644+
let title = match &e {
633645
CreatedPointerTag(tag, None, _) => format!("created base tag {tag:?}"),
634646
CreatedPointerTag(tag, Some(perm), None) =>
635647
format!("created {tag:?} with {perm} derived from unknown tag"),
@@ -735,7 +747,7 @@ impl<'tcx> MiriMachine<'tcx> {
735747
report_msg(
736748
diag_level,
737749
title,
738-
vec![msg],
750+
vec![label],
739751
notes,
740752
helps,
741753
&stacktrace,

src/tools/miri/src/intrinsics/mod.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -292,11 +292,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
292292
let b = this.read_scalar(b)?.to_f32()?;
293293
let c = this.read_scalar(c)?.to_f32()?;
294294
let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
295-
let res = if fuse {
296-
a.mul_add(b, c).value
297-
} else {
298-
((a * b).value + c).value
299-
};
295+
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
300296
let res = this.adjust_nan(res, &[a, b, c]);
301297
this.write_scalar(res, dest)?;
302298
}
@@ -306,11 +302,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
306302
let b = this.read_scalar(b)?.to_f64()?;
307303
let c = this.read_scalar(c)?.to_f64()?;
308304
let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
309-
let res = if fuse {
310-
a.mul_add(b, c).value
311-
} else {
312-
((a * b).value + c).value
313-
};
305+
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
314306
let res = this.adjust_nan(res, &[a, b, c]);
315307
this.write_scalar(res, dest)?;
316308
}

src/tools/miri/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ pub use crate::concurrency::data_race::{
124124
};
125125
pub use crate::concurrency::init_once::{EvalContextExt as _, InitOnceId};
126126
pub use crate::concurrency::sync::{
127-
CondvarId, EvalContextExt as _, MutexRef, RwLockId, SynchronizationObjects,
127+
CondvarId, EvalContextExt as _, MutexRef, RwLockRef, SynchronizationObjects,
128128
};
129129
pub use crate::concurrency::thread::{
130130
BlockReason, DynUnblockCallback, EvalContextExt as _, StackEmptyCallback, ThreadId,

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

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -289,15 +289,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
289289
};
290290
let mutex_ref = mutex_ref.clone();
291291

292-
if this.mutex_is_locked(&mutex_ref) {
293-
if this.mutex_get_owner(&mutex_ref) == this.active_thread() {
292+
if let Some(owner) = mutex_ref.owner() {
293+
if owner == this.active_thread() {
294294
// Matching the current macOS implementation: abort on reentrant locking.
295295
throw_machine_stop!(TerminationInfo::Abort(
296296
"attempted to lock an os_unfair_lock that is already locked by the current thread".to_owned()
297297
));
298298
}
299299

300-
this.mutex_enqueue_and_block(&mutex_ref, None);
300+
this.mutex_enqueue_and_block(mutex_ref, None);
301301
} else {
302302
this.mutex_lock(&mutex_ref);
303303
}
@@ -319,7 +319,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
319319
};
320320
let mutex_ref = mutex_ref.clone();
321321

322-
if this.mutex_is_locked(&mutex_ref) {
322+
if mutex_ref.owner().is_some() {
323323
// Contrary to the blocking lock function, this does not check for
324324
// reentrancy.
325325
this.write_scalar(Scalar::from_bool(false), dest)?;
@@ -350,9 +350,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
350350
));
351351
}
352352

353-
// If the lock is not locked by anyone now, it went quer.
353+
// If the lock is not locked by anyone now, it went quiet.
354354
// Reset to zero so that it can be moved and initialized again for the next phase.
355-
if !this.mutex_is_locked(&mutex_ref) {
355+
if mutex_ref.owner().is_none() {
356356
let lock_place = this.deref_pointer_as(lock_op, this.machine.layouts.u32)?;
357357
this.write_scalar_atomic(Scalar::from_u32(0), &lock_place, AtomicWriteOrd::Relaxed)?;
358358
}
@@ -371,9 +371,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
371371
};
372372
let mutex_ref = mutex_ref.clone();
373373

374-
if !this.mutex_is_locked(&mutex_ref)
375-
|| this.mutex_get_owner(&mutex_ref) != this.active_thread()
376-
{
374+
if mutex_ref.owner().is_none_or(|o| o != this.active_thread()) {
377375
throw_machine_stop!(TerminationInfo::Abort(
378376
"called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread".to_owned()
379377
));
@@ -393,17 +391,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
393391
};
394392
let mutex_ref = mutex_ref.clone();
395393

396-
if this.mutex_is_locked(&mutex_ref)
397-
&& this.mutex_get_owner(&mutex_ref) == this.active_thread()
398-
{
394+
if mutex_ref.owner().is_some_and(|o| o == this.active_thread()) {
399395
throw_machine_stop!(TerminationInfo::Abort(
400396
"called os_unfair_lock_assert_not_owner on an os_unfair_lock owned by the current thread".to_owned()
401397
));
402398
}
403399

404-
// If the lock is not locked by anyone now, it went quer.
400+
// If the lock is not locked by anyone now, it went quiet.
405401
// Reset to zero so that it can be moved and initialized again for the next phase.
406-
if !this.mutex_is_locked(&mutex_ref) {
402+
if mutex_ref.owner().is_none() {
407403
let lock_place = this.deref_pointer_as(lock_op, this.machine.layouts.u32)?;
408404
this.write_scalar_atomic(Scalar::from_u32(0), &lock_place, AtomicWriteOrd::Relaxed)?;
409405
}

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

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -229,9 +229,9 @@ fn mutex_kind_from_static_initializer<'tcx>(
229229
// We store some data directly inside the type, ignoring the platform layout:
230230
// - init: u32
231231

232-
#[derive(Debug, Copy, Clone)]
232+
#[derive(Debug, Clone)]
233233
struct PthreadRwLock {
234-
id: RwLockId,
234+
rwlock_ref: RwLockRef,
235235
}
236236

237237
fn rwlock_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> {
@@ -278,8 +278,8 @@ where
278278
)? {
279279
throw_unsup_format!("unsupported static initializer used for `pthread_rwlock_t`");
280280
}
281-
let id = ecx.machine.sync.rwlock_create();
282-
interp_ok(PthreadRwLock { id })
281+
let rwlock_ref = ecx.machine.sync.rwlock_create();
282+
interp_ok(PthreadRwLock { rwlock_ref })
283283
},
284284
)
285285
}
@@ -504,11 +504,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
504504

505505
let mutex = mutex_get_data(this, mutex_op)?.clone();
506506

507-
let ret = if this.mutex_is_locked(&mutex.mutex_ref) {
508-
let owner_thread = this.mutex_get_owner(&mutex.mutex_ref);
507+
let ret = if let Some(owner_thread) = mutex.mutex_ref.owner() {
509508
if owner_thread != this.active_thread() {
510509
this.mutex_enqueue_and_block(
511-
&mutex.mutex_ref,
510+
mutex.mutex_ref,
512511
Some((Scalar::from_i32(0), dest.clone())),
513512
);
514513
return interp_ok(());
@@ -541,8 +540,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
541540

542541
let mutex = mutex_get_data(this, mutex_op)?.clone();
543542

544-
interp_ok(Scalar::from_i32(if this.mutex_is_locked(&mutex.mutex_ref) {
545-
let owner_thread = this.mutex_get_owner(&mutex.mutex_ref);
543+
interp_ok(Scalar::from_i32(if let Some(owner_thread) = mutex.mutex_ref.owner() {
546544
if owner_thread != this.active_thread() {
547545
this.eval_libc_i32("EBUSY")
548546
} else {
@@ -596,7 +594,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
596594
// since we make the field uninit below.
597595
let mutex = mutex_get_data(this, mutex_op)?.clone();
598596

599-
if this.mutex_is_locked(&mutex.mutex_ref) {
597+
if mutex.mutex_ref.owner().is_some() {
600598
throw_ub_format!("destroyed a locked mutex");
601599
}
602600

@@ -616,12 +614,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
616614
) -> InterpResult<'tcx> {
617615
let this = self.eval_context_mut();
618616

619-
let id = rwlock_get_data(this, rwlock_op)?.id;
617+
let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
620618

621-
if this.rwlock_is_write_locked(id) {
622-
this.rwlock_enqueue_and_block_reader(id, Scalar::from_i32(0), dest.clone());
619+
if rwlock.rwlock_ref.is_write_locked() {
620+
this.rwlock_enqueue_and_block_reader(
621+
rwlock.rwlock_ref,
622+
Scalar::from_i32(0),
623+
dest.clone(),
624+
);
623625
} else {
624-
this.rwlock_reader_lock(id);
626+
this.rwlock_reader_lock(&rwlock.rwlock_ref);
625627
this.write_null(dest)?;
626628
}
627629

@@ -631,12 +633,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
631633
fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
632634
let this = self.eval_context_mut();
633635

634-
let id = rwlock_get_data(this, rwlock_op)?.id;
636+
let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
635637

636-
if this.rwlock_is_write_locked(id) {
638+
if rwlock.rwlock_ref.is_write_locked() {
637639
interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
638640
} else {
639-
this.rwlock_reader_lock(id);
641+
this.rwlock_reader_lock(&rwlock.rwlock_ref);
640642
interp_ok(Scalar::from_i32(0))
641643
}
642644
}
@@ -648,9 +650,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
648650
) -> InterpResult<'tcx> {
649651
let this = self.eval_context_mut();
650652

651-
let id = rwlock_get_data(this, rwlock_op)?.id;
653+
let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
652654

653-
if this.rwlock_is_locked(id) {
655+
if rwlock.rwlock_ref.is_locked() {
654656
// Note: this will deadlock if the lock is already locked by this
655657
// thread in any way.
656658
//
@@ -663,9 +665,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
663665
// report the deadlock only when no thread can continue execution,
664666
// but we could detect that this lock is already locked and report
665667
// an error.)
666-
this.rwlock_enqueue_and_block_writer(id, Scalar::from_i32(0), dest.clone());
668+
this.rwlock_enqueue_and_block_writer(
669+
rwlock.rwlock_ref,
670+
Scalar::from_i32(0),
671+
dest.clone(),
672+
);
667673
} else {
668-
this.rwlock_writer_lock(id);
674+
this.rwlock_writer_lock(&rwlock.rwlock_ref);
669675
this.write_null(dest)?;
670676
}
671677

@@ -675,22 +681,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
675681
fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
676682
let this = self.eval_context_mut();
677683

678-
let id = rwlock_get_data(this, rwlock_op)?.id;
684+
let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
679685

680-
if this.rwlock_is_locked(id) {
686+
if rwlock.rwlock_ref.is_locked() {
681687
interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
682688
} else {
683-
this.rwlock_writer_lock(id);
689+
this.rwlock_writer_lock(&rwlock.rwlock_ref);
684690
interp_ok(Scalar::from_i32(0))
685691
}
686692
}
687693

688694
fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
689695
let this = self.eval_context_mut();
690696

691-
let id = rwlock_get_data(this, rwlock_op)?.id;
697+
let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
692698

693-
if this.rwlock_reader_unlock(id)? || this.rwlock_writer_unlock(id)? {
699+
if this.rwlock_reader_unlock(&rwlock.rwlock_ref)?
700+
|| this.rwlock_writer_unlock(&rwlock.rwlock_ref)?
701+
{
694702
interp_ok(())
695703
} else {
696704
throw_ub_format!("unlocked an rwlock that was not locked by the active thread");
@@ -702,9 +710,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
702710

703711
// Reading the field also has the side-effect that we detect double-`destroy`
704712
// since we make the field uninit below.
705-
let id = rwlock_get_data(this, rwlock_op)?.id;
713+
let rwlock = rwlock_get_data(this, rwlock_op)?.clone();
706714

707-
if this.rwlock_is_locked(id) {
715+
if rwlock.rwlock_ref.is_locked() {
708716
throw_ub_format!("destroyed a locked rwlock");
709717
}
710718

0 commit comments

Comments
 (0)