Skip to content

Commit ebc6b6a

Browse files
authored
Rollup merge of #148374 - RalfJung:miri, r=RalfJung
miri subtree update Subtree update of `miri` to rust-lang/miri@52c5daf. Created using https://github.com/rust-lang/josh-sync. r? `@ghost`
2 parents 4e76fb0 + 63a18d4 commit ebc6b6a

File tree

489 files changed

+320
-1380
lines changed

Some content is hidden

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

489 files changed

+320
-1380
lines changed

src/tools/miri/.github/workflows/ci.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ jobs:
3131
os: ubuntu-24.04-arm
3232
multiarch: armhf
3333
gcc_cross: arm-linux-gnueabihf
34-
# Disabled due to Ubuntu repo trouble
34+
# Ubuntu mirrors are not reliable enough for these architectures
35+
# (see <https://bugs.launchpad.net/ubuntu/+bug/2130309>).
3536
# - host_target: riscv64gc-unknown-linux-gnu
3637
# os: ubuntu-latest
3738
# multiarch: riscv64
@@ -68,7 +69,7 @@ jobs:
6869
- name: install multiarch
6970
if: ${{ matrix.multiarch != '' }}
7071
run: |
71-
# s390x, ppc64el, riscv64 need Ubuntu Ports to be in the mirror list
72+
# armhf, s390x, ppc64el, riscv64 need Ubuntu Ports to be in the mirror list
7273
sudo bash -c "echo 'https://ports.ubuntu.com/ priority:4' >> /etc/apt/apt-mirrors.txt"
7374
# Add architecture
7475
sudo dpkg --add-architecture ${{ matrix.multiarch }}

src/tools/miri/README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,9 @@ Try running `cargo miri clean`.
292292
Miri adds its own set of `-Z` flags, which are usually set via the `MIRIFLAGS`
293293
environment variable. We first document the most relevant and most commonly used flags:
294294

295+
* `-Zmiri-backtrace=<0|1|full>` configures how Miri prints backtraces: `1` is the default,
296+
where backtraces are printed in pruned form; `full` prints backtraces without pruning, and `0`
297+
disables backtraces entirely.
295298
* `-Zmiri-deterministic-concurrency` makes Miri's concurrency-related behavior fully deterministic.
296299
Strictly speaking, Miri is always fully deterministic when isolation is enabled (the default
297300
mode), but this determinism is achieved by using an RNG with a fixed seed. Seemingly harmless
@@ -373,6 +376,12 @@ environment variable. We first document the most relevant and most commonly used
373376
ensure alignment. (The standard library `align_to` method works fine in both modes; under
374377
symbolic alignment it only fills the middle slice when the allocation guarantees sufficient
375378
alignment.)
379+
* `-Zmiri-user-relevant-crates=<crate>,<crate>,...` extends the list of crates that Miri considers
380+
"user-relevant". This affects the rendering of backtraces (for user-relevant crates, Miri shows
381+
not just the function name but the actual code) and it affects the spans collected for data races
382+
and aliasing violations (where Miri will show the span of the topmost non-`#[track_caller]` frame
383+
in a user-relevant crate). When using `cargo miri`, the crates in the local workspace are always
384+
considered user-relevant.
376385

377386
The remaining flags are for advanced use only, and more likely to change or be removed.
378387
Some of these are **unsound**, which means they can lead
@@ -474,7 +483,8 @@ to Miri failing to detect cases of undefined behavior in a program.
474483
* `-Zmiri-track-alloc-id=<id1>,<id2>,...` shows a backtrace when the given allocations are
475484
being allocated or freed. This helps in debugging memory leaks and
476485
use after free bugs. Specifying this argument multiple times does not overwrite the previous
477-
values, instead it appends its values to the list. Listing an id multiple times has no effect.
486+
values, instead it appends its values to the list. Listing an ID multiple times has no effect.
487+
You can also add IDs at runtime using `miri_track_alloc`.
478488
* `-Zmiri-track-pointer-tag=<tag1>,<tag2>,...` shows a backtrace when a given pointer tag
479489
is created and when (if ever) it is popped from a borrow stack (which is where the tag becomes invalid
480490
and any future use of it will error). This helps you in finding out why UB is

