Auto merge of #130709 - RalfJung:miri-sync, r=RalfJung

Miri subtree update

r? `@ghost`
This commit is contained in:
bors 2024-09-22 17:17:11 +00:00
commit 8ed95d1d9e
86 changed files with 2334 additions and 2283 deletions

View File

@ -12,6 +12,9 @@ on:
schedule:
- cron: '44 4 * * *' # At 4:44 UTC every day.
permissions:
contents: write
defaults:
run:
shell: bash

View File

@ -1,7 +1,7 @@
use std::env;
use std::iter;
use anyhow::{bail, Result};
use anyhow::{Result, bail};
pub struct Args {
args: iter::Peekable<env::Args>,

View File

@ -8,13 +8,13 @@ use std::path::PathBuf;
use std::process;
use std::time::Duration;
use anyhow::{anyhow, bail, Context, Result};
use anyhow::{Context, Result, anyhow, bail};
use path_macro::path;
use walkdir::WalkDir;
use xshell::{cmd, Shell};
use xshell::{Shell, cmd};
use crate::util::*;
use crate::Command;
use crate::util::*;
/// Used for rustc syncs.
const JOSH_FILTER: &str =

View File

@ -6,7 +6,7 @@ mod util;
use std::ops::Range;
use anyhow::{anyhow, bail, Context, Result};
use anyhow::{Context, Result, anyhow, bail};
#[derive(Clone, Debug)]
pub enum Command {

View File

@ -5,10 +5,10 @@ use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::{env, iter, thread};
use anyhow::{anyhow, bail, Context, Result};
use anyhow::{Context, Result, anyhow, bail};
use dunce::canonicalize;
use path_macro::path;
use xshell::{cmd, Cmd, Shell};
use xshell::{Cmd, Shell, cmd};
pub fn miri_dir() -> std::io::Result<PathBuf> {
const MIRI_SCRIPT_ROOT_DIR: &str = env!("CARGO_MANIFEST_DIR");

View File

@ -1 +1 @@
e2dc1a1c0f97a90319181a721ab317210307617a
6ce376774c0bc46ac8be247bca93ff5a1287a8fc

View File

@ -1,4 +1,4 @@
version = "Two"
style_edition = "2024"
use_small_heuristics = "Max"
match_arm_blocks = false
match_arm_leading_pipes = "Preserve"

View File

@ -4,7 +4,7 @@ use rand::Rng;
use rustc_target::abi::{Align, Size};
use crate::{concurrency::VClock, MemoryKind, MiriConfig, ThreadId};
use crate::{MemoryKind, MiriConfig, ThreadId, concurrency::VClock};
const MAX_POOL_SIZE: usize = 64;

View File

@ -12,12 +12,12 @@ use std::mem;
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::mir::{Mutability, RetagKind};
use rustc_middle::ty::{self, layout::HasParamEnv, Ty};
use rustc_middle::ty::{self, Ty, layout::HasParamEnv};
use rustc_target::abi::{Abi, Size};
use crate::borrow_tracker::{
stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder},
GlobalStateInner, ProtectorKind,
stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder},
};
use crate::concurrency::data_race::{NaReadType, NaWriteType};
use crate::*;
@ -913,11 +913,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
new_perm: NewPermission,
) -> InterpResult<'tcx> {
let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
let val = self.ecx.sb_retag_reference(
&val,
new_perm,
RetagInfo { cause: self.retag_cause, in_field: self.in_field },
)?;
let val = self.ecx.sb_retag_reference(&val, new_perm, RetagInfo {
cause: self.retag_cause,
in_field: self.in_field,
})?;
self.ecx.write_immediate(*val, place)?;
Ok(())
}
@ -1003,11 +1002,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
access: Some(AccessKind::Write),
protector: Some(ProtectorKind::StrongProtector),
};
this.sb_retag_place(
place,
new_perm,
RetagInfo { cause: RetagCause::InPlaceFnPassing, in_field: false },
)
this.sb_retag_place(place, new_perm, RetagInfo {
cause: RetagCause::InPlaceFnPassing,
in_field: false,
})
}
/// Mark the given tag as exposed. It was found on a pointer with the given AllocId.

View File

@ -4,11 +4,11 @@ use std::ops::Range;
use rustc_data_structures::fx::FxHashSet;
use tracing::trace;
use crate::borrow_tracker::{
stacked_borrows::{Item, Permission},
AccessKind, BorTag,
};
use crate::ProvenanceExtra;
use crate::borrow_tracker::{
AccessKind, BorTag,
stacked_borrows::{Item, Permission},
};
/// Exactly what cache size we should use is a difficult trade-off. There will always be some
/// workload which has a `BorTag` working set which exceeds the size of the cache, and ends up

View File

@ -4,12 +4,12 @@ use std::ops::Range;
use rustc_data_structures::fx::FxHashMap;
use rustc_span::{Span, SpanData};
use crate::borrow_tracker::ProtectorKind;
use crate::borrow_tracker::tree_borrows::{
perms::{PermTransition, Permission},
tree::LocationState,
unimap::UniIndex,
};
use crate::borrow_tracker::ProtectorKind;
use crate::*;
/// Cause of an access: either a real access or one

View File

@ -1,6 +1,6 @@
use rustc_middle::{
mir::{Mutability, RetagKind},
ty::{self, layout::HasParamEnv, Ty},
ty::{self, Ty, layout::HasParamEnv},
};
use rustc_span::def_id::DefId;
use rustc_target::abi::{Abi, Size};

View File

