mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 00:03:43 +00:00
Auto merge of #130709 - RalfJung:miri-sync, r=RalfJung
Miri subtree update r? `@ghost`
This commit is contained in:
commit
8ed95d1d9e
3
src/tools/miri/.github/workflows/ci.yml
vendored
3
src/tools/miri/.github/workflows/ci.yml
vendored
@ -12,6 +12,9 @@ on:
|
||||
schedule:
|
||||
- cron: '44 4 * * *' # At 4:44 UTC every day.
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
@ -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>,
|
||||
|
@ -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 =
|
||||
|
@ -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 {
|
||||
|
@ -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");
|
||||
|
@ -1 +1 @@
|
||||
e2dc1a1c0f97a90319181a721ab317210307617a
|
||||
6ce376774c0bc46ac8be247bca93ff5a1287a8fc
|
||||
|
@ -1,4 +1,4 @@
|
||||
version = "Two"
|
||||
style_edition = "2024"
|
||||
use_small_heuristics = "Max"
|
||||
match_arm_blocks = false
|
||||
match_arm_leading_pipes = "Preserve"
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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};
|
||||
|
@ -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>> {
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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> {}
|
||||
|
@ -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)]
|
||||
|
@ -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::{
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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> {}
|
||||
|
@ -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" => {
|
||||
|
@ -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::*;
|
||||
|
||||
|
@ -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(())
|
||||
|
@ -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;
|
||||
|
@ -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::*;
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
@ -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)?;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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::*;
|
||||
|
||||
|
@ -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::*;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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::*;
|
||||
|
||||
|
@ -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::*;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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`
|
||||
|
@ -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(|| {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::alloc::{alloc, dealloc, Layout};
|
||||
use std::alloc::{Layout, alloc, dealloc};
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::alloc::{alloc, dealloc, Layout};
|
||||
use std::alloc::{Layout, alloc, dealloc};
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::alloc::{alloc, dealloc, Layout};
|
||||
use std::alloc::{Layout, alloc, dealloc};
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::alloc::{alloc, realloc, Layout};
|
||||
use std::alloc::{Layout, alloc, realloc};
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::alloc::{alloc, realloc, Layout};
|
||||
use std::alloc::{Layout, alloc, realloc};
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::alloc::{alloc, dealloc, realloc, Layout};
|
||||
use std::alloc::{Layout, alloc, dealloc, realloc};
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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] {
|
||||
|
@ -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] {
|
||||
|
@ -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`
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::alloc::{alloc, dealloc, Layout};
|
||||
use std::alloc::{Layout, alloc, dealloc};
|
||||
|
||||
fn main() {
|
||||
unsafe {
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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];
|
||||
|
@ -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();
|
||||
|
@ -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() {
|
||||
|
@ -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)]
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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},
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind};
|
||||
|
||||
fn main() {
|
||||
let mut i = 3;
|
||||
|
@ -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]
|
||||
|
@ -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! {
|
||||
|
@ -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<()>>);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
@ -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)]
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user