src/tools/miri/cargo-miri/src/util.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,11 @@ fn cargo_extra_flags() -> Vec<String> {
213213

214214
pub fn get_cargo_metadata() -> Metadata {
215215
// This will honor the `CARGO` env var the same way our `cargo()` does.
216-
MetadataCommand::new().no_deps().other_options(cargo_extra_flags()).exec().unwrap()
216+
MetadataCommand::new()
217+
.no_deps()
218+
.other_options(cargo_extra_flags())
219+
.exec()
220+
.unwrap_or_else(|err| show_error!("{}", err))
217221
}
218222

219223
/// Pulls all the crates in this workspace from the cargo metadata.

src/tools/miri/rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
28d0a4a205f9e511ad2f51ee79a4aa19a704a455
1+
292be5c7c05138d753bbd4b30db7a3f1a5c914f7

src/tools/miri/src/bin/miri.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,8 @@ fn main() {
710710
fatal_error!("-Zmiri-force-page-size requires a power of 2: {page_size}");
711711
};
712712
miri_config.page_size = Some(page_size);
713+
} else if let Some(param) = arg.strip_prefix("-Zmiri-user-relevant-crates=") {
714+
miri_config.user_relevant_crates.extend(param.split(',').map(|s| s.to_owned()));
713715
} else {
714716
// Forward to rustc.
715717
rustc_args.push(arg);

src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -675,16 +675,22 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
675675
if let Ok((alloc_id, base_offset, orig_tag)) = this.ptr_try_get_alloc_id(place.ptr(), 0)
676676
{
677677
log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;
678-
// Still give it the new provenance, it got retagged after all.
678+
// Still give it the new provenance, it got retagged after all. If this was a
679+
// wildcard pointer, this will fix the AllocId and make future accesses with this
680+
// reference to other allocations UB, but that's fine: due to subobject provenance,
681+
// *all* future accesses with this reference should be UB!
679682
return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
680683
} else {
681684
// This pointer doesn't come with an AllocId. :shrug:
682685
log_creation(this, None)?;
683-
// Provenance unchanged.
686+
// Provenance unchanged. Ideally we'd make this pointer UB to use like above,
687+
// but there's no easy way to do that.
684688
return interp_ok(place.ptr().provenance);
685689
}
686690
}
687691

692+
// The pointer *must* have a valid AllocId to continue, so we want to resolve this to
693+
// a concrete ID even for wildcard pointers.
688694
let (alloc_id, base_offset, orig_tag) = this.ptr_get_alloc_id(place.ptr(), 0)?;
689695
log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;
690696

