Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
fe9d6fe
test that even ZST references prevent deallocation via child pointers
RalfJung Jun 3, 2026
e5c59b3
libc: Minimal support for `mprotect` and `madvise`
foonathan May 25, 2026
6a53473
add minimal NetBSD support
joboet Jun 18, 2026
9f27f6d
[Priroda] Add structured locals output
moabo3li Jun 17, 2026
e418127
[Priroda] Add type strings to locals output
moabo3li Jun 19, 2026
cc45b69
Prepare for merging from rust-lang/rust
Jun 22, 2026
e1e5e25
Merge ref '942ac9ce4116' from rust-lang/rust
Jun 22, 2026
9969ff0
Merge pull request #5119 from moabo3li/restructured_locals
oli-obk Jun 22, 2026
4b7eb68
Merge pull request #5123 from rust-lang/rustup-2026-06-22
RalfJung Jun 22, 2026
8d2cf93
Update `cargo miri --help` to point to README.md.
eswartz Jun 18, 2026
aa5792a
reformat pointers to README
RalfJung Jun 23, 2026
88fa4bf
Merge pull request #5113 from eswartz/expose-readme-md-info
RalfJung Jun 23, 2026
90231c7
Merge pull request #5114 from joboet/min-netbsd
RalfJung Jun 23, 2026
0cf893e
dont ICE on generic no_mangle items
RalfJung Jun 24, 2026
dc5b1a9
test-cargo-miri: test all workspace members
RalfJung Jun 24, 2026
01a12a4
add ui test for no mangle generic
HerrCai0907 Mar 29, 2026
1545b13
Merge pull request #5129 from RalfJung/no-mangle-generic
RalfJung Jun 24, 2026
135a0f7
Merge pull request #5063 from foonathan/master
RalfJung Jun 24, 2026
eca4b5b
move nextest section up and mark it more clearly as such
RalfJung Jun 25, 2026
d6d568b
Merge pull request #5131 from RalfJung/nextest
RalfJung Jun 25, 2026
2337590
add some more errno_result
RalfJung Jun 25, 2026
d2c6980
rename read_split_slice → read_partial
RalfJung Jun 25, 2026
5445de0
remove dangling comment
RalfJung Jun 25, 2026
b0d28dc
Merge pull request #5132 from RalfJung/errno-result
RalfJung Jun 25, 2026
ab88669
replace cfg_if with cfg_select
hkBst Jun 25, 2026
37ffa9a
run some more tests natively
RalfJung Jun 25, 2026
528bd23
update lockfile
RalfJung Jun 25, 2026
dfb71b7
Merge pull request #5134 from hkBst/cfg-select
RalfJung Jun 25, 2026
4a2d36a
Prepare for merging from rust-lang/rust
Jun 26, 2026
9beed1d
Merge ref '40557f6225e3' from rust-lang/rust
Jun 26, 2026
fb9fa4e
Merge pull request #5136 from rust-lang/rustup-2026-06-26
RalfJung Jun 26, 2026
f9a88c6
Prepare for merging from rust-lang/rust
Jun 27, 2026
b3c27e5
Merge ref '16761606d606' from rust-lang/rust
Jun 27, 2026
972dd17
Merge pull request #5139 from rust-lang/rustup-2026-06-27
oli-obk Jun 27, 2026
2e08805
fix: keep readable readiness when socket read end is closed
WhySoBad Jun 13, 2026
80f8d1c
avoid Windows quirk, and make write test consistent with read test
RalfJung Jun 27, 2026
f67abfb
Merge pull request #5106 from WhySoBad/network-sockets-stay-readable-…
RalfJung Jun 27, 2026
29643cf
Merge pull request #5088 from RalfJung/dealloc-protected
RalfJung Jun 27, 2026
290b370
Merge pull request #5133 from RalfJung/native
RalfJung Jun 27, 2026
aac0746
run x86 intrinsic tests natively
RalfJung Jun 27, 2026
a672666
run aarch64 intrinsic tests natively
RalfJung Jun 27, 2026
21dfe67
fix bug in `mpsadbw`
folkertdev Jun 27, 2026
f734a15
Merge pull request #5140 from RalfJung/arch-intrinsic-native
RalfJung Jun 27, 2026
075f549
add `vqdmulh*` aarch64 intrinsics
folkertdev Jun 22, 2026
8e4a0e1
Merge pull request #5125 from folkertdev/vqdmulh
RalfJung Jun 27, 2026
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
53 changes: 33 additions & 20 deletions src/tools/miri/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,38 @@ available (which affects `cfg(target_feature)`), and it tells Miri to consider t
that the interpreted program runs on as having the feature available (meaning the code is allowed to
invoke the corresponding intrinsics).

