Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ba6b40e
Prepare for merging from rust-lang/rust
Dec 14, 2025
705ec58
Merge ref '2cd4ee6bcf51' from rust-lang/rust
Dec 14, 2025
bed1817
Merge pull request #4763 from rust-lang/rustup-2025-12-14
oli-obk Dec 14, 2025
f7a8188
Prepare for merging from rust-lang/rust
Dec 15, 2025
426c72a
Merge ref '0208ee09be46' from rust-lang/rust
Dec 15, 2025
6d58cbd
Merge pull request #4765 from rust-lang/rustup-2025-12-15
oli-obk Dec 15, 2025
fc16679
add avx512 pshufb
usamoi Dec 14, 2025
75290e1
Merge pull request #4764 from usamoi/pshufb
RalfJung Dec 16, 2025
15069c2
Implement SetFileInformationByHandle, as well as direct shim test for it
CraftSpider Jun 2, 2025
2be0b30
Merge pull request #4547 from CraftSpider/windows-set-len
RalfJung Dec 17, 2025
c92d39e
Prepare for merging from rust-lang/rust
Dec 18, 2025
6abb813
Merge ref '686f9cefc37c' from rust-lang/rust
Dec 18, 2025
b48defa
Implement FlushFileBuffers
CraftSpider Dec 12, 2025
b63e6fb
Implement direct shim test
CraftSpider Dec 12, 2025
71c7e32
Merge pull request #4774 from rust-lang/rustup-2025-12-18
RalfJung Dec 18, 2025
3fd2d64
remove spin_loop hint intrinsics we no longer need
RalfJung Dec 18, 2025
018c2fe
Merge pull request #4775 from RalfJung/spin-loop-hint
RalfJung Dec 18, 2025
7f8cc15
Merge pull request #4761 from CraftSpider/windows-sync
RalfJung Dec 18, 2025
ef494fe
Prepare for merging from rust-lang/rust
Dec 19, 2025
077987a
Merge ref '526a91cbcc46' from rust-lang/rust
Dec 19, 2025
8870bb8
fmt
Dec 19, 2025
a316ed2
Merge pull request #4777 from rust-lang/rustup-2025-12-19
RalfJung Dec 19, 2025
69c43e8
Prepare for merging from rust-lang/rust
RalfJung Dec 20, 2025
4e73253
Merge ref 'f51d1bcdc69f' from rust-lang/rust
RalfJung Dec 20, 2025
d11ac79
normalize file name changes away
RalfJung Dec 20, 2025
a0d1ff6
Merge pull request #4780 from RalfJung/rustup
RalfJung Dec 20, 2025
ae6e209
Prepare for merging from rust-lang/rust
Dec 21, 2025
346e02a
Merge ref 'cb79c42008b9' from rust-lang/rust
Dec 21, 2025
bd051c4
re-bless genmc test
RalfJung Dec 21, 2025
1c9d5e9
Merge pull request #4782 from rust-lang/rustup-2025-12-21
RalfJung Dec 21, 2025
26ef77a
tree borrows: detect no_valid_exposed_references errors accurately
royAmmerschuber Dec 2, 2025
37e6101
Merge pull request #4757 from royAmmerschuber/feature/multi_exposed_main
RalfJung Dec 22, 2025
9ae46aa
readme: update android state
RalfJung Dec 22, 2025
9d52e4b
Merge pull request #4783 from RalfJung/android
RalfJung Dec 22, 2025
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
2 changes: 1 addition & 1 deletion src/tools/miri/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ degree documented below):
- We have unofficial support (not maintained by the Miri team itself) for some further operating systems.
- `solaris` / `illumos`: maintained by @devnexen. Supports the entire test suite.
- `freebsd`: maintained by @YohDeadfall and @LorrensP-2158466. Supports the entire test suite.
- `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works.
- `android`: **maintainer wanted**. Basic OS APIs and concurrency work, but file system access is not supported.
- For targets on other operating systems, Miri might fail before even reaching the `main` function.

However, even for targets that we do support, the degree of support for accessing platform APIs
Expand Down
2 changes: 1 addition & 1 deletion src/tools/miri/rust-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
dc47a69ed94bc88b10b7d500cceacf29b87bcbbe
cb79c42008b970269f6a06b257e5f04b93f24d03
12 changes: 6 additions & 6 deletions src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,9 @@ impl Permission {
self.inner.strongest_idempotent_foreign_access(prot)
}

/// Returns the strongest access allowed from a child to this node without
/// Returns the strongest access allowed that is local to this node without
/// causing UB (only considers possible transitions to this permission).
pub fn strongest_allowed_child_access(&self, protected: bool) -> WildcardAccessLevel {
pub fn strongest_allowed_local_access(&self, protected: bool) -> WildcardAccessLevel {
match self.inner {
// Everything except disabled can be accessed by read access.
Disabled => WildcardAccessLevel::None,
Expand Down Expand Up @@ -794,9 +794,9 @@ mod propagation_optimization_checks {
/// Checks that `strongest_allowed_child_access` correctly
/// represents which transitions are possible.
#[test]
fn strongest_allowed_child_access() {
fn strongest_allowed_local_access() {
for (permission, protected) in <(Permission, bool)>::exhaustive() {
let strongest_child_access = permission.strongest_allowed_child_access(protected);
let strongest_local_access = permission.strongest_allowed_local_access(protected);

let is_read_valid = Permission::perform_access(
AccessKind::Read,
Expand All @@ -814,8 +814,8 @@ mod propagation_optimization_checks {
)
.is_some();

assert_eq!(is_read_valid, strongest_child_access >= WildcardAccessLevel::Read);
assert_eq!(is_write_valid, strongest_child_access >= WildcardAccessLevel::Write);
assert_eq!(is_read_valid, strongest_local_access >= WildcardAccessLevel::Read);
assert_eq!(is_write_valid, strongest_local_access >= WildcardAccessLevel::Write);
}
}
}
33 changes: 30 additions & 3 deletions src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl LocationState {
// We need to update the wildcard state, if the permission
// of an exposed pointer changes.
if node.is_exposed {
let access_type = self.permission.strongest_allowed_child_access(protected);
let access_type = self.permission.strongest_allowed_local_access(protected);
WildcardState::update_exposure(idx, access_type, nodes, wildcard_accesses);
}
}
Expand Down Expand Up @@ -1034,6 +1034,9 @@ impl<'tcx> LocationTree {
wildcard_state.access_relatedness(access_kind, only_foreign)
};

// Whether there is an exposed node in this tree that allows this access.
let mut has_valid_exposed = false;

// This does a traversal across the tree updating children before their parents. The
// difference to `perform_normal_access` is that we take the access relatedness from
// the wildcard tracking state of the node instead of from the visitor itself.
Expand Down Expand Up @@ -1082,6 +1085,17 @@ impl<'tcx> LocationTree {
return Err(no_valid_exposed_references_error(diagnostics));
};

let mut entry = args.data.perms.entry(args.idx);
let perm = entry.or_insert(node.default_location_state());

// We only count exposed nodes through which an access could happen.
if node.is_exposed
&& perm.permission.strongest_allowed_local_access(protected).allows(access_kind)
&& max_local_tag.is_none_or(|max_local_tag| max_local_tag >= node.tag)
{
has_valid_exposed = true;
}

let Some(relatedness) = wildcard_relatedness.to_relatedness() else {
// If the access type is Either, then we do not apply any transition
// to this node, but we still update each of its children.
Expand All @@ -1090,8 +1104,6 @@ impl<'tcx> LocationTree {
return Ok(());
};

let mut entry = args.data.perms.entry(args.idx);
let perm = entry.or_insert(node.default_location_state());
// We know the exact relatedness, so we can actually do precise checks.
perm.perform_transition(
args.idx,
Expand All @@ -1115,6 +1127,21 @@ impl<'tcx> LocationTree {
})
},
)?;
// If there is no exposed node in this tree that allows this access, then the
// access *must* be foreign. So we check if the root of this tree would allow this
// as a foreign access, and if not, then we can error.
// In practice, all wildcard trees accept foreign accesses, but the main tree does
// not, so this catches UB when none of the nodes in the main tree allows this access.
if !has_valid_exposed
&& self
.wildcard_accesses
.get(root)
.unwrap()
.access_relatedness(access_kind, /* only_foreign */ true)
.is_none()
{
return Err(no_valid_exposed_references_error(diagnostics)).into();
}
interp_ok(())
}
}
Expand Down
14 changes: 12 additions & 2 deletions src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ pub enum WildcardAccessLevel {
Read,
Write,
}
impl WildcardAccessLevel {
/// Weather this access kind is allowed at this level.
pub fn allows(self, kind: AccessKind) -> bool {
let required_level = match kind {
AccessKind::Read => Self::Read,
AccessKind::Write => Self::Write,
};
required_level <= self
}
}

/// Where the access happened relative to the current node.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -430,7 +440,7 @@ impl Tree {
.map(|p| p.permission())
.unwrap_or_else(|| node.default_location_state().permission());

let access_type = perm.strongest_allowed_child_access(protected);
let access_type = perm.strongest_allowed_local_access(protected);
WildcardState::update_exposure(
id,
access_type,
Expand Down Expand Up @@ -480,7 +490,7 @@ impl Tree {
perms.get(id).copied().unwrap_or_else(|| node.default_location_state());

perm.permission()
.strongest_allowed_child_access(protected_tags.contains_key(&node.tag))
.strongest_allowed_local_access(protected_tags.contains_key(&node.tag))
} else {
WildcardAccessLevel::None
};
Expand Down
14 changes: 0 additions & 14 deletions src/tools/miri/src/shims/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,6 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Prefix should have already been checked.
let unprefixed_name = link_name.as_str().strip_prefix("llvm.aarch64.").unwrap();
match unprefixed_name {
"isb" => {
let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let arg = this.read_scalar(arg)?.to_i32()?;
match arg {
// SY ("full system scope")
15 => {
this.yield_active_thread();
}
_ => {
throw_unsup_format!("unsupported llvm.aarch64.isb argument {}", arg);
}
}
}

// Used to implement the vpmaxq_u8 function.
// Computes the maximum of adjacent pairs; the first half of the output is produced from the
// `left` input, the second half of the output from the `right` input.
Expand Down
16 changes: 0 additions & 16 deletions src/tools/miri/src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -813,22 +813,6 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
this, link_name, abi, args, dest,
);
}
// FIXME: Move this to an `arm` submodule.
"llvm.arm.hint" if this.tcx.sess.target.arch == Arch::Arm => {
let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let arg = this.read_scalar(arg)?.to_i32()?;
// Note that different arguments might have different target feature requirements.
match arg {
// YIELD
1 => {
this.expect_target_feature_for_intrinsic(link_name, "v6")?;
this.yield_active_thread();
}
_ => {
throw_unsup_format!("unsupported llvm.arm.hint argument {}", arg);
}
}
}

// Fallback to shims in submodules.
_ => {
Expand Down
11 changes: 11 additions & 0 deletions src/tools/miri/src/shims/windows/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let res = this.GetFileInformationByHandle(handle, info)?;
this.write_scalar(res, dest)?;
}
"SetFileInformationByHandle" => {
let [handle, class, info, size] =
this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let res = this.SetFileInformationByHandle(handle, class, info, size)?;
this.write_scalar(res, dest)?;
}
"FlushFileBuffers" => {
let [handle] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let res = this.FlushFileBuffers(handle)?;
this.write_scalar(res, dest)?;
}
"DeleteFileW" => {
let [file_name] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?;
let res = this.DeleteFileW(file_name)?;
Expand Down
118 changes: 118 additions & 0 deletions src/tools/miri/src/shims/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::path::PathBuf;
use std::time::SystemTime;

use bitflags::bitflags;
use rustc_abi::Size;
use rustc_target::spec::Os;

use crate::shims::files::{FdId, FileDescription, FileHandle};
Expand Down Expand Up @@ -372,6 +373,123 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
interp_ok(this.eval_windows("c", "TRUE"))
}

fn SetFileInformationByHandle(
&mut self,
file: &OpTy<'tcx>, // HANDLE
class: &OpTy<'tcx>, // FILE_INFO_BY_HANDLE_CLASS
file_information: &OpTy<'tcx>, // LPVOID
buffer_size: &OpTy<'tcx>, // DWORD
) -> InterpResult<'tcx, Scalar> {
// ^ Returns BOOL (i32 on Windows)
let this = self.eval_context_mut();
this.assert_target_os(Os::Windows, "SetFileInformationByHandle");
this.check_no_isolation("`SetFileInformationByHandle`")?;

let class = this.read_scalar(class)?.to_u32()?;
let buffer_size = this.read_scalar(buffer_size)?.to_u32()?;
let file_information = this.read_pointer(file_information)?;
this.check_ptr_access(
file_information,
Size::from_bytes(buffer_size),
CheckInAllocMsg::MemoryAccess,
)?;

let file = this.read_handle(file, "SetFileInformationByHandle")?;
let Handle::File(fd_num) = file else { this.invalid_handle("SetFileInformationByHandle")? };
let Some(desc) = this.machine.fds.get(fd_num) else {
this.invalid_handle("SetFileInformationByHandle")?
};
let file = desc.downcast::<FileHandle>().ok_or_else(|| {
err_unsup_format!(
"`SetFileInformationByHandle` is only supported on file-backed file descriptors"
)
})?;

if class == this.eval_windows_u32("c", "FileEndOfFileInfo") {
let place = this
.ptr_to_mplace(file_information, this.windows_ty_layout("FILE_END_OF_FILE_INFO"));
let new_len =
this.read_scalar(&this.project_field_named(&place, "EndOfFile")?)?.to_i64()?;
match file.file.set_len(new_len.try_into().unwrap()) {
Ok(_) => interp_ok(this.eval_windows("c", "TRUE")),
Err(e) => {
this.set_last_error(e)?;
interp_ok(this.eval_windows("c", "FALSE"))
}
}
} else if class == this.eval_windows_u32("c", "FileAllocationInfo") {
// On Windows, files are somewhat similar to a `Vec` in that they have a separate
// "length" (called "EOF position") and "capacity" (called "allocation size").
// Growing the allocation size is largely a performance hint which we can
// ignore -- it can also be directly queried, but we currently do not support that.
// So we only need to do something if this operation shrinks the allocation size
// so far that it affects the EOF position.
let place = this
.ptr_to_mplace(file_information, this.windows_ty_layout("FILE_ALLOCATION_INFO"));
let new_alloc_size: u64 = this
.read_scalar(&this.project_field_named(&place, "AllocationSize")?)?
.to_i64()?
.try_into()
.unwrap();
let old_len = match file.file.metadata() {
Ok(m) => m.len(),
Err(e) => {
this.set_last_error(e)?;
return interp_ok(this.eval_windows("c", "FALSE"));
}
};
if new_alloc_size < old_len {
match file.file.set_len(new_alloc_size) {
Ok(_) => interp_ok(this.eval_windows("c", "TRUE")),
Err(e) => {
this.set_last_error(e)?;
interp_ok(this.eval_windows("c", "FALSE"))
}
}
} else {
interp_ok(this.eval_windows("c", "TRUE"))
}
} else {
throw_unsup_format!(
"SetFileInformationByHandle: Unsupported `FileInformationClass` value {}",
class
)
}
}

fn FlushFileBuffers(
&mut self,
file: &OpTy<'tcx>, // HANDLE
) -> InterpResult<'tcx, Scalar> {
// ^ returns BOOL (i32 on Windows)
let this = self.eval_context_mut();
this.assert_target_os(Os::Windows, "FlushFileBuffers");

let file = this.read_handle(file, "FlushFileBuffers")?;
let Handle::File(fd_num) = file else { this.invalid_handle("FlushFileBuffers")? };
let Some(desc) = this.machine.fds.get(fd_num) else {
this.invalid_handle("FlushFileBuffers")?
};
let file = desc.downcast::<FileHandle>().ok_or_else(|| {
err_unsup_format!(
"`FlushFileBuffers` is only supported on file-backed file descriptors"
)
})?;

if !file.writable {
this.set_last_error(IoError::WindowsError("ERROR_ACCESS_DENIED"))?;
return interp_ok(this.eval_windows("c", "FALSE"));
}

match file.file.sync_all() {
Ok(_) => interp_ok(this.eval_windows("c", "TRUE")),
Err(e) => {
this.set_last_error(e)?;
interp_ok(this.eval_windows("c", "FALSE"))
}
}
}

fn DeleteFileW(
&mut self,
file_name: &OpTy<'tcx>, // LPCWSTR
Expand Down
25 changes: 2 additions & 23 deletions src/tools/miri/src/shims/x86/avx2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use rustc_target::callconv::FnAbi;

use super::{
ShiftOp, horizontal_bin_op, mpsadbw, packssdw, packsswb, packusdw, packuswb, permute, pmaddbw,
pmulhrsw, psadbw, psign, shift_simd_by_scalar,
pmulhrsw, psadbw, pshufb, psign, shift_simd_by_scalar,
};
use crate::*;

Expand Down Expand Up @@ -189,28 +189,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
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!(dest_len, left_len);
assert_eq!(dest_len, right_len);

for i in 0..dest_len {
let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u8()?;
let dest = this.project_index(&dest, i)?;

let res = if right & 0x80 == 0 {
// Shuffle each 128-bit (16-byte) block independently.
let j = u64::from(right % 16).strict_add(i & !15);
this.read_scalar(&this.project_index(&left, j)?)?
} else {
// If the highest bit in `right` is 1, write zero.
Scalar::from_u8(0)
};

this.write_scalar(res, &dest)?;
}
pshufb(this, left, right, dest)?;
}
// Used to implement the _mm256_sign_epi{8,16,32} functions.
// Negates elements from `left` when the corresponding element in
Expand Down
Loading
Loading