@@ -743,7 +749,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
743749
// Make sure the data race model also knows about this.
744750
// FIXME(genmc): Ensure this is still done in GenMC mode. Check for other places where GenMC may need to be informed.
745751
if let Some(data_race) = alloc_extra.data_race.as_vclocks_mut() {
746-
data_race.write(
752+
data_race.write_non_atomic(
747753
alloc_id,
748754
range,
749755
NaWriteType::Retag,
@@ -792,7 +798,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
792798
assert_eq!(access, AccessKind::Read);
793799
// Make sure the data race model also knows about this.
794800
if let Some(data_race) = alloc_extra.data_race.as_vclocks_ref() {
795-
data_race.read(
801+
data_race.read_non_atomic(
796802
alloc_id,
797803
range,
798804
NaReadType::Retag,

src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
366366
// Also inform the data race model (but only if any bytes are actually affected).
367367
if range_in_alloc.size.bytes() > 0 {
368368
if let Some(data_race) = alloc_extra.data_race.as_vclocks_ref() {
369-
data_race.read(
369+
data_race.read_non_atomic(
370370
alloc_id,
371371
range_in_alloc,
372372
NaReadType::Retag,

src/tools/miri/src/clock.rs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,7 @@ impl Instant {
4646
InstantKind::Virtual { nanoseconds: earlier },
4747
) => {
4848
let duration = nanoseconds.saturating_sub(earlier);
49-
cfg_select! {
50-
bootstrap => {
51-
// `Duration` does not provide a nice constructor from a `u128` of nanoseconds,
52-
// so we have to implement this ourselves.
53-
// It is possible for second to overflow because u64::MAX < (u128::MAX / 1e9).
54-
// It will be saturated to u64::MAX seconds if the value after division exceeds u64::MAX.
55-
let seconds = u64::try_from(duration / 1_000_000_000).unwrap_or(u64::MAX);
56-
// It is impossible for nanosecond to overflow because u32::MAX > 1e9.
57-
let nanosecond = u32::try_from(duration.wrapping_rem(1_000_000_000)).unwrap();
58-
Duration::new(seconds, nanosecond)
59-
}
60-
_ => {
61-
Duration::from_nanos_u128(duration)
62-
}
63-
}
49+
Duration::from_nanos_u128(duration)
6450
}
6551
_ => panic!("all `Instant` must be of the same kind"),
6652
}

src/tools/miri/src/concurrency/data_race.rs

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -648,18 +648,17 @@ impl MemoryCellClocks {
648648
thread_clocks.clock.index_mut(index).span = current_span;
649649
}
650650
thread_clocks.clock.index_mut(index).set_read_type(read_type);
651-
if self.write_was_before(&thread_clocks.clock) {
652-
// We must be ordered-after all atomic writes.
653-
let race_free = if let Some(atomic) = self.atomic() {
654-
atomic.write_vector <= thread_clocks.clock
655-
} else {
656-
true
657-
};
658-
self.read.set_at_index(&thread_clocks.clock, index);
659-
if race_free { Ok(()) } else { Err(DataRace) }
660-
} else {
661-
Err(DataRace)
651+
// Check synchronization with non-atomic writes.
652+
if !self.write_was_before(&thread_clocks.clock) {
653+
return Err(DataRace);
662654
}
655+
// Check synchronization with atomic writes.
656+
if !self.atomic().is_none_or(|atomic| atomic.write_vector <= thread_clocks.clock) {
657+
return Err(DataRace);
658+
}
659+
// Record this access.
660+
self.read.set_at_index(&thread_clocks.clock, index);
661+
Ok(())
663662
}
664663

665664
/// Detect races for non-atomic write operations at the current memory cell
@@ -675,24 +674,23 @@ impl MemoryCellClocks {
675674
if !current_span.is_dummy() {
676675
thread_clocks.clock.index_mut(index).span = current_span;
677676
}
678-
if self.write_was_before(&thread_clocks.clock) && self.read <= thread_clocks.clock {
679-
let race_free = if let Some(atomic) = self.atomic() {
680-
atomic.write_vector <= thread_clocks.clock
681-
&& atomic.read_vector <= thread_clocks.clock
682-
} else {
683-
true
684-
};
685-
self.write = (index, thread_clocks.clock[index]);
686-
self.write_type = write_type;
687-
if race_free {
688-
self.read.set_zero_vector();
689-
Ok(())
690-
} else {
691-
Err(DataRace)
692-
}
693-
} else {
694-
Err(DataRace)
677+
// Check synchronization with non-atomic accesses.
678+
if !(self.write_was_before(&thread_clocks.clock) && self.read <= thread_clocks.clock) {
679+
return Err(DataRace);
695680
}
681+
// Check synchronization with atomic accesses.
682+
if !self.atomic().is_none_or(|atomic| {
683+
atomic.write_vector <= thread_clocks.clock && atomic.read_vector <= thread_clocks.clock
684+
}) {
685+
return Err(DataRace);
686+
}
687+
// Record this access.
688+
self.write = (index, thread_clocks.clock[index]);
689+
self.write_type = write_type;
690+
self.read.set_zero_vector();
691+
// This is not an atomic location any more.
692+
self.atomic_ops = None;
693+
Ok(())
696694
}
697695
}
698696

@@ -758,14 +756,9 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
758756
let this = self.eval_context_mut();
759757
this.atomic_access_check(dest, AtomicAccessType::Store)?;
760758

761-
// Read the previous value so we can put it in the store buffer later.
762-
// The program didn't actually do a read, so suppress the memory access hooks.
763-
// This is also a very special exception where we just ignore an error -- if this read
764-
// was UB e.g. because the memory is uninitialized, we don't want to know!
765-
let old_val = this.run_for_validation_ref(|this| this.read_scalar(dest)).discard_err();
766-
767759
// Inform GenMC about the atomic store.
768760
if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
761+
let old_val = this.run_for_validation_ref(|this| this.read_scalar(dest)).discard_err();
769762
if genmc_ctx.atomic_store(
770763
this,
771764
dest.ptr().addr(),
@@ -780,6 +773,9 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
780773
}
781774
return interp_ok(());
782775
}
776+
777+
// Read the previous value so we can put it in the store buffer later.
778+
let old_val = this.get_latest_nonatomic_val(dest);
783779
this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?;
784780
this.validate_atomic_store(dest, atomic)?;
785781
this.buffered_atomic_write(val, dest, atomic, old_val)
@@ -1201,7 +1197,7 @@ impl VClockAlloc {
12011197
/// operation for which data-race detection is handled separately, for example
12021198
/// atomic read operations. The `ty` parameter is used for diagnostics, letting
12031199
/// the user know which type was read.
1204-
pub fn read<'tcx>(
1200+
pub fn read_non_atomic<'tcx>(
12051201
&self,
12061202
alloc_id: AllocId,
12071203
access_range: AllocRange,
@@ -1243,7 +1239,7 @@ impl VClockAlloc {
12431239
/// being created or if it is temporarily disabled during a racy read or write
12441240
/// operation. The `ty` parameter is used for diagnostics, letting
12451241
/// the user know which type was written.
1246-
pub fn write<'tcx>(
1242+
pub fn write_non_atomic<'tcx>(
12471243
&mut self,
12481244
alloc_id: AllocId,
12491245
access_range: AllocRange,
@@ -1540,6 +1536,35 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
15401536
)
15411537
}
15421538

1539+
/// Returns the most recent *non-atomic* value stored in the given place.
1540+
/// Errors if we don't need that (because we don't do store buffering) or if
1541+
/// the most recent value is in fact atomic.
1542+
fn get_latest_nonatomic_val(&self, place: &MPlaceTy<'tcx>) -> Result<Option<Scalar>, ()> {
1543+
let this = self.eval_context_ref();
1544+
// These cannot fail because `atomic_access_check` was done first.
1545+
let (alloc_id, offset, _prov) = this.ptr_get_alloc_id(place.ptr(), 0).unwrap();
1546+
let alloc_meta = &this.get_alloc_extra(alloc_id).unwrap().data_race;
1547+
if alloc_meta.as_weak_memory_ref().is_none() {
1548+
// No reason to read old value if we don't track store buffers.
1549+
return Err(());
1550+
}
1551+
let data_race = alloc_meta.as_vclocks_ref().unwrap();
1552+
// Only read old value if this is currently a non-atomic location.
1553+
for (_range, clocks) in data_race.alloc_ranges.borrow_mut().iter(offset, place.layout.size)
1554+
{
1555+
// If this had an atomic write that's not before the non-atomic write, that should
1556+
// already be in the store buffer. Initializing the store buffer now would use the
1557+
// wrong `sync_clock` so we better make sure that does not happen.
1558+
if clocks.atomic().is_some_and(|atomic| !(atomic.write_vector <= clocks.write())) {
1559+
return Err(());
1560+
}
1561+
}
1562+
// The program didn't actually do a read, so suppress the memory access hooks.
1563+
// This is also a very special exception where we just ignore an error -- if this read
1564+
// was UB e.g. because the memory is uninitialized, we don't want to know!
1565+
Ok(this.run_for_validation_ref(|this| this.read_scalar(place)).discard_err())
1566+
}
1567+
15431568
/// Generic atomic operation implementation
15441569
fn validate_atomic_op<A: Debug + Copy>(
15451570
&self,

src/tools/miri/src/concurrency/weak_memory.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,11 @@ impl StoreBufferAlloc {
177177
Self { store_buffers: RefCell::new(RangeObjectMap::new()) }
178178
}
179179

180-
/// When a non-atomic access happens on a location that has been atomically accessed
181-
/// before without data race, we can determine that the non-atomic access fully happens
180+
/// When a non-atomic write happens on a location that has been atomically accessed
181+
/// before without data race, we can determine that the non-atomic write fully happens
182182
/// after all the prior atomic writes so the location no longer needs to exhibit
183-
/// any weak memory behaviours until further atomic accesses.
184-
pub fn memory_accessed(&self, range: AllocRange, global: &DataRaceState) {
183+
/// any weak memory behaviours until further atomic writes.
184+
pub fn non_atomic_write(&self, range: AllocRange, global: &DataRaceState) {
185185
if !global.ongoing_action_data_race_free() {
186186
let mut buffers = self.store_buffers.borrow_mut();
187187
let access_type = buffers.access_type(range);
@@ -223,18 +223,23 @@ impl StoreBufferAlloc {
223223
fn get_or_create_store_buffer_mut<'tcx>(
224224
&mut self,
225225
range: AllocRange,
226-
init: Option<Scalar>,
226+
init: Result<Option<Scalar>, ()>,
227227
) -> InterpResult<'tcx, &mut StoreBuffer> {
228228
let buffers = self.store_buffers.get_mut();
229229
let access_type = buffers.access_type(range);
230230
let pos = match access_type {
231231
AccessType::PerfectlyOverlapping(pos) => pos,
232232
AccessType::Empty(pos) => {
233+
let init =
234+
init.expect("cannot have empty store buffer when previous write was atomic");
233235
buffers.insert_at_pos(pos, range, StoreBuffer::new(init));
234236
pos
235237
}
236238
AccessType::ImperfectlyOverlapping(pos_range) => {
237239
// Once we reach here we would've already checked that this access is not racy.
240+
let init = init.expect(
241+
"cannot have partially overlapping store buffer when previous write was atomic",
242+
);
238243
buffers.remove_pos_range(pos_range.clone());
239244
buffers.insert_at_pos(pos_range.start, range, StoreBuffer::new(init));
240245
pos_range.start
@@ -490,7 +495,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
490495
}
491496
let range = alloc_range(base_offset, place.layout.size);
492497
let sync_clock = data_race_clocks.sync_clock(range);
493-
let buffer = alloc_buffers.get_or_create_store_buffer_mut(range, Some(init))?;
498+
let buffer = alloc_buffers.get_or_create_store_buffer_mut(range, Ok(Some(init)))?;
494499
// The RMW always reads from the most recent store.
495500
buffer.read_from_last_store(global, threads, atomic == AtomicRwOrd::SeqCst);
496501
buffer.buffered_write(
@@ -556,15 +561,16 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
556561
/// Add the given write to the store buffer. (Does not change machine memory.)
557562
///
558563
/// `init` says with which value to initialize the store buffer in case there wasn't a store
559-
/// buffer for this memory range before.
564+
/// buffer for this memory range before. `Err(())` means the value is not available;
565+
/// `Ok(None)` means the memory does not contain a valid scalar.
560566
///
561567
/// Must be called *after* `validate_atomic_store` to ensure that `sync_clock` is up-to-date.
562568
fn buffered_atomic_write(
563569
&mut self,
564570
val: Scalar,
565571
dest: &MPlaceTy<'tcx>,
566572
atomic: AtomicWriteOrd,
567-
init: Option<Scalar>,
573+
init: Result<Option<Scalar>, ()>,
568574
) -> InterpResult<'tcx> {
569575
let this = self.eval_context_mut();
570576
let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(dest.ptr(), 0)?;

0 commit comments

Comments
 (0)