### Nextest integration

Miri can be combined with [`cargo-nextest`](https://nexte.st):

```
cargo install --locked cargo-nextest
cargo miri nextest run
```

Nextest spawns a separate instance of Miri for each test, which has several advantages:
- Tests can run in parallel. Miri itself only uses a single thread per interpreter so this can
give a massive speedup (but see the caveat below).
- Tests do not stop when a single problem is found. Miri aborts execution when it encounters
Undefined Behavior or an unsupported operation (there is often not really any way to continue),
so once a single test fails, the remaining tests cannot be executed. Nextest's process-per-test
model means that you end up with a full list of which tests worked in Miri and which tests had a
problem.

However, there is also a big caveat: Miri will [re-compile the test crate every time it is
invoked](https://github.com/rust-lang/miri/issues/5013), which means a crate with N tests will be
compiled N+1 times. If the test crate takes a long time to build, this can outweigh the benefits of
parallelization.

For more information about nextest, see the [`cargo-nextest` Miri
documentation](https://nexte.st/book/miri.html).

Note: Nextest's one-test-per-process model means that `cargo miri test` is able to detect data
races where two tests race on a shared resource, but `cargo miri nextest run` will not detect
such races.

Note: `cargo-nextest` [does not support doctests](https://github.com/nextest-rs/nextest/issues/16).

### Testing multiple different executions

Certain parts of the execution are picked randomly by Miri, such as the exact base address
Expand All @@ -184,6 +216,7 @@ MIRIFLAGS="-Zmiri-many-seeds" cargo miri test # tries the seeds in 0..64
MIRIFLAGS="-Zmiri-many-seeds=0..16" cargo miri test
```

Miri will test the given range of seeds with parallel interpreter instances.
The default of 64 different seeds can be quite slow, so you often want to specify a smaller range.

### Running Miri on CI
Expand Down Expand Up @@ -243,26 +276,6 @@ However, even for targets that we do support, the degree of support for accessin
(such as the file system) differs between targets: generally, Linux targets have the best support,
and macOS targets are usually on par. Windows is supported less well.

### Running tests in parallel

Though it implements Rust threading, Miri itself is a single-threaded interpreter
(it works like a multi-threaded OS on a single-core CPU).
This means that when running `cargo miri test`, you will probably see a dramatic
increase in the amount of time it takes to run your whole test suite due to the
inherent interpreter slowdown and a loss of parallelism.

You can get your test suite's parallelism back by running `cargo miri nextest run -jN`
(note that you will need [`cargo-nextest`](https://nexte.st) installed).
This works because `cargo-nextest` collects a list of all tests then launches a
separate `cargo miri run` for each test. For more information about nextest, see the
[`cargo-nextest` Miri documentation](https://nexte.st/book/miri.html).

Note: This one-test-per-process model means that `cargo miri test` is able to detect data
races where two tests race on a shared resource, but `cargo miri nextest run` will not detect
such races.

Note: `cargo-nextest` does not support doctests, see https://github.com/nextest-rs/nextest/issues/16

### Directly invoking the `miri` driver

The recommended way to invoke Miri is via `cargo miri`. Directly invoking the underlying `miri`
Expand Down
3 changes: 3 additions & 0 deletions src/tools/miri/cargo-miri/src/phases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ Examples:
This will print the path to the generated sysroot (and nothing else) on stdout.
stderr will still contain progress information about how the build is doing.

For documentation on `-Zmiri-...` flags, see Miri's README.md, available at:
- $(rustc --print sysroot)/share/doc/miri/README.md
- https://github.com/rust-lang/miri/blob/master/README.md
";

fn show_help() {
Expand Down
1 change: 1 addition & 0 deletions src/tools/miri/ci/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ case $HOST_TARGET in
MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests
MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests
MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-illumos run_tests
MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-netbsd run_tests_minimal hello
;;
armv7-unknown-linux-gnueabihf)
# Host
Expand Down
73 changes: 62 additions & 11 deletions src/tools/miri/priroda/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ extern crate rustc_data_structures;
extern crate rustc_driver;
extern crate rustc_hir;
extern crate rustc_hir_analysis;
extern crate rustc_index;
extern crate rustc_interface;
extern crate rustc_log;
extern crate rustc_middle;
Expand All @@ -19,13 +20,15 @@ use std::path::PathBuf;
use miri::*;
use rustc_driver::Compilation;
use rustc_hir::attrs::CrateType;
use rustc_index::IndexVec;
use rustc_interface::interface;
use rustc_middle::mir;
use rustc_middle::mir::{Local, VarDebugInfoContents};
use rustc_middle::ty::TyCtxt;
use rustc_session::EarlyDiagCtxt;
use rustc_session::config::ErrorOutputType;
use rustc_span::Span;
use rustc_span::source_map::SourceMap;
use rustc_span::{Span, Symbol};

fn find_sysroot() -> String {
std::env::var("MIRI_SYSROOT")
Expand Down Expand Up @@ -129,6 +132,11 @@ struct PrirodaContext<'tcx> {
last_location: Option<SourceLocation>,
}

struct LocalDesc {
name: Option<Symbol>,
local: Local,
ty: String,
}
/// Controls when execution returns to the frontend.
enum ResumeMode {
/// Stop at the next visible MIR instruction.
Expand Down Expand Up @@ -336,15 +344,49 @@ impl<'tcx> PrirodaContext<'tcx> {
}
}

/// Returns the names of all user-visible locals in the innermost stack frame.
/// Returns structured descriptions for locals in the innermost stack frame.
///
/// Uses `var_debug_info` from the MIR body, which is the same source that
/// DWARF debug info is built from, so the names match what the user wrote.
fn list_locals(&self) -> Vec<String> {
/// Starts from all MIR locals, then enriches them with source names from
/// `var_debug_info` when a debug entry maps directly to a whole local.
fn list_locals(&self) -> Vec<LocalDesc> {
let Some(frame) = self.ecx.active_thread_stack().last() else {
return Vec::new();
};
frame.body().var_debug_info.iter().map(|info| info.name.to_string()).collect()

self.local_desc_map(frame).into_iter().collect()
}

fn local_desc_map(
&self,
frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
) -> IndexVec<Local, LocalDesc> {
// Initialize one description per MIR local so the table can be indexed by Local.
let mut locals: IndexVec<Local, LocalDesc> = frame
.body()
.local_decls
.iter_enumerated()
.map(|(id, local_decl)| {
LocalDesc { name: None, local: id, ty: local_decl.ty.to_string() }
})
.collect();

// FIXME: Some debug-info entries do not have a backing MIR local, for example
// because the source variable was optimized out or is represented as a
// projection. This local-indexed table cannot represent those entries yet;
// the final locals list should become a `Vec<LocalDesc>` with `id : Option<Local>`, `id`
// could be renamed to `local`.

// Attach source names from debug info when the debug entry maps directly to a whole MIR local.
for var_debug_info in &frame.body().var_debug_info {
if let VarDebugInfoContents::Place(place) = var_debug_info.value
&& let Some(local) = place.as_local()
&& locals[local].name.is_none()
{
locals[local].name = Some(var_debug_info.name);
}
}

locals
}
}

Expand All @@ -366,7 +408,7 @@ enum BreakpointSetResult {
enum CommandResult {
ExecutionStopped(StepResult),
BreakpointResult(BreakpointSetResult),
Locals(Vec<String>),
Locals(Vec<LocalDesc>),
// FIXME: distinguish terminating the debugger session from disconnecting a
// frontend and terminating the interpreted program once multiple frontends exist.
TerminateSession,
Expand Down Expand Up @@ -403,12 +445,21 @@ impl Cli {

BreakpointSetResult::Duplicate => println!("Duplicate breakpoint"),
},
CommandResult::Locals(names) =>
if names.is_empty() {
CommandResult::Locals(locals_desc) =>
if locals_desc.is_empty() {
println!("no locals");
} else {
for name in &names {
println!("{name}");
for local_desc in &locals_desc {
let mut name_str = "None".to_string();
if let Some(name) = local_desc.name {
name_str = name.to_string();
}
println!(
"Name: {}, Id: _{}, Ty: {}",
name_str,
local_desc.local.index(),
local_desc.ty,
);
}
},
CommandResult::TerminateSession => {
Expand Down
8 changes: 6 additions & 2 deletions src/tools/miri/priroda/tests/ui/locals_in_function.stdout
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
(priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/locals_in_function.rs:5
(priroda) Hit breakpoint
{MANIFEST_DIR}/tests/ui/locals_in_function.rs:5
(priroda) x
y
(priroda) Name: None, Id: _0, Ty: ()
Name: x, Id: _1, Ty: i32
Name: y, Id: _2, Ty: bool
Name: None, Id: _3, Ty: (i32, bool)
Name: None, Id: _4, Ty: i32
Name: None, Id: _5, Ty: bool
(priroda) quitting
2 changes: 1 addition & 1 deletion src/tools/miri/rust-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
01f54e80e888b66d6486a3a95d481b87353016df
16761606d606b6ec4d0c88fc9251670742ad9fd2
4 changes: 3 additions & 1 deletion src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,9 @@ impl<'tcx> Tree {
// Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`).
// Related to https://github.com/rust-lang/rust/issues/55005.
&& !perm.permission.is_cell()
// Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579.
// Only trigger UB if the accessed bit is set, i.e. if the protector
// is actually protecting this offset. See #4579. Note that this
// takes into account the access we just did above!
&& perm.accessed
{
Err(TbError {
Expand Down
8 changes: 7 additions & 1 deletion src/tools/miri/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,13 @@ pub fn iter_exported_symbols<'tcx>(
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
};
if exported {
// FIXME: `#[no_mangle]` makes no sense on a generic item, but still causes it to be
// considered "extern". Remove this once `no_mangle_generic_items` is a hard error.
let exported_mono = exported && {
let generics = tcx.generics_of(def_id);
!generics.requires_monomorphization(tcx)
};
if exported_mono {
f(LOCAL_CRATE, def_id.into())?;
}
}
Expand Down
39 changes: 39 additions & 0 deletions src/tools/miri/src/shims/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,45 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}

// Signed saturating doubling multiply returning the high half.
//
// Used by the `vqdmulh*` functions.
//
// This LLVM intrinsic multiplies the values of corresponding elements of the two source
// vector registers (which are signed integers), doubles the results, places the most significant half of the
// final results (using a saturating cast to fit the element type) into a vector, and writes the vector to the destination register.
//
// https://developer.arm.com/architectures/instruction-sets/intrinsics#f:@navigationhierarchiessimdisa=[Neon]&q=vqdmulh
name if name.starts_with("neon.sqdmulh.") => {
let [left, right] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;

let (left, left_len) = this.project_to_simd(left)?;
let (right, right_len) = this.project_to_simd(right)?;
let (dest, dest_len) = this.project_to_simd(dest)?;
assert_eq!(left_len, right_len);
assert_eq!(left_len, dest_len);

let elem_size = dest.layout.field(this, 0).size;
let bits = elem_size.bits();
let min = elem_size.signed_int_min();
let max = elem_size.signed_int_max();

for i in 0..dest_len {
let a = this.read_scalar(&this.project_index(&left, i)?)?.to_int(elem_size)?;
let b = this.read_scalar(&this.project_index(&right, i)?)?.to_int(elem_size)?;

// Uses i128 arithmetic, which cannot overflow because the intrinsic takes at most i32.
let doubled = a.strict_mul(b).strict_mul(2);
let res = (doubled >> bits).clamp(min, max);

this.write_scalar(
Scalar::from_int(res, elem_size),
&this.project_index(&dest, i)?,
)?;
}
}

// Vector table lookup: each index selects a byte from the 16-byte table, out-of-range -> 0.
// Used to implement vtbl1_u8 function.
// LLVM does not have a portable shuffle that takes non-const indices
Expand Down
38 changes: 33 additions & 5 deletions src/tools/miri/src/shims/unix/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -840,18 +840,46 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}

"mmap" => {
let [addr, length, prot, flags, fd, offset] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let [addr, length, prot, flags, fd, offset] = this.check_shim_sig(
shim_sig!(extern "C" fn(*mut _, usize, i32, i32, i32, libc::off_t) -> *mut _),
link_name,
abi,
args,
)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
let ptr = this.mmap(addr, length, prot, flags, fd, offset)?;
this.write_scalar(ptr, dest)?;
}
"munmap" => {
let [addr, length] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let [addr, length] = this.check_shim_sig(
shim_sig!(extern "C" fn(*mut _, usize) -> i32),
link_name,
abi,
args,
)?;
let result = this.munmap(addr, length)?;
this.write_scalar(result, dest)?;
}
"mprotect" => {
let [addr, length, prot] = this.check_shim_sig(
shim_sig!(extern "C" fn(*mut _, usize, i32) -> i32),
link_name,
abi,
args,
)?;
let result = this.mprotect(addr, length, prot)?;
this.write_scalar(result, dest)?;
}
"madvise" => {
let [addr, length, advice] = this.check_shim_sig(
shim_sig!(extern "C" fn(*mut _, usize, i32) -> i32),
link_name,
abi,
args,
)?;
let result = this.madvise(addr, length, advice)?;
this.write_scalar(result, dest)?;
}

"reallocarray" => {
// Currently this function does not exist on all Unixes, e.g. on macOS.
Expand Down Expand Up @@ -1410,7 +1438,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let [_, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.write_null(dest)?;
}
"sigaction" | "mprotect" if this.frame_in_std() => {
"sigaction" if this.frame_in_std() => {
let [_, _, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
this.write_null(dest)?;
}
Expand Down
Loading
Loading