@ -1,9 +1,9 @@
use std::cmp::{Ordering, PartialOrd};
use std::fmt;
use crate::AccessKind;
use crate::borrow_tracker::tree_borrows::diagnostics::TransitionError;
use crate::borrow_tracker::tree_borrows::tree::AccessRelatedness;
use crate::AccessKind;
/// The activation states of a pointer.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -345,18 +345,14 @@ pub mod diagnostics {
use super::*;
impl fmt::Display for PermissionPriv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
ReservedFrz { conflicted: false } => "Reserved",
ReservedFrz { conflicted: true } => "Reserved (conflicted)",
ReservedIM => "Reserved (interior mutable)",
Active => "Active",
Frozen => "Frozen",
Disabled => "Disabled",
}
)
write!(f, "{}", match self {
ReservedFrz { conflicted: false } => "Reserved",
ReservedFrz { conflicted: true } => "Reserved (conflicted)",
ReservedIM => "Reserved (interior mutable)",
Active => "Active",
Frozen => "Frozen",
Disabled => "Disabled",
})
}
}
@ -551,7 +547,7 @@ impl Permission {
#[cfg(test)]
mod propagation_optimization_checks {
pub use super::*;
use crate::borrow_tracker::tree_borrows::exhaustive::{precondition, Exhaustive};
use crate::borrow_tracker::tree_borrows::exhaustive::{Exhaustive, precondition};
impl Exhaustive for PermissionPriv {
fn exhaustive() -> Box<dyn Iterator<Item = Self>> {

View File

@ -19,10 +19,10 @@ use rustc_span::Span;
use rustc_target::abi::Size;
use crate::borrow_tracker::tree_borrows::{
Permission,
diagnostics::{self, NodeDebugInfo, TbError, TransitionError},
perms::PermTransition,
unimap::{UniEntry, UniIndex, UniKeyMap, UniValMap},
Permission,
};
use crate::borrow_tracker::{GlobalState, ProtectorKind};
use crate::*;
@ -587,16 +587,13 @@ impl Tree {
let mut debug_info = NodeDebugInfo::new(root_tag, root_default_perm, span);
// name the root so that all allocations contain one named pointer
debug_info.add_name("root of the allocation");
nodes.insert(
root_idx,
Node {
tag: root_tag,
parent: None,
children: SmallVec::default(),
default_initial_perm: root_default_perm,
debug_info,
},
);
nodes.insert(root_idx, Node {
tag: root_tag,
parent: None,
children: SmallVec::default(),
default_initial_perm: root_default_perm,
debug_info,
});
nodes
};
let rperms = {
@ -626,16 +623,13 @@ impl<'tcx> Tree {
let idx = self.tag_mapping.insert(new_tag);
let parent_idx = self.tag_mapping.get(&parent_tag).unwrap();
// Create the node
self.nodes.insert(
idx,
Node {
tag: new_tag,
parent: Some(parent_idx),
children: SmallVec::default(),
default_initial_perm,
debug_info: NodeDebugInfo::new(new_tag, default_initial_perm, span),
},
);
self.nodes.insert(idx, Node {
tag: new_tag,
parent: Some(parent_idx),
children: SmallVec::default(),
default_initial_perm,
debug_info: NodeDebugInfo::new(new_tag, default_initial_perm, span),
});
// Register new_tag as a child of parent_tag
self.nodes.get_mut(parent_idx).unwrap().children.push(idx);
// Initialize perms

View File

@ -2,7 +2,7 @@
#![cfg(test)]
use super::*;
use crate::borrow_tracker::tree_borrows::exhaustive::{precondition, Exhaustive};
use crate::borrow_tracker::tree_borrows::exhaustive::{Exhaustive, precondition};
use std::fmt;
impl Exhaustive for LocationState {

View File

@ -1,5 +1,5 @@
use std::any::Any;
use std::collections::{hash_map::Entry, VecDeque};
use std::collections::{VecDeque, hash_map::Entry};
use std::ops::Not;
use std::time::Duration;
@ -283,12 +283,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
data: Option<Box<dyn Any>>,
) -> InterpResult<'tcx, MutexId> {
let this = self.eval_context_mut();
this.create_id(
lock,
offset,
|ecx| &mut ecx.machine.sync.mutexes,
Mutex { data, ..Default::default() },
)
this.create_id(lock, offset, |ecx| &mut ecx.machine.sync.mutexes, Mutex {
data,
..Default::default()
})
}
/// Lazily create a new mutex.
@ -355,12 +353,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
data: Option<Box<dyn Any>>,
) -> InterpResult<'tcx, CondvarId> {
let this = self.eval_context_mut();
this.create_id(
condvar,
offset,
|ecx| &mut ecx.machine.sync.condvars,
Condvar { data, ..Default::default() },
)
this.create_id(condvar, offset, |ecx| &mut ecx.machine.sync.condvars, Condvar {
data,
..Default::default()
})
}
fn condvar_get_or_create_id(

View File

@ -1,5 +1,5 @@
use rustc_index::Idx;
use rustc_span::{Span, SpanData, DUMMY_SP};
use rustc_span::{DUMMY_SP, Span, SpanData};
use smallvec::SmallVec;
use std::{
cmp::Ordering,

View File

@ -2,7 +2,7 @@ use std::fmt::{self, Write};
use std::num::NonZero;
use rustc_errors::{Diag, DiagMessage, Level};
use rustc_span::{SpanData, Symbol, DUMMY_SP};
use rustc_span::{DUMMY_SP, SpanData, Symbol};
use rustc_target::abi::{Align, Size};
use crate::borrow_tracker::stacked_borrows::diagnostics::TagHistory;

View File

@ -13,9 +13,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def::Namespace;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{
self,
self, Ty, TyCtxt,
layout::{LayoutCx, LayoutOf},
Ty, TyCtxt,
};
use rustc_target::spec::abi::Abi;

View File

@ -7,12 +7,12 @@ use std::time::Duration;
use rand::RngCore;
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use rustc_apfloat::Float;
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use rustc_hir::{
def::{DefKind, Namespace},
def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE},
Safety,
def::{DefKind, Namespace},
def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE},
};
use rustc_index::IndexVec;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@ -21,9 +21,8 @@ use rustc_middle::middle::exported_symbols::ExportedSymbol;
use rustc_middle::mir;
use rustc_middle::ty::layout::{FnAbiOf, MaybeResult};
use rustc_middle::ty::{
self,
self, FloatTy, IntTy, Ty, TyCtxt, UintTy,
layout::{LayoutOf, TyAndLayout},
FloatTy, IntTy, Ty, TyCtxt, UintTy,
};
use rustc_session::config::CrateType;
use rustc_span::{Span, Symbol};

View File

@ -9,12 +9,12 @@ use rustc_middle::{
mir,
ty::{self, FloatTy},
};
use rustc_span::{sym, Symbol};
use rustc_span::{Symbol, sym};
use rustc_target::abi::Size;
use crate::*;
use atomic::EvalContextExt as _;
use helpers::{check_arg_count, ToHost, ToSoft};
use helpers::{ToHost, ToSoft, check_arg_count};
use simd::EvalContextExt as _;
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}

View File

@ -3,10 +3,10 @@ use either::Either;
use rustc_apfloat::{Float, Round};
use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
use rustc_middle::{mir, ty, ty::FloatTy};
use rustc_span::{sym, Symbol};
use rustc_span::{Symbol, sym};
use rustc_target::abi::{Endian, HasDataLayout};
use crate::helpers::{bool_to_simd_element, check_arg_count, simd_element_to_bool, ToHost, ToSoft};
use crate::helpers::{ToHost, ToSoft, bool_to_simd_element, check_arg_count, simd_element_to_bool};
use crate::*;
#[derive(Copy, Clone)]

View File

@ -113,13 +113,13 @@ pub type PlaceTy<'tcx> = interpret::PlaceTy<'tcx, machine::Provenance>;
pub type MPlaceTy<'tcx> = interpret::MPlaceTy<'tcx, machine::Provenance>;
pub use crate::intrinsics::EvalContextExt as _;
pub use crate::shims::EmulateItemResult;
pub use crate::shims::env::{EnvVars, EvalContextExt as _};
pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _};
pub use crate::shims::os_str::EvalContextExt as _;
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
pub use crate::shims::time::EvalContextExt as _;
pub use crate::shims::tls::TlsData;
pub use crate::shims::EmulateItemResult;
pub use crate::alloc_addresses::{EvalContextExt as _, ProvenanceMode};
pub use crate::alloc_bytes::MiriAllocBytes;
@ -140,11 +140,11 @@ pub use crate::concurrency::{
},
};
pub use crate::diagnostics::{
report_error, EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo,
EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo, report_error,
};
pub use crate::eval::{
create_ecx, eval_entry, AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, RejectOpWith,
ValidationMode,
AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, RejectOpWith, ValidationMode,
create_ecx, eval_entry,
};
pub use crate::helpers::{AccessKind, EvalContextExt as _};
pub use crate::machine::{

View File

@ -8,9 +8,9 @@ use std::fmt;
use std::path::Path;
use std::process;
use rand::rngs::StdRng;
use rand::Rng;
use rand::SeedableRng;
use rand::rngs::StdRng;
use rustc_attr::InlineAttr;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@ -20,9 +20,8 @@ use rustc_middle::{
mir,
query::TyCtxtAt,
ty::{
self,
self, Instance, Ty, TyCtxt,
layout::{HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout},
Instance, Ty, TyCtxt,
},
};
use rustc_session::config::InliningThreshold;
@ -1080,13 +1079,10 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
// Call the lang item.
let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap();
let panic = ty::Instance::mono(ecx.tcx.tcx, panic);
ecx.call_function(
panic,
Abi::Rust,
&[],
None,
StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
)?;
ecx.call_function(panic, Abi::Rust, &[], None, StackPopCleanup::Goto {
ret: None,
unwind: mir::UnwindAction::Unreachable,
})?;
Ok(())
}

View File

@ -1,6 +1,6 @@
use std::iter;
use rand::{seq::IteratorRandom, Rng};
use rand::{Rng, seq::IteratorRandom};
use rustc_apfloat::{Float, FloatConvert};
use rustc_middle::mir;
use rustc_target::abi::Size;

View File

@ -2,7 +2,7 @@ use crate::*;
use rustc_ast::ast::Mutability;
use rustc_middle::ty::layout::LayoutOf as _;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_span::{hygiene, BytePos, Loc, Symbol};
use rustc_span::{BytePos, Loc, Symbol, hygiene};
use rustc_target::{abi::Size, spec::abi::Abi};
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}

View File

@ -63,10 +63,10 @@ impl<'tcx> MiriMachine<'tcx> {
match this.tcx.sess.target.os.as_ref() {
"linux" => {
Self::null_ptr_extern_statics(
this,
&["__cxa_thread_atexit_impl", "__clock_gettime64"],
)?;
Self::null_ptr_extern_statics(this, &[
"__cxa_thread_atexit_impl",
"__clock_gettime64",
])?;
Self::weak_symbol_extern_statics(this, &["getrandom", "statx"])?;
}
"freebsd" => {

View File

@ -7,8 +7,8 @@ use std::os::unix::ffi::{OsStrExt, OsStringExt};
#[cfg(windows)]
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::LayoutOf;
use crate::*;

View File

@ -13,8 +13,8 @@
use rustc_ast::Mutability;
use rustc_middle::{mir, ty};
use rustc_target::spec::abi::Abi;
use rustc_target::spec::PanicStrategy;
use rustc_target::spec::abi::Abi;
use crate::*;
use helpers::check_arg_count;
@ -248,13 +248,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Call the lang item associated with this message.
let fn_item = this.tcx.require_lang_item(msg.panic_function(), None);
let instance = ty::Instance::mono(this.tcx.tcx, fn_item);
this.call_function(
instance,
Abi::Rust,
&[],
None,
StackPopCleanup::Goto { ret: None, unwind },
)?;
this.call_function(instance, Abi::Rust, &[], None, StackPopCleanup::Goto {
ret: None,
unwind,
})?;
}
}
Ok(())

View File

@ -1,7 +1,7 @@
//! Implement thread-local storage.
use std::collections::btree_map::Entry as BTreeEntry;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry as BTreeEntry;
use std::task::Poll;
use rustc_middle::ty;

View File

@ -4,8 +4,8 @@ use std::io::ErrorKind;
use std::mem;
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::LayoutOf;
use rustc_target::abi::Size;
use crate::*;

View File

@ -25,49 +25,64 @@ pub(crate) enum FlockOp {
pub trait FileDescription: std::fmt::Debug + Any {
fn name(&self) -> &'static str;
/// Reads as much as possible into the given buffer, and returns the number of bytes read.
/// Reads as much as possible into the given buffer `ptr`.
/// `len` indicates how many bytes we should try to read.
/// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error.
fn read<'tcx>(
&self,
_self_ref: &FileDescriptionRef,
_communicate_allowed: bool,
_bytes: &mut [u8],
_ptr: Pointer,
_len: usize,
_dest: &MPlaceTy<'tcx>,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
) -> InterpResult<'tcx> {
throw_unsup_format!("cannot read from {}", self.name());
}
/// Writes as much as possible from the given buffer, and returns the number of bytes written.
/// Writes as much as possible from the given buffer `ptr`.
/// `len` indicates how many bytes we should try to write.
/// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error.
fn write<'tcx>(
&self,
_self_ref: &FileDescriptionRef,
_communicate_allowed: bool,
_bytes: &[u8],
_ptr: Pointer,
_len: usize,
_dest: &MPlaceTy<'tcx>,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
) -> InterpResult<'tcx> {
throw_unsup_format!("cannot write to {}", self.name());
}
/// Reads as much as possible into the given buffer from a given offset,
/// and returns the number of bytes read.
/// Reads as much as possible into the given buffer `ptr` from a given offset.
/// `len` indicates how many bytes we should try to read.
/// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error.
fn pread<'tcx>(
&self,
_communicate_allowed: bool,
_bytes: &mut [u8],
_offset: u64,
_ptr: Pointer,
_len: usize,
_dest: &MPlaceTy<'tcx>,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
) -> InterpResult<'tcx> {
throw_unsup_format!("cannot pread from {}", self.name());
}
/// Writes as much as possible from the given buffer starting at a given offset,
/// and returns the number of bytes written.
/// Writes as much as possible from the given buffer `ptr` starting at a given offset.
/// `ptr` is the pointer to the user supplied read buffer.
/// `len` indicates how many bytes we should try to write.
/// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error.
fn pwrite<'tcx>(
&self,
_communicate_allowed: bool,
_bytes: &[u8],
_ptr: Pointer,
_len: usize,
_offset: u64,
_dest: &MPlaceTy<'tcx>,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
) -> InterpResult<'tcx> {
throw_unsup_format!("cannot pwrite to {}", self.name());
}
@ -125,14 +140,18 @@ impl FileDescription for io::Stdin {
&self,
_self_ref: &FileDescriptionRef,
communicate_allowed: bool,
bytes: &mut [u8],
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
let mut bytes = vec![0; len];
if !communicate_allowed {
// We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
helpers::isolation_abort_error("`read` from stdin")?;
}
Ok(Read::read(&mut { self }, bytes))
let result = Read::read(&mut { self }, &mut bytes);
ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
}
fn is_tty(&self, communicate_allowed: bool) -> bool {
@ -149,9 +168,12 @@ impl FileDescription for io::Stdout {
&self,
_self_ref: &FileDescriptionRef,
_communicate_allowed: bool,
bytes: &[u8],
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
// We allow writing to stderr even with isolation enabled.
let result = Write::write(&mut { self }, bytes);
// Stdout is buffered, flush to make sure it appears on the
@ -160,8 +182,7 @@ impl FileDescription for io::Stdout {
// the host -- there is no good in adding extra buffering
// here.
io::stdout().flush().unwrap();
Ok(result)
ecx.return_written_byte_count_or_error(result, dest)
}
fn is_tty(&self, communicate_allowed: bool) -> bool {
@ -178,12 +199,16 @@ impl FileDescription for io::Stderr {
&self,
_self_ref: &FileDescriptionRef,
_communicate_allowed: bool,
bytes: &[u8],
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
// We allow writing to stderr even with isolation enabled.
// No need to flush, stderr is not buffered.
Ok(Write::write(&mut { self }, bytes))
let result = Write::write(&mut { self }, bytes);
ecx.return_written_byte_count_or_error(result, dest)
}
fn is_tty(&self, communicate_allowed: bool) -> bool {
@ -204,11 +229,14 @@ impl FileDescription for NullOutput {
&self,
_self_ref: &FileDescriptionRef,
_communicate_allowed: bool,
bytes: &[u8],
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
_ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
// We just don't write anything, but report to the user that we did.
Ok(Ok(bytes.len()))
let result = Ok(len);
ecx.return_written_byte_count_or_error(result, dest)
}
}
@ -535,7 +563,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
buf: Pointer,
count: u64,
offset: Option<i128>,
) -> InterpResult<'tcx, Scalar> {
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
// Isolation check is done via `FileDescription` trait.
@ -550,48 +579,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let count = count
.min(u64::try_from(this.target_isize_max()).unwrap())
.min(u64::try_from(isize::MAX).unwrap());
let count = usize::try_from(count).unwrap(); // now it fits in a `usize`
let communicate = this.machine.communicate();
// We temporarily dup the FD to be able to retain mutable access to `this`.
let Some(fd) = this.machine.fds.get(fd_num) else {
trace!("read: FD not found");
return Ok(Scalar::from_target_isize(this.fd_not_found()?, this));
let res: i32 = this.fd_not_found()?;
this.write_int(res, dest)?;
return Ok(());
};
trace!("read: FD mapped to {fd:?}");
// We want to read at most `count` bytes. We are sure that `count` is not negative
// because it was a target's `usize`. Also we are sure that its smaller than
// `usize::MAX` because it is bounded by the host's `isize`.
let mut bytes = vec![0; usize::try_from(count).unwrap()];
let result = match offset {
None => fd.read(&fd, communicate, &mut bytes, this),
match offset {
None => fd.read(&fd, communicate, buf, count, dest, this)?,
Some(offset) => {
let Ok(offset) = u64::try_from(offset) else {
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
return Ok(Scalar::from_target_isize(-1, this));
this.write_int(-1, dest)?;
return Ok(());
};
fd.pread(communicate, &mut bytes, offset, this)
fd.pread(communicate, offset, buf, count, dest, this)?
}
};
// `File::read` never returns a value larger than `count`, so this cannot fail.
match result?.map(|c| i64::try_from(c).unwrap()) {
Ok(read_bytes) => {
// If reading to `bytes` did not fail, we write those bytes to the buffer.
// Crucially, if fewer than `bytes.len()` bytes were read, only write
// that much into the output buffer!
this.write_bytes_ptr(
buf,
bytes[..usize::try_from(read_bytes).unwrap()].iter().copied(),
)?;
Ok(Scalar::from_target_isize(read_bytes, this))
}
Err(e) => {
this.set_last_error_from_io_error(e)?;
Ok(Scalar::from_target_isize(-1, this))
}
}
Ok(())
}
fn write(
@ -600,7 +616,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
buf: Pointer,
count: u64,
offset: Option<i128>,
) -> InterpResult<'tcx, Scalar> {
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
// Isolation check is done via `FileDescription` trait.
@ -613,27 +630,72 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let count = count
.min(u64::try_from(this.target_isize_max()).unwrap())
.min(u64::try_from(isize::MAX).unwrap());
let count = usize::try_from(count).unwrap(); // now it fits in a `usize`
let communicate = this.machine.communicate();
let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned();
// We temporarily dup the FD to be able to retain mutable access to `this`.
let Some(fd) = this.machine.fds.get(fd_num) else {
return Ok(Scalar::from_target_isize(this.fd_not_found()?, this));
let res: i32 = this.fd_not_found()?;
this.write_int(res, dest)?;
return Ok(());
};
let result = match offset {
None => fd.write(&fd, communicate, &bytes, this),
match offset {
None => fd.write(&fd, communicate, buf, count, dest, this)?,
Some(offset) => {
let Ok(offset) = u64::try_from(offset) else {
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
return Ok(Scalar::from_target_isize(-1, this));
this.write_int(-1, dest)?;
return Ok(());
};
fd.pwrite(communicate, &bytes, offset, this)
fd.pwrite(communicate, buf, count, offset, dest, this)?
}
};
Ok(())
}
let result = result?.map(|c| i64::try_from(c).unwrap());
Ok(Scalar::from_target_isize(this.try_unwrap_io_result(result)?, this))
/// Helper to implement `FileDescription::read`:
/// `result` should be the return value of some underlying `read` call that used `bytes` as its output buffer.
/// The length of `bytes` must not exceed either the host's or the target's `isize`.
/// If `Result` indicates success, `bytes` is written to `buf` and the size is written to `dest`.
/// Otherwise, `-1` is written to `dest` and the last libc error is set appropriately.
fn return_read_bytes_and_count(
&mut self,
buf: Pointer,
bytes: &[u8],
result: io::Result<usize>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
match result {
Ok(read_bytes) => {
// If reading to `bytes` did not fail, we write those bytes to the buffer.
// Crucially, if fewer than `bytes.len()` bytes were read, only write
// that much into the output buffer!
this.write_bytes_ptr(buf, bytes[..read_bytes].iter().copied())?;
// The actual read size is always less than what got originally requested so this cannot fail.
this.write_int(u64::try_from(read_bytes).unwrap(), dest)?;
return Ok(());
}
Err(e) => {
this.set_last_error_from_io_error(e)?;
this.write_int(-1, dest)?;
return Ok(());
}
}
}
/// This function writes the number of written bytes (given in `result`) to `dest`, or sets the
/// last libc error and writes -1 to dest.
fn return_written_byte_count_or_error(
&mut self,
result: io::Result<usize>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let result = this.try_unwrap_io_result(result.map(|c| i64::try_from(c).unwrap()))?;
this.write_int(result, dest)?;
Ok(())
}
}

View File

@ -92,8 +92,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
let result = this.read(fd, buf, count, None)?;
this.write_scalar(result, dest)?;
this.read(fd, buf, count, None, dest)?;
}
"write" => {
let [fd, buf, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
@ -101,9 +100,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(n)?;
trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
let result = this.write(fd, buf, count, None)?;
// Now, `result` is the value we return back to the program.
this.write_scalar(result, dest)?;
this.write(fd, buf, count, None, dest)?;
}
"pread" => {
let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
@ -111,8 +108,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
let result = this.read(fd, buf, count, Some(offset))?;
this.write_scalar(result, dest)?;
this.read(fd, buf, count, Some(offset), dest)?;
}
"pwrite" => {
let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
@ -121,9 +117,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let count = this.read_target_usize(n)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
let result = this.write(fd, buf, count, Some(offset))?;
// Now, `result` is the value we return back to the program.
this.write_scalar(result, dest)?;
this.write(fd, buf, count, Some(offset), dest)?;
}
"pread64" => {
let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
@ -131,8 +125,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let buf = this.read_pointer(buf)?;
let count = this.read_target_usize(count)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
let result = this.read(fd, buf, count, Some(offset))?;
this.write_scalar(result, dest)?;
this.read(fd, buf, count, Some(offset), dest)?;
}
"pwrite64" => {
let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
@ -141,9 +134,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let count = this.read_target_usize(n)?;
let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off64_t").size)?;
trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
let result = this.write(fd, buf, count, Some(offset))?;
// Now, `result` is the value we return back to the program.
this.write_scalar(result, dest)?;
this.write(fd, buf, count, Some(offset), dest)?;
}
"close" => {
let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

View File

@ -2,7 +2,7 @@
use std::borrow::Cow;
use std::fs::{
read_dir, remove_dir, remove_file, rename, DirBuilder, File, FileType, OpenOptions, ReadDir,
DirBuilder, File, FileType, OpenOptions, ReadDir, read_dir, remove_dir, remove_file, rename,
};
use std::io::{self, ErrorKind, IsTerminal, Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
@ -34,32 +34,43 @@ impl FileDescription for FileHandle {
&self,
_self_ref: &FileDescriptionRef,
communicate_allowed: bool,
bytes: &mut [u8],
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
Ok((&mut &self.file).read(bytes))
let mut bytes = vec![0; len];
let result = (&mut &self.file).read(&mut bytes);
ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
}
fn write<'tcx>(
&self,
_self_ref: &FileDescriptionRef,
communicate_allowed: bool,
bytes: &[u8],
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
Ok((&mut &self.file).write(bytes))
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
let result = (&mut &self.file).write(bytes);
ecx.return_written_byte_count_or_error(result, dest)
}
fn pread<'tcx>(
&self,
communicate_allowed: bool,
bytes: &mut [u8],
offset: u64,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
let mut bytes = vec![0; len];
// Emulates pread using seek + read + seek to restore cursor position.
// Correctness of this emulation relies on sequential nature of Miri execution.
// The closure is used to emulate `try` block, since we "bubble" `io::Error` using `?`.
@ -67,27 +78,31 @@ impl FileDescription for FileHandle {
let mut f = || {
let cursor_pos = file.stream_position()?;
file.seek(SeekFrom::Start(offset))?;
let res = file.read(bytes);
let res = file.read(&mut bytes);
// Attempt to restore cursor position even if the read has failed
file.seek(SeekFrom::Start(cursor_pos))
.expect("failed to restore file position, this shouldn't be possible");
res
};
Ok(f())
let result = f();
ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
}
fn pwrite<'tcx>(
&self,
communicate_allowed: bool,
bytes: &[u8],
ptr: Pointer,
len: usize,
offset: u64,
_ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
// Emulates pwrite using seek + write + seek to restore cursor position.
// Correctness of this emulation relies on sequential nature of Miri execution.
// The closure is used to emulate `try` block, since we "bubble" `io::Error` using `?`.
let file = &mut &self.file;
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
let mut f = || {
let cursor_pos = file.stream_position()?;
file.seek(SeekFrom::Start(offset))?;
@ -97,7 +112,8 @@ impl FileDescription for FileHandle {
.expect("failed to restore file position, this shouldn't be possible");
res
};
Ok(f())
let result = f();
ecx.return_written_byte_count_or_error(result, dest)
}
fn seek<'tcx>(
@ -173,7 +189,7 @@ impl FileDescription for FileHandle {
use windows_sys::Win32::{
Foundation::{ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, FALSE, HANDLE, TRUE},
Storage::FileSystem::{
LockFileEx, UnlockFile, LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY,
LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, LockFileEx, UnlockFile,
},
};
let fh = self.file.as_raw_handle() as HANDLE;

View File

@ -2,18 +2,12 @@
use std::cell::{Cell, RefCell};
use std::io;
use std::io::{Error, ErrorKind};
use std::mem;
use rustc_target::abi::Endian;
use crate::shims::unix::fd::FileDescriptionRef;
use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
use crate::shims::unix::*;
use crate::{concurrency::VClock, *};
// We'll only do reads and writes in chunks of size u64.
const U64_ARRAY_SIZE: usize = mem::size_of::<u64>();
/// Maximum value that the eventfd counter can hold.
const MAX_COUNTER: u64 = u64::MAX - 1;
@ -62,37 +56,50 @@ impl FileDescription for Event {
&self,
self_ref: &FileDescriptionRef,
_communicate_allowed: bool,
bytes: &mut [u8],
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
) -> InterpResult<'tcx> {
// We're treating the buffer as a `u64`.
let ty = ecx.machine.layouts.u64;
// Check the size of slice, and return error only if the size of the slice < 8.
let Some(bytes) = bytes.first_chunk_mut::<U64_ARRAY_SIZE>() else {
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
};
if len < ty.size.bytes_usize() {
ecx.set_last_error_from_io_error(Error::from(ErrorKind::InvalidInput))?;
ecx.write_int(-1, dest)?;
return Ok(());
}
// eventfd read at the size of u64.
let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);
// Block when counter == 0.
let counter = self.counter.get();
if counter == 0 {
if self.is_nonblock {
return Ok(Err(Error::from(ErrorKind::WouldBlock)));
} else {
//FIXME: blocking is not supported
throw_unsup_format!("eventfd: blocking is unsupported");
ecx.set_last_error_from_io_error(Error::from(ErrorKind::WouldBlock))?;
ecx.write_int(-1, dest)?;
return Ok(());
}
throw_unsup_format!("eventfd: blocking is unsupported");
} else {
// Synchronize with all prior `write` calls to this FD.
ecx.acquire_clock(&self.clock.borrow());
// Return the counter in the host endianness using the buffer provided by caller.
*bytes = match ecx.tcx.sess.target.endian {
Endian::Little => counter.to_le_bytes(),
Endian::Big => counter.to_be_bytes(),
};
// Give old counter value to userspace, and set counter value to 0.
ecx.write_int(counter, &buf_place)?;
self.counter.set(0);
// When any of the event happened, we check and update the status of all supported event
// types for current file description.
ecx.check_and_update_readiness(self_ref)?;
return Ok(Ok(U64_ARRAY_SIZE));
// Tell userspace how many bytes we wrote.
ecx.write_int(buf_place.layout.size.bytes(), dest)?;
}
Ok(())
}
/// A write call adds the 8-byte integer value supplied in
@ -111,21 +118,27 @@ impl FileDescription for Event {
&self,
self_ref: &FileDescriptionRef,
_communicate_allowed: bool,
bytes: &[u8],
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
) -> InterpResult<'tcx> {
// We're treating the buffer as a `u64`.
let ty = ecx.machine.layouts.u64;
// Check the size of slice, and return error only if the size of the slice < 8.
let Some(bytes) = bytes.first_chunk::<U64_ARRAY_SIZE>() else {
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
};
// Convert from bytes to int according to host endianness.
let num = match ecx.tcx.sess.target.endian {
Endian::Little => u64::from_le_bytes(*bytes),
Endian::Big => u64::from_be_bytes(*bytes),
};
if len < ty.layout.size.bytes_usize() {
let result = Err(Error::from(ErrorKind::InvalidInput));
return ecx.return_written_byte_count_or_error(result, dest);
}
// Read the user supplied value from the pointer.
let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);
let num = ecx.read_scalar(&buf_place)?.to_u64()?;
// u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1.
if num == u64::MAX {
return Ok(Err(Error::from(ErrorKind::InvalidInput)));
let result = Err(Error::from(ErrorKind::InvalidInput));
return ecx.return_written_byte_count_or_error(result, dest);
}
// If the addition does not let the counter to exceed the maximum value, update the counter.
// Else, block.
@ -137,20 +150,20 @@ impl FileDescription for Event {
}
self.counter.set(new_count);
}
None | Some(u64::MAX) => {
None | Some(u64::MAX) =>
if self.is_nonblock {
return Ok(Err(Error::from(ErrorKind::WouldBlock)));
let result = Err(Error::from(ErrorKind::WouldBlock));
return ecx.return_written_byte_count_or_error(result, dest);
} else {
//FIXME: blocking is not supported
throw_unsup_format!("eventfd: blocking is unsupported");
}
}
},
};
// When any of the event happened, we check and update the status of all supported event
// types for current file description.
ecx.check_and_update_readiness(self_ref)?;
Ok(Ok(U64_ARRAY_SIZE))
// Return how many bytes we read.
ecx.write_int(buf_place.layout.size.bytes(), dest)
}
}

View File

@ -7,6 +7,8 @@ use std::collections::VecDeque;
use std::io;
use std::io::{Error, ErrorKind, Read};
use rustc_target::abi::Size;
use crate::shims::unix::fd::{FileDescriptionRef, WeakFileDescriptionRef};
use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
use crate::shims::unix::*;
@ -126,14 +128,17 @@ impl FileDescription for AnonSocket {
&self,
_self_ref: &FileDescriptionRef,
_communicate_allowed: bool,
bytes: &mut [u8],
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
let request_byte_size = bytes.len();
) -> InterpResult<'tcx> {
let mut bytes = vec![0; len];
// Always succeed on read size 0.
if request_byte_size == 0 {
return Ok(Ok(0));
if len == 0 {
let result = Ok(0);
return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest);
}
let Some(readbuf) = &self.readbuf else {
@ -146,7 +151,8 @@ impl FileDescription for AnonSocket {
if self.peer_fd().upgrade().is_none() {
// Socketpair with no peer and empty buffer.
// 0 bytes successfully read indicates end-of-file.
return Ok(Ok(0));
let result = Ok(0);
return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest);
} else {
if self.is_nonblock {
// Non-blocking socketpair with writer and empty buffer.
@ -154,7 +160,8 @@ impl FileDescription for AnonSocket {
// EAGAIN or EWOULDBLOCK can be returned for socket,
// POSIX.1-2001 allows either error to be returned for this case.
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
return Ok(Err(Error::from(ErrorKind::WouldBlock)));
let result = Err(Error::from(ErrorKind::WouldBlock));
return ecx.return_read_bytes_and_count(ptr, &bytes, result, dest);
} else {
// Blocking socketpair with writer and empty buffer.
// FIXME: blocking is currently not supported
@ -170,7 +177,7 @@ impl FileDescription for AnonSocket {
// Do full read / partial read based on the space available.
// Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
let actual_read_size = readbuf.buf.read(bytes).unwrap();
let actual_read_size = readbuf.buf.read(&mut bytes).unwrap();
// Need to drop before others can access the readbuf again.
drop(readbuf);
@ -186,28 +193,32 @@ impl FileDescription for AnonSocket {
ecx.check_and_update_readiness(&peer_fd)?;
}
return Ok(Ok(actual_read_size));
let result = Ok(actual_read_size);
ecx.return_read_bytes_and_count(ptr, &bytes, result, dest)
}
fn write<'tcx>(
&self,
_self_ref: &FileDescriptionRef,
_communicate_allowed: bool,
bytes: &[u8],
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, io::Result<usize>> {
let write_size = bytes.len();
) -> InterpResult<'tcx> {
// Always succeed on write size 0.
// ("If count is zero and fd refers to a file other than a regular file, the results are not specified.")
if write_size == 0 {
return Ok(Ok(0));
if len == 0 {
let result = Ok(0);
return ecx.return_written_byte_count_or_error(result, dest);
}
// We are writing to our peer's readbuf.
let Some(peer_fd) = self.peer_fd().upgrade() else {
// If the upgrade from Weak to Rc fails, it indicates that all read ends have been
// closed.
return Ok(Err(Error::from(ErrorKind::BrokenPipe)));
let result = Err(Error::from(ErrorKind::BrokenPipe));
return ecx.return_written_byte_count_or_error(result, dest);
};
let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else {
@ -221,7 +232,8 @@ impl FileDescription for AnonSocket {
if available_space == 0 {
if self.is_nonblock {
// Non-blocking socketpair with a full buffer.
return Ok(Err(Error::from(ErrorKind::WouldBlock)));
let result = Err(Error::from(ErrorKind::WouldBlock));
return ecx.return_written_byte_count_or_error(result, dest);
} else {
// Blocking socketpair with a full buffer.
throw_unsup_format!("socketpair write: blocking isn't supported yet");
@ -232,7 +244,8 @@ impl FileDescription for AnonSocket {
writebuf.clock.join(clock);
}
// Do full write / partial write based on the space available.
let actual_write_size = write_size.min(available_space);
let actual_write_size = len.min(available_space);
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
writebuf.buf.extend(&bytes[..actual_write_size]);
// Need to stop accessing peer_fd so that it can be notified.
@ -242,7 +255,8 @@ impl FileDescription for AnonSocket {
// The kernel does this even if the fd was already readable before, so we follow suit.
ecx.check_and_update_readiness(&peer_fd)?;
return Ok(Ok(actual_write_size));
let result = Ok(actual_write_size);
ecx.return_written_byte_count_or_error(result, dest)
}
}

View File

@ -1,5 +1,5 @@
use rustc_middle::ty::layout::LayoutOf as _;
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::LayoutOf as _;
use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;

View File

@ -1,14 +1,14 @@
use rustc_apfloat::{ieee::Double, ieee::Single};
use rustc_middle::mir;
use rustc_middle::ty::layout::LayoutOf as _;
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::LayoutOf as _;
use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;
use super::{
bin_op_simd_float_all, conditional_dot_product, convert_float_to_int, horizontal_bin_op,
mask_load, mask_store, round_all, test_bits_masked, test_high_bits_masked, unary_op_ps,
FloatBinOp, FloatUnaryOp,
FloatBinOp, FloatUnaryOp, bin_op_simd_float_all, conditional_dot_product, convert_float_to_int,
horizontal_bin_op, mask_load, mask_store, round_all, test_bits_masked, test_high_bits_masked,
unary_op_ps,
};
use crate::*;

View File

@ -1,12 +1,12 @@
use rustc_middle::mir;
use rustc_middle::ty::layout::LayoutOf as _;
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::LayoutOf as _;
use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;
use super::{
horizontal_bin_op, int_abs, mask_load, mask_store, mpsadbw, packssdw, packsswb, packusdw,
packuswb, pmulhrsw, psign, shift_simd_by_scalar, shift_simd_by_simd, ShiftOp,
ShiftOp, horizontal_bin_op, int_abs, mask_load, mask_store, mpsadbw, packssdw, packsswb,
packusdw, packuswb, pmulhrsw, psign, shift_simd_by_scalar, shift_simd_by_simd,
};
use crate::*;

View File

@ -1,8 +1,8 @@
use rand::Rng as _;
use rustc_apfloat::{ieee::Single, Float};
use rustc_middle::ty::layout::LayoutOf as _;
use rustc_apfloat::{Float, ieee::Single};
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::LayoutOf as _;
use rustc_middle::{mir, ty};
use rustc_span::Symbol;
use rustc_target::abi::Size;

View File

@ -3,8 +3,8 @@ use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;
use super::{
bin_op_simd_float_all, bin_op_simd_float_first, unary_op_ps, unary_op_ss, FloatBinOp,
FloatUnaryOp,
FloatBinOp, FloatUnaryOp, bin_op_simd_float_all, bin_op_simd_float_first, unary_op_ps,
unary_op_ss,
};
use crate::*;

View File

@ -3,8 +3,8 @@ use rustc_span::Symbol;
use rustc_target::spec::abi::Abi;
use super::{
bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int, packssdw, packsswb,
packuswb, shift_simd_by_scalar, FloatBinOp, ShiftOp,
FloatBinOp, ShiftOp, bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int,
packssdw, packsswb, packuswb, shift_simd_by_scalar,
};
use crate::*;

View File

@ -1,6 +1,6 @@
use rustc_middle::mir;
use rustc_middle::ty::layout::LayoutOf as _;
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::LayoutOf as _;
use rustc_span::Symbol;
use rustc_target::abi::Size;
use rustc_target::spec::abi::Abi;

View File

@ -23,7 +23,7 @@ fn main() {
// (We rely on the test runner to always disable isolation when passing no arguments.)
if std::env::args().len() <= 1 {
fn host_to_target_path(path: String) -> PathBuf {
use std::ffi::{c_char, CStr, CString};
use std::ffi::{CStr, CString, c_char};
let path = CString::new(path).unwrap();
let mut out = Vec::with_capacity(1024);

View File

@ -5,7 +5,7 @@ fn main() {
println!("subcrate running");
fn host_to_target_path(path: String) -> PathBuf {
use std::ffi::{c_char, CStr, CString};
use std::ffi::{CStr, CString, c_char};
let path = CString::new(path).unwrap();
let mut out = Vec::with_capacity(1024);

View File

@ -8,7 +8,7 @@ fn main() {
println!("subcrate testing");
fn host_to_target_path(path: String) -> PathBuf {
use std::ffi::{c_char, CStr, CString};
use std::ffi::{CStr, CString, c_char};
let path = CString::new(path).unwrap();
let mut out = Vec::with_capacity(1024);

View File

@ -8,7 +8,7 @@
use std::thread;
use windows_sys::Win32::Foundation::{HANDLE, WAIT_OBJECT_0};
use windows_sys::Win32::System::Threading::{WaitForSingleObject, INFINITE};
use windows_sys::Win32::System::Threading::{INFINITE, WaitForSingleObject};
// XXX HACK: This is how miri represents the handle for thread 0.
// This value can be "legitimately" obtained by using `GetCurrentThread` with `DuplicateHandle`

View File

@ -8,7 +8,7 @@
use std::thread;
use windows_sys::Win32::Foundation::WAIT_OBJECT_0;
use windows_sys::Win32::System::Threading::{GetCurrentThread, WaitForSingleObject, INFINITE};
use windows_sys::Win32::System::Threading::{GetCurrentThread, INFINITE, WaitForSingleObject};
fn main() {
thread::spawn(|| {

View File

@ -1,4 +1,4 @@
use std::alloc::{alloc, dealloc, Layout};
use std::alloc::{Layout, alloc, dealloc};
fn main() {
unsafe {

View File

@ -1,4 +1,4 @@
use std::alloc::{alloc, dealloc, Layout};
use std::alloc::{Layout, alloc, dealloc};
fn main() {
unsafe {

View File

@ -1,4 +1,4 @@
use std::alloc::{alloc, dealloc, Layout};
use std::alloc::{Layout, alloc, dealloc};
fn main() {
unsafe {

View File

@ -1,4 +1,4 @@
use std::alloc::{alloc, realloc, Layout};
use std::alloc::{Layout, alloc, realloc};
fn main() {
unsafe {

View File

@ -1,4 +1,4 @@
use std::alloc::{alloc, realloc, Layout};
use std::alloc::{Layout, alloc, realloc};
fn main() {
unsafe {

View File

@ -1,4 +1,4 @@
use std::alloc::{alloc, dealloc, realloc, Layout};
use std::alloc::{Layout, alloc, dealloc, realloc};
fn main() {
unsafe {

View File

@ -1,7 +1,7 @@
//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows
use std::alloc::{alloc, Layout};
use std::alloc::{Layout, alloc};
fn inner(x: *mut i32, _y: &i32) {
// If `x` and `y` alias, retagging is fine with this... but we really

View File

@ -1,6 +1,6 @@
//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows
use std::alloc::{alloc, dealloc, Layout};
use std::alloc::{Layout, alloc, dealloc};
// `x` is strongly protected but covers zero bytes.
// Let's see if deallocating the allocation x points to is UB:

View File

@ -3,8 +3,8 @@
// Avoid accidental synchronization via address reuse inside `thread::spawn`.
//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
use std::sync::atomic::{fence, AtomicUsize, Ordering};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering, fence};
use std::thread;
use std::time::Duration;

View File

@ -2,7 +2,7 @@
// Avoid accidental synchronization via address reuse inside `thread::spawn`.
//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
use std::sync::atomic::{AtomicU16, AtomicU8, Ordering};
use std::sync::atomic::{AtomicU8, AtomicU16, Ordering};
use std::thread;
fn convert(a: &AtomicU16) -> &[AtomicU8; 2] {

View File

@ -2,7 +2,7 @@
// Avoid accidental synchronization via address reuse inside `thread::spawn`.
//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
use std::sync::atomic::{AtomicU16, AtomicU8, Ordering};
use std::sync::atomic::{AtomicU8, AtomicU16, Ordering};
use std::thread;
fn convert(a: &AtomicU16) -> &[AtomicU8; 2] {

View File

@ -1,7 +1,8 @@
fn main() {
let x = &[0i32; 2];
let x = x.as_ptr().wrapping_add(1);
// If the `!0` is interpreted as `isize`, it is just `-1` and hence harmless.
// However, this is unsigned arithmetic, so really this is `usize::MAX` and hence UB.
unsafe { x.byte_add(!0).read() }; //~ERROR: does not fit in an `isize`
// If `usize::MAX` is interpreted as `isize`, it is just `-1` and hence harmless.
let _ = unsafe { x.byte_offset(usize::MAX as isize) };
// However, `byte_add` uses unsigned arithmetic, so really this is `usize::MAX` and hence UB.
let _ = unsafe { x.byte_add(usize::MAX) }; //~ERROR: does not fit in an `isize`
}

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
--> tests/fail/intrinsics/ptr_offset_unsigned_overflow.rs:LL:CC
|
LL | unsafe { x.byte_add(!0).read() };
| ^^^^^^^^^^^^^^ overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
LL | let _ = unsafe { x.byte_add(usize::MAX) };
| ^^^^^^^^^^^^^^^^^^^^^^ overflowing pointer arithmetic: the total offset in bytes does not fit in an `isize`
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

View File

@ -8,7 +8,7 @@
// so we have to stick to C++11 emulation from existing research.
use std::sync::atomic::Ordering::*;
use std::sync::atomic::{fence, AtomicUsize};
use std::sync::atomic::{AtomicUsize, fence};
use std::thread::spawn;
// Spins until it reads the given value

View File

@ -1,4 +1,4 @@
use std::alloc::{alloc, dealloc, Layout};
use std::alloc::{Layout, alloc, dealloc};
fn main() {
unsafe {

View File

@ -8,8 +8,8 @@
#![feature(ptr_internals)]
use core::ptr::addr_of_mut;
use core::ptr::Unique;
use core::ptr::addr_of_mut;
fn main() {
let mut data = 0u8;

View File

@ -3,7 +3,7 @@
#![allow(dropping_copy_types)]
use std::alloc::{alloc, dealloc, Layout};
use std::alloc::{Layout, alloc, dealloc};
use std::slice::from_raw_parts;
fn main() {

View File

@ -6,7 +6,7 @@
// Test printing allocations that contain single-byte provenance.
use std::alloc::{alloc, dealloc, Layout};
use std::alloc::{Layout, alloc, dealloc};
use std::mem::{self, MaybeUninit};
use std::slice::from_raw_parts;

View File

@ -7,7 +7,7 @@ use std::thread;
use windows_sys::Win32::Foundation::{FALSE, TRUE};
use windows_sys::Win32::System::Threading::{
InitOnceBeginInitialize, InitOnceComplete, INIT_ONCE, INIT_ONCE_INIT_FAILED,
INIT_ONCE, INIT_ONCE_INIT_FAILED, InitOnceBeginInitialize, InitOnceComplete,
};
// not in windows-sys

View File

@ -7,7 +7,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use windows_sys::Win32::Foundation::{HANDLE, WAIT_OBJECT_0};
use windows_sys::Win32::System::Threading::{WaitForSingleObject, INFINITE};
use windows_sys::Win32::System::Threading::{INFINITE, WaitForSingleObject};
fn main() {
static FLAG: AtomicBool = AtomicBool::new(false);

View File

@ -231,10 +231,10 @@ fn test_two_same_fd_in_same_epoll_instance() {
//Two notification should be received.
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
let expected_value = 5 as u64;
check_epoll_wait::<8>(
epfd,
&[(expected_event, expected_value), (expected_event, expected_value)],
);
check_epoll_wait::<8>(epfd, &[
(expected_event, expected_value),
(expected_event, expected_value),
]);
}
fn test_epoll_eventfd() {
@ -291,10 +291,10 @@ fn test_epoll_socketpair_both_sides() {
let expected_value0 = fds[0] as u64;
let expected_event1 = u32::try_from(libc::EPOLLOUT).unwrap();
let expected_value1 = fds[1] as u64;
check_epoll_wait::<8>(
epfd,
&[(expected_event0, expected_value0), (expected_event1, expected_value1)],
);
check_epoll_wait::<8>(epfd, &[
(expected_event0, expected_value0),
(expected_event1, expected_value1),
]);
// Read from fds[0].
let mut buf: [u8; 5] = [0; 5];
@ -454,10 +454,10 @@ fn test_socketpair_read() {
let expected_value0 = fds[0] as u64;
let expected_event1 = u32::try_from(libc::EPOLLOUT).unwrap();
let expected_value1 = fds[1] as u64;
check_epoll_wait::<8>(
epfd,
&[(expected_event0, expected_value0), (expected_event1, expected_value1)],
);
check_epoll_wait::<8>(epfd, &[
(expected_event0, expected_value0),
(expected_event1, expected_value1),
]);
// Read 3 bytes from fds[0].
let mut buf: [u8; 3] = [0; 3];

View File

@ -5,7 +5,7 @@
#![feature(io_error_uncategorized)]
use std::ffi::{CStr, CString, OsString};
use std::fs::{canonicalize, remove_file, File};
use std::fs::{File, canonicalize, remove_file};
use std::io::{Error, ErrorKind, Write};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::AsRawFd;
@ -169,7 +169,7 @@ fn test_ftruncate<T: From<i32>>(
#[cfg(target_os = "linux")]
fn test_o_tmpfile_flag() {
use std::fs::{create_dir, OpenOptions};
use std::fs::{OpenOptions, create_dir};
use std::os::unix::fs::OpenOptionsExt;
let dir_path = utils::prepare_dir("miri_test_fs_dir");
create_dir(&dir_path).unwrap();

View File

@ -1,6 +1,6 @@
//@only-target: linux # We only support tokio on Linux
use tokio::time::{sleep, Duration, Instant};
use tokio::time::{Duration, Instant, sleep};
#[tokio::main]
async fn main() {

View File

@ -22,7 +22,7 @@
// Available: https://ss265.host.cs.st-andrews.ac.uk/papers/n3132.pdf.
use std::sync::atomic::Ordering::*;
use std::sync::atomic::{fence, AtomicBool, AtomicI32};
use std::sync::atomic::{AtomicBool, AtomicI32, fence};
use std::thread::spawn;
#[derive(Copy, Clone)]

View File

@ -10,10 +10,10 @@
#![allow(incomplete_features, dead_code)]
// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests
use core::future::{async_drop_in_place, AsyncDrop, Future};
use core::future::{AsyncDrop, Future, async_drop_in_place};
use core::hint::black_box;
use core::mem::{self, ManuallyDrop};
use core::pin::{pin, Pin};
use core::pin::{Pin, pin};
use core::task::{Context, Poll, Waker};
async fn test_async_drop<T>(x: T) {
@ -125,7 +125,10 @@ struct AsyncReference<'a> {
}
impl AsyncDrop for AsyncReference<'_> {
type Dropper<'a> = impl Future<Output = ()> where Self: 'a;
type Dropper<'a>
= impl Future<Output = ()>
where
Self: 'a;
fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> {
async move {

View File

@ -7,7 +7,7 @@
#![allow(static_mut_refs)]
use std::sync::atomic::{
compiler_fence, fence, AtomicBool, AtomicIsize, AtomicPtr, AtomicU64, Ordering::*,
AtomicBool, AtomicIsize, AtomicPtr, AtomicU64, Ordering::*, compiler_fence, fence,
};
fn main() {

View File

@ -11,7 +11,7 @@ use std::{
alloc::{AllocError, Allocator, Layout},
cell::{Cell, UnsafeCell},
mem,
ptr::{self, addr_of, NonNull},
ptr::{self, NonNull, addr_of},
thread::{self, ThreadId},
};

View File

@ -1,4 +1,4 @@
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::panic::{AssertUnwindSafe, catch_unwind};
fn main() {
let mut i = 3;

View File

@ -11,7 +11,7 @@
#![allow(incomplete_features, internal_features)]
use std::intrinsics::simd as intrinsics;
use std::ptr;
use std::simd::{prelude::*, StdFloat};
use std::simd::{StdFloat, prelude::*};
extern "rust-intrinsic" {
#[rustc_nounwind]

View File

@ -2,7 +2,7 @@
#![allow(unconditional_panic, non_fmt_panics)]
use std::cell::Cell;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::process;
thread_local! {

View File

@ -5,7 +5,7 @@
//! that separate threads have their own panicking state.
use std::sync::{Arc, Condvar, Mutex};
use std::thread::{spawn, JoinHandle};
use std::thread::{JoinHandle, spawn};
struct BlockOnDrop(Option<JoinHandle<()>>);

View File

@ -1,5 +1,5 @@
//@compile-flags: -Zmiri-disable-isolation
use std::path::{absolute, Path, PathBuf};
use std::path::{Path, PathBuf, absolute};
#[path = "../utils/mod.rs"]
mod utils;

View File

@ -3,7 +3,7 @@
//@ignore-target: windows # File handling is not implemented yet
//@compile-flags: -Zmiri-disable-isolation
use std::fs::{read_link, remove_file, File};
use std::fs::{File, read_link, remove_file};
use std::io::{Read, Result};
use std::path::Path;

View File

@ -7,8 +7,8 @@
use std::collections::HashMap;
use std::ffi::OsString;
use std::fs::{
canonicalize, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, rename, File,
OpenOptions,
File, OpenOptions, canonicalize, create_dir, read_dir, remove_dir, remove_dir_all, remove_file,
rename,
};
use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write};
use std::path::Path;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
// the RNG and always read the latest value from the store buffer.
use std::sync::atomic::Ordering::*;
use std::sync::atomic::{fence, AtomicUsize};
use std::sync::atomic::{AtomicUsize, fence};
use std::thread::spawn;
#[allow(dead_code)]

View File

@ -12,7 +12,7 @@ use ui_test::custom_flags::edition::Edition;
use ui_test::dependencies::DependencyBuilder;
use ui_test::per_test_config::TestConfig;
use ui_test::spanned::Spanned;
use ui_test::{status_emitter, CommandBuilder, Config, Format, Match, OutputConflictHandling};
use ui_test::{CommandBuilder, Config, Format, Match, OutputConflictHandling, status_emitter};
#[derive(Copy, Clone, Debug)]
enum Mode {
@ -118,24 +118,21 @@ fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) ->
config.comment_defaults.base().add_custom("edition", Edition("2021".into()));
if with_dependencies {
config.comment_defaults.base().set_custom(
"dependencies",
DependencyBuilder {
program: CommandBuilder {
// Set the `cargo-miri` binary, which we expect to be in the same folder as the `miri` binary.
// (It's a separate crate, so we don't get an env var from cargo.)
program: miri_path()
.with_file_name(format!("cargo-miri{}", env::consts::EXE_SUFFIX)),
// There is no `cargo miri build` so we just use `cargo miri run`.
args: ["miri", "run"].into_iter().map(Into::into).collect(),
// Reset `RUSTFLAGS` to work around <https://github.com/rust-lang/rust/pull/119574#issuecomment-1876878344>.
envs: vec![("RUSTFLAGS".into(), None)],
..CommandBuilder::cargo()
},
crate_manifest_path: Path::new("test_dependencies").join("Cargo.toml"),
build_std: None,
config.comment_defaults.base().set_custom("dependencies", DependencyBuilder {
program: CommandBuilder {
// Set the `cargo-miri` binary, which we expect to be in the same folder as the `miri` binary.
// (It's a separate crate, so we don't get an env var from cargo.)
program: miri_path()
.with_file_name(format!("cargo-miri{}", env::consts::EXE_SUFFIX)),
// There is no `cargo miri build` so we just use `cargo miri run`.
args: ["miri", "run"].into_iter().map(Into::into).collect(),
// Reset `RUSTFLAGS` to work around <https://github.com/rust-lang/rust/pull/119574#issuecomment-1876878344>.
envs: vec![("RUSTFLAGS".into(), None)],
..CommandBuilder::cargo()
},
);
crate_manifest_path: Path::new("test_dependencies").join("Cargo.toml"),
build_std: None,
});
}
config
}