format much of Miri
This commit is contained in:
parent
7b3566096c
commit
4e231bab5e
@ -2,8 +2,8 @@ extern crate rustc_driver;
|
||||
extern crate rustc_hir;
|
||||
extern crate rustc_interface;
|
||||
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_driver::Compilation;
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_interface::{interface, Queries};
|
||||
|
||||
use crate::test::Bencher;
|
||||
|
@ -1,11 +1,11 @@
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate rustc_middle;
|
||||
extern crate rustc_driver;
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_hir;
|
||||
extern crate rustc_interface;
|
||||
extern crate rustc_middle;
|
||||
extern crate rustc_session;
|
||||
extern crate rustc_errors;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::env;
|
||||
@ -14,11 +14,11 @@ use std::str::FromStr;
|
||||
use hex::FromHexError;
|
||||
use log::debug;
|
||||
|
||||
use rustc_session::{CtfeBacktrace, config::ErrorOutputType};
|
||||
use rustc_errors::emitter::{HumanReadableErrorType, ColorConfig};
|
||||
use rustc_driver::Compilation;
|
||||
use rustc_errors::emitter::{ColorConfig, HumanReadableErrorType};
|
||||
use rustc_hir::def_id::LOCAL_CRATE;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::{config::ErrorOutputType, CtfeBacktrace};
|
||||
|
||||
struct MiriCompilerCalls {
|
||||
miri_config: miri::MiriConfig,
|
||||
@ -37,8 +37,13 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
|
||||
let (entry_def_id, _) = if let Some((entry_def, x)) = tcx.entry_fn(LOCAL_CRATE) {
|
||||
(entry_def, x)
|
||||
} else {
|
||||
let output_ty = ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(ColorConfig::Auto));
|
||||
rustc_session::early_error(output_ty, "miri can only run programs that have a main function");
|
||||
let output_ty = ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(
|
||||
ColorConfig::Auto,
|
||||
));
|
||||
rustc_session::early_error(
|
||||
output_ty,
|
||||
"miri can only run programs that have a main function",
|
||||
);
|
||||
};
|
||||
let mut config = self.miri_config.clone();
|
||||
|
||||
@ -249,10 +254,7 @@ fn main() {
|
||||
err => panic!("unknown error decoding -Zmiri-seed as hex: {:?}", err),
|
||||
});
|
||||
if seed_raw.len() > 8 {
|
||||
panic!(
|
||||
"-Zmiri-seed must be at most 8 bytes, was {}",
|
||||
seed_raw.len()
|
||||
);
|
||||
panic!("-Zmiri-seed must be at most 8 bytes, was {}", seed_raw.len());
|
||||
}
|
||||
|
||||
let mut bytes = [0; 8];
|
||||
@ -260,17 +262,19 @@ fn main() {
|
||||
miri_config.seed = Some(u64::from_be_bytes(bytes));
|
||||
}
|
||||
arg if arg.starts_with("-Zmiri-env-exclude=") => {
|
||||
miri_config.excluded_env_vars
|
||||
miri_config
|
||||
.excluded_env_vars
|
||||
.push(arg.strip_prefix("-Zmiri-env-exclude=").unwrap().to_owned());
|
||||
}
|
||||
arg if arg.starts_with("-Zmiri-track-pointer-tag=") => {
|
||||
let id: u64 = match arg.strip_prefix("-Zmiri-track-pointer-tag=").unwrap().parse() {
|
||||
Ok(id) => id,
|
||||
Err(err) => panic!(
|
||||
"-Zmiri-track-pointer-tag requires a valid `u64` argument: {}",
|
||||
err
|
||||
),
|
||||
};
|
||||
let id: u64 =
|
||||
match arg.strip_prefix("-Zmiri-track-pointer-tag=").unwrap().parse() {
|
||||
Ok(id) => id,
|
||||
Err(err) => panic!(
|
||||
"-Zmiri-track-pointer-tag requires a valid `u64` argument: {}",
|
||||
err
|
||||
),
|
||||
};
|
||||
if let Some(id) = miri::PtrId::new(id) {
|
||||
miri_config.tracked_pointer_tag = Some(id);
|
||||
} else {
|
||||
@ -280,10 +284,8 @@ fn main() {
|
||||
arg if arg.starts_with("-Zmiri-track-call-id=") => {
|
||||
let id: u64 = match arg.strip_prefix("-Zmiri-track-call-id=").unwrap().parse() {
|
||||
Ok(id) => id,
|
||||
Err(err) => panic!(
|
||||
"-Zmiri-track-call-id requires a valid `u64` argument: {}",
|
||||
err
|
||||
),
|
||||
Err(err) =>
|
||||
panic!("-Zmiri-track-call-id requires a valid `u64` argument: {}", err),
|
||||
};
|
||||
if let Some(id) = miri::CallId::new(id) {
|
||||
miri_config.tracked_call_id = Some(id);
|
||||
@ -292,20 +294,28 @@ fn main() {
|
||||
}
|
||||
}
|
||||
arg if arg.starts_with("-Zmiri-track-alloc-id=") => {
|
||||
let id: u64 = match arg.strip_prefix("-Zmiri-track-alloc-id=").unwrap().parse() {
|
||||
let id: u64 = match arg.strip_prefix("-Zmiri-track-alloc-id=").unwrap().parse()
|
||||
{
|
||||
Ok(id) => id,
|
||||
Err(err) => panic!(
|
||||
"-Zmiri-track-alloc-id requires a valid `u64` argument: {}",
|
||||
err
|
||||
),
|
||||
Err(err) =>
|
||||
panic!("-Zmiri-track-alloc-id requires a valid `u64` argument: {}", err),
|
||||
};
|
||||
miri_config.tracked_alloc_id = Some(miri::AllocId(id));
|
||||
}
|
||||
arg if arg.starts_with("-Zmiri-compare-exchange-weak-failure-rate=") => {
|
||||
let rate = match arg.strip_prefix("-Zmiri-compare-exchange-weak-failure-rate=").unwrap().parse::<f64>() {
|
||||
let rate = match arg
|
||||
.strip_prefix("-Zmiri-compare-exchange-weak-failure-rate=")
|
||||
.unwrap()
|
||||
.parse::<f64>()
|
||||
{
|
||||
Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate,
|
||||
Ok(_) => panic!("-Zmiri-compare-exchange-weak-failure-rate must be between `0.0` and `1.0`"),
|
||||
Err(err) => panic!("-Zmiri-compare-exchange-weak-failure-rate requires a `f64` between `0.0` and `1.0`: {}", err),
|
||||
Ok(_) => panic!(
|
||||
"-Zmiri-compare-exchange-weak-failure-rate must be between `0.0` and `1.0`"
|
||||
),
|
||||
Err(err) => panic!(
|
||||
"-Zmiri-compare-exchange-weak-failure-rate requires a `f64` between `0.0` and `1.0`: {}",
|
||||
err
|
||||
),
|
||||
};
|
||||
miri_config.cmpxchg_weak_failure_rate = rate;
|
||||
}
|
||||
@ -319,5 +329,9 @@ fn main() {
|
||||
|
||||
debug!("rustc arguments: {:?}", rustc_args);
|
||||
debug!("crate arguments: {:?}", miri_config.args);
|
||||
run_compiler(rustc_args, &mut MiriCompilerCalls { miri_config }, /* insert_default_args: */ true)
|
||||
run_compiler(
|
||||
rustc_args,
|
||||
&mut MiriCompilerCalls { miri_config },
|
||||
/* insert_default_args: */ true,
|
||||
)
|
||||
}
|
||||
|
14
src/eval.rs
14
src/eval.rs
@ -135,8 +135,9 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
|
||||
argvs.push(arg_place.ptr);
|
||||
}
|
||||
// Make an array with all these pointers, in the Miri memory.
|
||||
let argvs_layout =
|
||||
ecx.layout_of(tcx.mk_array(tcx.mk_imm_ptr(tcx.types.u8), u64::try_from(argvs.len()).unwrap()))?;
|
||||
let argvs_layout = ecx.layout_of(
|
||||
tcx.mk_array(tcx.mk_imm_ptr(tcx.types.u8), u64::try_from(argvs.len()).unwrap()),
|
||||
)?;
|
||||
let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into());
|
||||
for (idx, arg) in argvs.into_iter().enumerate() {
|
||||
let place = ecx.mplace_field(&argvs_place, idx)?;
|
||||
@ -224,9 +225,11 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
|
||||
assert!(ecx.step()?, "a terminated thread was scheduled for execution");
|
||||
}
|
||||
SchedulingAction::ExecuteTimeoutCallback => {
|
||||
assert!(ecx.machine.communicate,
|
||||
assert!(
|
||||
ecx.machine.communicate,
|
||||
"scheduler callbacks require disabled isolation, but the code \
|
||||
that created the callback did not check it");
|
||||
that created the callback did not check it"
|
||||
);
|
||||
ecx.run_timeout_callback()?;
|
||||
}
|
||||
SchedulingAction::ExecuteDtors => {
|
||||
@ -241,7 +244,8 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
|
||||
}
|
||||
ecx.process_diagnostics(info);
|
||||
}
|
||||
let return_code = ecx.read_scalar(&ret_place.into())?.check_init()?.to_machine_isize(&ecx)?;
|
||||
let return_code =
|
||||
ecx.read_scalar(&ret_place.into())?.check_init()?.to_machine_isize(&ecx)?;
|
||||
Ok(return_code)
|
||||
})();
|
||||
|
||||
|
@ -5,10 +5,10 @@ use std::time::Duration;
|
||||
|
||||
use log::trace;
|
||||
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty::{self, List, TyCtxt, layout::TyAndLayout};
|
||||
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
|
||||
use rustc_target::abi::{LayoutOf, Size, FieldsShape, Variants};
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty::{self, layout::TyAndLayout, List, TyCtxt};
|
||||
use rustc_target::abi::{FieldsShape, LayoutOf, Size, Variants};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use rand::RngCore;
|
||||
@ -19,10 +19,8 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mi
|
||||
|
||||
/// Gets an instance for a path.
|
||||
fn try_resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> Option<DefId> {
|
||||
tcx.crates()
|
||||
.iter()
|
||||
.find(|&&krate| tcx.original_crate_name(krate).as_str() == path[0])
|
||||
.and_then(|krate| {
|
||||
tcx.crates().iter().find(|&&krate| tcx.original_crate_name(krate).as_str() == path[0]).and_then(
|
||||
|krate| {
|
||||
let krate = DefId { krate: *krate, index: CRATE_DEF_INDEX };
|
||||
let mut items = tcx.item_children(krate);
|
||||
let mut path_it = path.iter().skip(1).peekable();
|
||||
@ -40,7 +38,8 @@ fn try_resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> Option<DefId
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
@ -53,10 +52,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
|
||||
/// Evaluates the scalar at the specified path. Returns Some(val)
|
||||
/// if the path could be resolved, and None otherwise
|
||||
fn eval_path_scalar(
|
||||
&mut self,
|
||||
path: &[&str],
|
||||
) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
|
||||
fn eval_path_scalar(&mut self, path: &[&str]) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
|
||||
let this = self.eval_context_mut();
|
||||
let instance = this.resolve_path(path);
|
||||
let cid = GlobalId { instance, promoted: None };
|
||||
@ -67,9 +63,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
|
||||
/// Helper function to get a `libc` constant as a `Scalar`.
|
||||
fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar<Tag>> {
|
||||
self.eval_context_mut()
|
||||
.eval_path_scalar(&["libc", name])?
|
||||
.check_init()
|
||||
self.eval_context_mut().eval_path_scalar(&["libc", name])?.check_init()
|
||||
}
|
||||
|
||||
/// Helper function to get a `libc` constant as an `i32`.
|
||||
@ -101,7 +95,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
/// Helper function to get the `TyAndLayout` of a `windows` type
|
||||
fn windows_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
|
||||
let this = self.eval_context_mut();
|
||||
let ty = this.resolve_path(&["std", "sys", "windows", "c", name]).ty(*this.tcx, ty::ParamEnv::reveal_all());
|
||||
let ty = this
|
||||
.resolve_path(&["std", "sys", "windows", "c", name])
|
||||
.ty(*this.tcx, ty::ParamEnv::reveal_all());
|
||||
this.layout_of(ty)
|
||||
}
|
||||
|
||||
@ -170,7 +166,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let param_env = ty::ParamEnv::reveal_all(); // in Miri this is always the param_env we use... and this.param_env is private.
|
||||
let callee_abi = f.ty(*this.tcx, param_env).fn_sig(*this.tcx).abi();
|
||||
if callee_abi != caller_abi {
|
||||
throw_ub_format!("calling a function with ABI {} using caller ABI {}", callee_abi.name(), caller_abi.name())
|
||||
throw_ub_format!(
|
||||
"calling a function with ABI {} using caller ABI {}",
|
||||
callee_abi.name(),
|
||||
caller_abi.name()
|
||||
)
|
||||
}
|
||||
|
||||
// Push frame.
|
||||
@ -181,9 +181,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let mut callee_args = this.frame().body.args_iter();
|
||||
for arg in args {
|
||||
let callee_arg = this.local_place(
|
||||
callee_args.next().ok_or_else(||
|
||||
err_ub_format!("callee has fewer arguments than expected")
|
||||
)?
|
||||
callee_args
|
||||
.next()
|
||||
.ok_or_else(|| err_ub_format!("callee has fewer arguments than expected"))?,
|
||||
)?;
|
||||
this.write_immediate(*arg, &callee_arg)?;
|
||||
}
|
||||
@ -356,7 +356,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_union(&mut self, _v: &MPlaceTy<'tcx, Tag>, _fields: NonZeroUsize) -> InterpResult<'tcx> {
|
||||
fn visit_union(
|
||||
&mut self,
|
||||
_v: &MPlaceTy<'tcx, Tag>,
|
||||
_fields: NonZeroUsize,
|
||||
) -> InterpResult<'tcx> {
|
||||
bug!("we should have already handled unions in `visit_value`")
|
||||
}
|
||||
}
|
||||
@ -465,12 +469,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
})?
|
||||
} else if target.families.contains(&"windows".to_owned()) {
|
||||
// FIXME: we have to finish implementing the Windows equivalent of this.
|
||||
this.eval_windows("c", match e.kind() {
|
||||
NotFound => "ERROR_FILE_NOT_FOUND",
|
||||
_ => throw_unsup_format!("io error {} cannot be transformed into a raw os error", e)
|
||||
})?
|
||||
this.eval_windows(
|
||||
"c",
|
||||
match e.kind() {
|
||||
NotFound => "ERROR_FILE_NOT_FOUND",
|
||||
_ => throw_unsup_format!(
|
||||
"io error {} cannot be transformed into a raw os error",
|
||||
e
|
||||
),
|
||||
},
|
||||
)?
|
||||
} else {
|
||||
throw_unsup_format!("setting the last OS error from an io::Error is unsupported for {}.", target_os)
|
||||
throw_unsup_format!(
|
||||
"setting the last OS error from an io::Error is unsupported for {}.",
|
||||
target_os
|
||||
)
|
||||
};
|
||||
this.set_last_error(last_error)
|
||||
}
|
||||
@ -556,8 +569,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
|
||||
/// Check that the number of args is what we expect.
|
||||
pub fn check_arg_count<'a, 'tcx, const N: usize>(args: &'a [OpTy<'tcx, Tag>]) -> InterpResult<'tcx, &'a [OpTy<'tcx, Tag>; N]>
|
||||
where &'a [OpTy<'tcx, Tag>; N]: TryFrom<&'a [OpTy<'tcx, Tag>]> {
|
||||
pub fn check_arg_count<'a, 'tcx, const N: usize>(
|
||||
args: &'a [OpTy<'tcx, Tag>],
|
||||
) -> InterpResult<'tcx, &'a [OpTy<'tcx, Tag>; N]>
|
||||
where
|
||||
&'a [OpTy<'tcx, Tag>; N]: TryFrom<&'a [OpTy<'tcx, Tag>]>,
|
||||
{
|
||||
if let Ok(ops) = args.try_into() {
|
||||
return Ok(ops);
|
||||
}
|
||||
@ -569,7 +586,11 @@ pub fn check_abi<'a>(abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> {
|
||||
if abi == exp_abi {
|
||||
Ok(())
|
||||
} else {
|
||||
throw_ub_format!("calling a function with ABI {} using caller ABI {}", exp_abi.name(), abi.name())
|
||||
throw_ub_format!(
|
||||
"calling a function with ABI {} using caller ABI {}",
|
||||
exp_abi.name(),
|
||||
abi.name()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ use log::trace;
|
||||
use rand::Rng;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_target::abi::{Size, HasDataLayout};
|
||||
use rustc_target::abi::{HasDataLayout, Size};
|
||||
|
||||
use crate::*;
|
||||
|
||||
|
22
src/lib.rs
22
src/lib.rs
@ -3,13 +3,13 @@
|
||||
#![feature(map_try_insert)]
|
||||
#![feature(never_type)]
|
||||
#![feature(try_blocks)]
|
||||
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![allow(clippy::cast_lossless)]
|
||||
|
||||
extern crate rustc_apfloat;
|
||||
extern crate rustc_ast;
|
||||
#[macro_use] extern crate rustc_middle;
|
||||
#[macro_use]
|
||||
extern crate rustc_middle;
|
||||
extern crate rustc_data_structures;
|
||||
extern crate rustc_hir;
|
||||
extern crate rustc_index;
|
||||
@ -44,18 +44,18 @@ pub use crate::shims::env::{EnvVars, EvalContextExt as _};
|
||||
pub use crate::shims::foreign_items::EvalContextExt as _;
|
||||
pub use crate::shims::intrinsics::EvalContextExt as _;
|
||||
pub use crate::shims::os_str::EvalContextExt as _;
|
||||
pub use crate::shims::time::EvalContextExt as _;
|
||||
pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
|
||||
pub use crate::shims::time::EvalContextExt as _;
|
||||
pub use crate::shims::tls::{EvalContextExt as _, TlsData};
|
||||
pub use crate::shims::EvalContextExt as _;
|
||||
|
||||
pub use crate::data_race::{
|
||||
AtomicReadOp, AtomicWriteOp, AtomicRwOp, AtomicFenceOp,
|
||||
EvalContextExt as DataRaceEvalContextExt
|
||||
AtomicFenceOp, AtomicReadOp, AtomicRwOp, AtomicWriteOp,
|
||||
EvalContextExt as DataRaceEvalContextExt,
|
||||
};
|
||||
pub use crate::diagnostics::{
|
||||
register_diagnostic, report_error, EvalContextExt as DiagnosticsEvalContextExt,
|
||||
TerminationInfo, NonHaltingDiagnostic,
|
||||
NonHaltingDiagnostic, TerminationInfo,
|
||||
};
|
||||
pub use crate::eval::{create_ecx, eval_main, AlignmentCheck, MiriConfig};
|
||||
pub use crate::helpers::EvalContextExt as HelpersEvalContextExt;
|
||||
@ -67,17 +67,13 @@ pub use crate::mono_hash_map::MonoHashMap;
|
||||
pub use crate::operator::EvalContextExt as OperatorEvalContextExt;
|
||||
pub use crate::range_map::RangeMap;
|
||||
pub use crate::stacked_borrows::{
|
||||
EvalContextExt as StackedBorEvalContextExt, Item, Permission, CallId, PtrId, Stack, Stacks, Tag,
|
||||
CallId, EvalContextExt as StackedBorEvalContextExt, Item, Permission, PtrId, Stack, Stacks, Tag,
|
||||
};
|
||||
pub use crate::sync::{CondvarId, EvalContextExt as SyncEvalContextExt, MutexId, RwLockId};
|
||||
pub use crate::thread::{
|
||||
EvalContextExt as ThreadsEvalContextExt, SchedulingAction, ThreadId, ThreadManager, ThreadState,
|
||||
};
|
||||
pub use crate::sync::{
|
||||
EvalContextExt as SyncEvalContextExt, CondvarId, MutexId, RwLockId
|
||||
};
|
||||
pub use crate::vector_clock::{
|
||||
VClock, VectorIdx, VTimestamp
|
||||
};
|
||||
pub use crate::vector_clock::{VClock, VTimestamp, VectorIdx};
|
||||
|
||||
/// Insert rustc arguments at the beginning of the argument list that Miri wants to be
|
||||
/// set per default, for maximal validation power.
|
||||
|
@ -3,10 +3,10 @@
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
use std::num::NonZeroU64;
|
||||
use std::rc::Rc;
|
||||
use std::time::Instant;
|
||||
use std::fmt;
|
||||
|
||||
use log::trace;
|
||||
use rand::rngs::StdRng;
|
||||
@ -21,8 +21,8 @@ use rustc_middle::{
|
||||
TyCtxt,
|
||||
},
|
||||
};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_target::abi::{LayoutOf, Size};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
@ -100,7 +100,7 @@ impl fmt::Display for MiriMemoryKind {
|
||||
Env => write!(f, "environment variable"),
|
||||
Global => write!(f, "global (static or const)"),
|
||||
ExternStatic => write!(f, "extern static"),
|
||||
Tls => write!(f, "thread-local static"),
|
||||
Tls => write!(f, "thread-local static"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -176,11 +176,7 @@ impl MemoryExtra {
|
||||
) {
|
||||
let ptr = ptr.assert_ptr();
|
||||
assert_eq!(ptr.offset, Size::ZERO);
|
||||
this.memory
|
||||
.extra
|
||||
.extern_statics
|
||||
.try_insert(Symbol::intern(name), ptr.alloc_id)
|
||||
.unwrap();
|
||||
this.memory.extra.extern_statics.try_insert(Symbol::intern(name), ptr.alloc_id).unwrap();
|
||||
}
|
||||
|
||||
/// Sets up the "extern statics" for this machine.
|
||||
@ -196,7 +192,11 @@ impl MemoryExtra {
|
||||
this.write_scalar(Scalar::from_machine_usize(0, this), &place.into())?;
|
||||
Self::add_extern_static(this, "__cxa_thread_atexit_impl", place.ptr);
|
||||
// "environ"
|
||||
Self::add_extern_static(this, "environ", this.machine.env_vars.environ.unwrap().ptr);
|
||||
Self::add_extern_static(
|
||||
this,
|
||||
"environ",
|
||||
this.machine.env_vars.environ.unwrap().ptr,
|
||||
);
|
||||
}
|
||||
"windows" => {
|
||||
// "_tls_used"
|
||||
@ -282,8 +282,8 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
|
||||
validate: bool,
|
||||
layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
|
||||
) -> Self {
|
||||
let layouts = PrimitiveLayouts::new(layout_cx)
|
||||
.expect("Couldn't get layouts of primitive types");
|
||||
let layouts =
|
||||
PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
|
||||
Evaluator {
|
||||
// `env_vars` could be initialized properly here if `Memory` were available before
|
||||
// calling this method.
|
||||
@ -476,15 +476,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
|
||||
|
||||
let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
|
||||
let alloc = alloc.into_owned();
|
||||
let (stacks, base_tag) =
|
||||
if let Some(stacked_borrows) = &memory_extra.stacked_borrows {
|
||||
let (stacks, base_tag) =
|
||||
Stacks::new_allocation(id, alloc.size, Rc::clone(stacked_borrows), kind);
|
||||
(Some(stacks), base_tag)
|
||||
} else {
|
||||
// No stacks, no tag.
|
||||
(None, Tag::Untagged)
|
||||
};
|
||||
let (stacks, base_tag) = if let Some(stacked_borrows) = &memory_extra.stacked_borrows {
|
||||
let (stacks, base_tag) =
|
||||
Stacks::new_allocation(id, alloc.size, Rc::clone(stacked_borrows), kind);
|
||||
(Some(stacks), base_tag)
|
||||
} else {
|
||||
// No stacks, no tag.
|
||||
(None, Tag::Untagged)
|
||||
};
|
||||
let race_alloc = if let Some(data_race) = &memory_extra.data_race {
|
||||
Some(data_race::AllocExtra::new_allocation(&data_race, alloc.size, kind))
|
||||
} else {
|
||||
@ -518,7 +517,6 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn after_static_mem_initialized(
|
||||
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||
ptr: Pointer<Self::PointerTag>,
|
||||
@ -545,11 +543,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
|
||||
kind: mir::RetagKind,
|
||||
place: &PlaceTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
if ecx.memory.extra.stacked_borrows.is_some() {
|
||||
ecx.retag(kind, place)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
if ecx.memory.extra.stacked_borrows.is_some() { ecx.retag(kind, place) } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -566,24 +560,20 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
|
||||
}
|
||||
|
||||
fn stack<'a>(
|
||||
ecx: &'a InterpCx<'mir, 'tcx, Self>
|
||||
ecx: &'a InterpCx<'mir, 'tcx, Self>,
|
||||
) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
|
||||
ecx.active_thread_stack()
|
||||
}
|
||||
|
||||
fn stack_mut<'a>(
|
||||
ecx: &'a mut InterpCx<'mir, 'tcx, Self>
|
||||
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
|
||||
) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
|
||||
ecx.active_thread_stack_mut()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn after_stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
|
||||
if ecx.memory.extra.stacked_borrows.is_some() {
|
||||
ecx.retag_return_place()
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
if ecx.memory.extra.stacked_borrows.is_some() { ecx.retag_return_place() } else { Ok(()) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -77,7 +77,10 @@ impl<T> RangeMap<T> {
|
||||
};
|
||||
// The first offset that is not included any more.
|
||||
let end = offset + len;
|
||||
slice.iter().take_while(move |elem| elem.range.start < end).map(|elem| (Size::from_bytes(elem.range.start), &elem.data))
|
||||
slice
|
||||
.iter()
|
||||
.take_while(move |elem| elem.range.start < end)
|
||||
.map(|elem| (Size::from_bytes(elem.range.start), &elem.data))
|
||||
}
|
||||
|
||||
pub fn iter_mut_all<'a>(&'a mut self) -> impl Iterator<Item = &'a mut T> + 'a {
|
||||
@ -213,7 +216,9 @@ mod tests {
|
||||
fn to_vec<T: Copy>(map: &RangeMap<T>, offset: u64, len: u64) -> Vec<T> {
|
||||
(offset..offset + len)
|
||||
.into_iter()
|
||||
.map(|i| map.iter(Size::from_bytes(i), Size::from_bytes(1)).next().map(|(_, &t)| t).unwrap())
|
||||
.map(|i| {
|
||||
map.iter(Size::from_bytes(i), Size::from_bytes(1)).next().map(|(_, &t)| t).unwrap()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -267,7 +272,9 @@ mod tests {
|
||||
assert_eq!(to_vec(&map, 10, 10), vec![23, 42, 23, 23, 23, 19, 19, 19, 19, 19]);
|
||||
// Should be seeing two blocks with 19.
|
||||
assert_eq!(
|
||||
map.iter(Size::from_bytes(15), Size::from_bytes(2)).map(|(_, &t)| t).collect::<Vec<_>>(),
|
||||
map.iter(Size::from_bytes(15), Size::from_bytes(2))
|
||||
.map(|(_, &t)| t)
|
||||
.collect::<Vec<_>>(),
|
||||
vec![19, 19]
|
||||
);
|
||||
|
||||
|
@ -1,19 +1,18 @@
|
||||
use crate::rustc_target::abi::LayoutOf as _;
|
||||
use crate::*;
|
||||
use helpers::check_arg_count;
|
||||
use rustc_middle::ty::{self, TypeAndMut};
|
||||
use rustc_ast::ast::Mutability;
|
||||
use rustc_middle::ty::{self, TypeAndMut};
|
||||
use rustc_span::BytePos;
|
||||
use rustc_target::abi::Size;
|
||||
use std::convert::TryInto as _;
|
||||
use crate::rustc_target::abi::LayoutOf as _;
|
||||
|
||||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
|
||||
fn handle_miri_get_backtrace(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, Tag>],
|
||||
dest: &PlaceTy<'tcx, Tag>
|
||||
dest: &PlaceTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let tcx = this.tcx;
|
||||
@ -35,24 +34,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
data.push((frame.instance, span.lo()));
|
||||
}
|
||||
|
||||
let ptrs: Vec<_> = data.into_iter().map(|(instance, pos)| {
|
||||
// We represent a frame pointer by using the `span.lo` value
|
||||
// as an offset into the function's allocation. This gives us an
|
||||
// opaque pointer that we can return to user code, and allows us
|
||||
// to reconstruct the needed frame information in `handle_miri_resolve_frame`.
|
||||
// Note that we never actually read or write anything from/to this pointer -
|
||||
// all of the data is represented by the pointer value itself.
|
||||
let mut fn_ptr = this.memory.create_fn_alloc(FnVal::Instance(instance));
|
||||
fn_ptr.offset = Size::from_bytes(pos.0);
|
||||
Scalar::Ptr(fn_ptr)
|
||||
}).collect();
|
||||
let ptrs: Vec<_> = data
|
||||
.into_iter()
|
||||
.map(|(instance, pos)| {
|
||||
// We represent a frame pointer by using the `span.lo` value
|
||||
// as an offset into the function's allocation. This gives us an
|
||||
// opaque pointer that we can return to user code, and allows us
|
||||
// to reconstruct the needed frame information in `handle_miri_resolve_frame`.
|
||||
// Note that we never actually read or write anything from/to this pointer -
|
||||
// all of the data is represented by the pointer value itself.
|
||||
let mut fn_ptr = this.memory.create_fn_alloc(FnVal::Instance(instance));
|
||||
fn_ptr.offset = Size::from_bytes(pos.0);
|
||||
Scalar::Ptr(fn_ptr)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let len = ptrs.len();
|
||||
|
||||
let ptr_ty = tcx.mk_ptr(TypeAndMut {
|
||||
ty: tcx.types.unit,
|
||||
mutbl: Mutability::Mut
|
||||
});
|
||||
let ptr_ty = tcx.mk_ptr(TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut });
|
||||
|
||||
let array_ty = tcx.mk_array(ptr_ty, ptrs.len().try_into().unwrap());
|
||||
|
||||
@ -63,14 +62,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
this.write_immediate_to_mplace(ptr.into(), &place)?;
|
||||
}
|
||||
|
||||
this.write_immediate(Immediate::new_slice(alloc.ptr.into(), len.try_into().unwrap(), this), dest)?;
|
||||
this.write_immediate(
|
||||
Immediate::new_slice(alloc.ptr.into(), len.try_into().unwrap(), this),
|
||||
dest,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_miri_resolve_frame(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, Tag>],
|
||||
dest: &PlaceTy<'tcx, Tag>
|
||||
dest: &PlaceTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let tcx = this.tcx;
|
||||
@ -83,7 +85,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
|
||||
let ptr = this.force_ptr(this.read_scalar(ptr)?.check_init()?)?;
|
||||
|
||||
let fn_instance = if let Some(GlobalAlloc::Function(instance)) = this.tcx.get_global_alloc(ptr.alloc_id) {
|
||||
let fn_instance = if let Some(GlobalAlloc::Function(instance)) =
|
||||
this.tcx.get_global_alloc(ptr.alloc_id)
|
||||
{
|
||||
instance
|
||||
} else {
|
||||
throw_ub_format!("expected function pointer, found {:?}", ptr);
|
||||
@ -100,7 +104,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
if !(4..=5).contains(&num_fields) {
|
||||
// Always mention 5 fields, since the 4-field struct
|
||||
// is deprecated and slated for removal.
|
||||
throw_ub_format!("bad declaration of miri_resolve_frame - should return a struct with 5 fields");
|
||||
throw_ub_format!(
|
||||
"bad declaration of miri_resolve_frame - should return a struct with 5 fields"
|
||||
);
|
||||
}
|
||||
|
||||
let pos = BytePos(ptr.offset.bytes().try_into().unwrap());
|
||||
@ -121,7 +127,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let dest = this.force_allocation(dest)?;
|
||||
if let ty::Adt(adt, _) = dest.layout.ty.kind() {
|
||||
if !adt.repr.c() {
|
||||
throw_ub_format!("miri_resolve_frame must be declared with a `#[repr(C)]` return type");
|
||||
throw_ub_format!(
|
||||
"miri_resolve_frame must be declared with a `#[repr(C)]` return type"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let this = self.eval_context_mut();
|
||||
match dlsym {
|
||||
Dlsym::Posix(dlsym) => posix::EvalContextExt::call_dlsym(this, dlsym, abi, args, ret),
|
||||
Dlsym::Windows(dlsym) => windows::EvalContextExt::call_dlsym(this, dlsym, abi, args, ret),
|
||||
Dlsym::Windows(dlsym) =>
|
||||
windows::EvalContextExt::call_dlsym(this, dlsym, abi, args, ret),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
105
src/shims/env.rs
105
src/shims/env.rs
@ -1,10 +1,10 @@
|
||||
use std::ffi::{OsString, OsStr};
|
||||
use std::env;
|
||||
use std::convert::TryFrom;
|
||||
use std::env;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
|
||||
use rustc_target::abi::{Size, LayoutOf};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_mir::interpret::Pointer;
|
||||
use rustc_target::abi::{LayoutOf, Size};
|
||||
|
||||
use crate::*;
|
||||
|
||||
@ -49,9 +49,13 @@ impl<'tcx> EnvVars<'tcx> {
|
||||
for (name, value) in env::vars() {
|
||||
if !excluded_env_vars.contains(&name) {
|
||||
let var_ptr = match target_os {
|
||||
"linux" | "macos" => alloc_env_var_as_c_str(name.as_ref(), value.as_ref(), ecx)?,
|
||||
"linux" | "macos" =>
|
||||
alloc_env_var_as_c_str(name.as_ref(), value.as_ref(), ecx)?,
|
||||
"windows" => alloc_env_var_as_wide_str(name.as_ref(), value.as_ref(), ecx)?,
|
||||
unsupported => throw_unsup_format!("environment support for target OS `{}` not yet available", unsupported),
|
||||
unsupported => throw_unsup_format!(
|
||||
"environment support for target OS `{}` not yet available",
|
||||
unsupported
|
||||
),
|
||||
};
|
||||
ecx.machine.env_vars.map.insert(OsString::from(name), var_ptr);
|
||||
}
|
||||
@ -102,14 +106,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
fn getenv(&mut self, name_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, Scalar<Tag>> {
|
||||
let this = self.eval_context_mut();
|
||||
let target_os = &this.tcx.sess.target.os;
|
||||
assert!(target_os == "linux" || target_os == "macos", "`getenv` is only available for the UNIX target family");
|
||||
assert!(
|
||||
target_os == "linux" || target_os == "macos",
|
||||
"`getenv` is only available for the UNIX target family"
|
||||
);
|
||||
|
||||
let name_ptr = this.read_scalar(name_op)?.check_init()?;
|
||||
let name = this.read_os_str_from_c_str(name_ptr)?;
|
||||
Ok(match this.machine.env_vars.map.get(name) {
|
||||
Some(var_ptr) => {
|
||||
// The offset is used to strip the "{name}=" part of the string.
|
||||
Scalar::from(var_ptr.offset(Size::from_bytes(u64::try_from(name.len()).unwrap().checked_add(1).unwrap()), this)?)
|
||||
Scalar::from(var_ptr.offset(
|
||||
Size::from_bytes(u64::try_from(name.len()).unwrap().checked_add(1).unwrap()),
|
||||
this,
|
||||
)?)
|
||||
}
|
||||
None => Scalar::null_ptr(&*this.tcx),
|
||||
})
|
||||
@ -118,10 +128,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
#[allow(non_snake_case)]
|
||||
fn GetEnvironmentVariableW(
|
||||
&mut self,
|
||||
name_op: &OpTy<'tcx, Tag>, // LPCWSTR
|
||||
buf_op: &OpTy<'tcx, Tag>, // LPWSTR
|
||||
size_op: &OpTy<'tcx, Tag>, // DWORD
|
||||
) -> InterpResult<'tcx, u32> { // Returns DWORD (u32 in Windows)
|
||||
name_op: &OpTy<'tcx, Tag>, // LPCWSTR
|
||||
buf_op: &OpTy<'tcx, Tag>, // LPWSTR
|
||||
size_op: &OpTy<'tcx, Tag>, // DWORD
|
||||
) -> InterpResult<'tcx, u32> {
|
||||
// ^ Returns DWORD (u32 on Windows)
|
||||
|
||||
let this = self.eval_context_mut();
|
||||
this.assert_target_os("windows", "GetEnvironmentVariableW");
|
||||
|
||||
@ -130,9 +142,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
Ok(match this.machine.env_vars.map.get(&name) {
|
||||
Some(var_ptr) => {
|
||||
// The offset is used to strip the "{name}=" part of the string.
|
||||
let name_offset_bytes =
|
||||
u64::try_from(name.len()).unwrap().checked_add(1).unwrap().checked_mul(2).unwrap();
|
||||
let var_ptr = Scalar::from(var_ptr.offset(Size::from_bytes(name_offset_bytes), this)?);
|
||||
#[rustfmt::skip]
|
||||
let name_offset_bytes = u64::try_from(name.len()).unwrap()
|
||||
.checked_add(1).unwrap()
|
||||
.checked_mul(2).unwrap();
|
||||
let var_ptr =
|
||||
Scalar::from(var_ptr.offset(Size::from_bytes(name_offset_bytes), this)?);
|
||||
let var = this.read_os_str_from_wide_str(var_ptr)?;
|
||||
|
||||
let buf_ptr = this.read_scalar(buf_op)?.check_init()?;
|
||||
@ -169,12 +184,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn FreeEnvironmentStringsW(&mut self, env_block_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
|
||||
fn FreeEnvironmentStringsW(
|
||||
&mut self,
|
||||
env_block_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
this.assert_target_os("windows", "FreeEnvironmentStringsW");
|
||||
|
||||
let env_block_ptr = this.read_scalar(env_block_op)?.check_init()?;
|
||||
let result = this.memory.deallocate(this.force_ptr(env_block_ptr)?, None, MiriMemoryKind::Env.into());
|
||||
let result = this.memory.deallocate(
|
||||
this.force_ptr(env_block_ptr)?,
|
||||
None,
|
||||
MiriMemoryKind::Env.into(),
|
||||
);
|
||||
// If the function succeeds, the return value is nonzero.
|
||||
Ok(result.is_ok() as i32)
|
||||
}
|
||||
@ -186,7 +208,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
let mut this = self.eval_context_mut();
|
||||
let target_os = &this.tcx.sess.target.os;
|
||||
assert!(target_os == "linux" || target_os == "macos", "`setenv` is only available for the UNIX target family");
|
||||
assert!(
|
||||
target_os == "linux" || target_os == "macos",
|
||||
"`setenv` is only available for the UNIX target family"
|
||||
);
|
||||
|
||||
let name_ptr = this.read_scalar(name_op)?.check_init()?;
|
||||
let value_ptr = this.read_scalar(value_op)?.check_init()?;
|
||||
@ -202,8 +227,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
if let Some((name, value)) = new {
|
||||
let var_ptr = alloc_env_var_as_c_str(&name, &value, &mut this)?;
|
||||
if let Some(var) = this.machine.env_vars.map.insert(name, var_ptr) {
|
||||
this.memory
|
||||
.deallocate(var, None, MiriMemoryKind::Env.into())?;
|
||||
this.memory.deallocate(var, None, MiriMemoryKind::Env.into())?;
|
||||
}
|
||||
this.update_environ()?;
|
||||
Ok(0) // return zero on success
|
||||
@ -248,8 +272,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let value = this.read_os_str_from_wide_str(value_ptr)?;
|
||||
let var_ptr = alloc_env_var_as_wide_str(&name, &value, &mut this)?;
|
||||
if let Some(var) = this.machine.env_vars.map.insert(name, var_ptr) {
|
||||
this.memory
|
||||
.deallocate(var, None, MiriMemoryKind::Env.into())?;
|
||||
this.memory.deallocate(var, None, MiriMemoryKind::Env.into())?;
|
||||
}
|
||||
this.update_environ()?;
|
||||
Ok(1) // return non-zero on success
|
||||
@ -259,7 +282,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
fn unsetenv(&mut self, name_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
let target_os = &this.tcx.sess.target.os;
|
||||
assert!(target_os == "linux" || target_os == "macos", "`unsetenv` is only available for the UNIX target family");
|
||||
assert!(
|
||||
target_os == "linux" || target_os == "macos",
|
||||
"`unsetenv` is only available for the UNIX target family"
|
||||
);
|
||||
|
||||
let name_ptr = this.read_scalar(name_op)?.check_init()?;
|
||||
let mut success = None;
|
||||
@ -271,8 +297,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
if let Some(old) = success {
|
||||
if let Some(var) = old {
|
||||
this.memory
|
||||
.deallocate(var, None, MiriMemoryKind::Env.into())?;
|
||||
this.memory.deallocate(var, None, MiriMemoryKind::Env.into())?;
|
||||
}
|
||||
this.update_environ()?;
|
||||
Ok(0)
|
||||
@ -291,7 +316,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
) -> InterpResult<'tcx, Scalar<Tag>> {
|
||||
let this = self.eval_context_mut();
|
||||
let target_os = &this.tcx.sess.target.os;
|
||||
assert!(target_os == "linux" || target_os == "macos", "`getcwd` is only available for the UNIX target family");
|
||||
assert!(
|
||||
target_os == "linux" || target_os == "macos",
|
||||
"`getcwd` is only available for the UNIX target family"
|
||||
);
|
||||
|
||||
this.check_no_isolation("`getcwd`")?;
|
||||
|
||||
@ -337,7 +365,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
fn chdir(&mut self, path_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
let target_os = &this.tcx.sess.target.os;
|
||||
assert!(target_os == "linux" || target_os == "macos", "`getcwd` is only available for the UNIX target family");
|
||||
assert!(
|
||||
target_os == "linux" || target_os == "macos",
|
||||
"`getcwd` is only available for the UNIX target family"
|
||||
);
|
||||
|
||||
this.check_no_isolation("`chdir`")?;
|
||||
|
||||
@ -353,10 +384,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn SetCurrentDirectoryW (
|
||||
fn SetCurrentDirectoryW(
|
||||
&mut self,
|
||||
path_op: &OpTy<'tcx, Tag> // LPCTSTR
|
||||
) -> InterpResult<'tcx, i32> { // Returns BOOL (i32 in Windows)
|
||||
path_op: &OpTy<'tcx, Tag>, // LPCTSTR
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
// ^ Returns BOOL (i32 on Windows)
|
||||
|
||||
let this = self.eval_context_mut();
|
||||
this.assert_target_os("windows", "SetCurrentDirectoryW");
|
||||
|
||||
@ -380,7 +413,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
// Deallocate the old environ list, if any.
|
||||
if let Some(environ) = this.machine.env_vars.environ {
|
||||
let old_vars_ptr = this.read_scalar(&environ.into())?.check_init()?;
|
||||
this.memory.deallocate(this.force_ptr(old_vars_ptr)?, None, MiriMemoryKind::Env.into())?;
|
||||
this.memory.deallocate(
|
||||
this.force_ptr(old_vars_ptr)?,
|
||||
None,
|
||||
MiriMemoryKind::Env.into(),
|
||||
)?;
|
||||
} else {
|
||||
// No `environ` allocated yet, let's do that.
|
||||
// This is memory backing an extern static, hence `ExternStatic`, not `Env`.
|
||||
@ -390,7 +427,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
|
||||
// Collect all the pointers to each variable in a vector.
|
||||
let mut vars: Vec<Scalar<Tag>> = this.machine.env_vars.map.values().map(|&ptr| ptr.into()).collect();
|
||||
let mut vars: Vec<Scalar<Tag>> =
|
||||
this.machine.env_vars.map.values().map(|&ptr| ptr.into()).collect();
|
||||
// Add the trailing null pointer.
|
||||
vars.push(Scalar::null_ptr(this));
|
||||
// Make an array with all these pointers inside Miri.
|
||||
@ -402,10 +440,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let place = this.mplace_field(&vars_place, idx)?;
|
||||
this.write_scalar(var, &place.into())?;
|
||||
}
|
||||
this.write_scalar(
|
||||
vars_place.ptr,
|
||||
&this.machine.env_vars.environ.unwrap().into(),
|
||||
)?;
|
||||
this.write_scalar(vars_place.ptr, &this.machine.env_vars.environ.unwrap().into())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,16 +1,22 @@
|
||||
use std::{convert::{TryInto, TryFrom}, iter};
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
iter,
|
||||
};
|
||||
|
||||
use log::trace;
|
||||
|
||||
use rustc_apfloat::Float;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::mir;
|
||||
use rustc_target::{abi::{Align, Size}, spec::{PanicStrategy, abi::Abi}};
|
||||
use rustc_middle::ty;
|
||||
use rustc_apfloat::Float;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_target::{
|
||||
abi::{Align, Size},
|
||||
spec::{abi::Abi, PanicStrategy},
|
||||
};
|
||||
|
||||
use crate::*;
|
||||
use super::backtrace::EvalContextExt as _;
|
||||
use crate::*;
|
||||
use helpers::{check_abi, check_arg_count};
|
||||
|
||||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
|
@ -74,8 +74,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let req_align = this
|
||||
.force_bits(this.read_scalar(align_op)?.check_init()?, this.pointer_size())?;
|
||||
let req_align =
|
||||
this.force_bits(this.read_scalar(align_op)?.check_init()?, this.pointer_size())?;
|
||||
|
||||
// Stop if the alignment is not a power of two.
|
||||
if !req_align.is_power_of_two() {
|
||||
|
@ -48,7 +48,6 @@ pub fn bytes_to_os_str<'a, 'tcx>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsSt
|
||||
|
||||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
|
||||
/// Helper function to read an OsString from a null-terminated sequence of bytes, which is what
|
||||
/// the Unix APIs usually handle.
|
||||
fn read_os_str_from_c_str<'a>(&'a self, scalar: Scalar<Tag>) -> InterpResult<'tcx, &'a OsStr>
|
||||
@ -94,7 +93,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
scalar: Scalar<Tag>,
|
||||
size: u64,
|
||||
) -> InterpResult<'tcx, (bool, u64)> {
|
||||
|
||||
let bytes = os_str_to_bytes(os_str)?;
|
||||
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
|
||||
// terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
|
||||
@ -199,7 +197,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let this = self.eval_context_ref();
|
||||
let os_str = this.read_os_str_from_wide_str(scalar)?;
|
||||
|
||||
Ok(this.convert_path_separator(Cow::Owned(os_str), PathConversion::TargetToHost).into_owned().into())
|
||||
Ok(this
|
||||
.convert_path_separator(Cow::Owned(os_str), PathConversion::TargetToHost)
|
||||
.into_owned()
|
||||
.into())
|
||||
}
|
||||
|
||||
/// Write a Path to the machine memory (as a null-terminated sequence of bytes),
|
||||
@ -211,7 +212,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
size: u64,
|
||||
) -> InterpResult<'tcx, (bool, u64)> {
|
||||
let this = self.eval_context_mut();
|
||||
let os_str = this.convert_path_separator(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
|
||||
let os_str = this
|
||||
.convert_path_separator(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
|
||||
this.write_os_str_to_c_str(&os_str, scalar, size)
|
||||
}
|
||||
|
||||
@ -224,7 +226,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
size: u64,
|
||||
) -> InterpResult<'tcx, (bool, u64)> {
|
||||
let this = self.eval_context_mut();
|
||||
let os_str = this.convert_path_separator(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
|
||||
let os_str = this
|
||||
.convert_path_separator(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget);
|
||||
this.write_os_str_to_wide_str(&os_str, scalar, size)
|
||||
}
|
||||
|
||||
@ -270,4 +273,3 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,8 @@
|
||||
use log::trace;
|
||||
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
use crate::*;
|
||||
use helpers::check_arg_count;
|
||||
@ -54,10 +54,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let &[ref payload] = check_arg_count(args)?;
|
||||
let payload = this.read_scalar(payload)?.check_init()?;
|
||||
let thread = this.active_thread_mut();
|
||||
assert!(
|
||||
thread.panic_payload.is_none(),
|
||||
"the panic runtime should avoid double-panics"
|
||||
);
|
||||
assert!(thread.panic_payload.is_none(), "the panic runtime should avoid double-panics");
|
||||
thread.panic_payload = Some(payload);
|
||||
|
||||
// Jump to the unwind block to begin unwinding.
|
||||
@ -111,7 +108,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
// This lets `handle_stack_pop` (below) know that we should stop unwinding
|
||||
// when we pop this frame.
|
||||
if this.tcx.sess.panic_strategy() == PanicStrategy::Unwind {
|
||||
this.frame_mut().extra.catch_unwind = Some(CatchUnwindData { catch_fn, data, dest: *dest, ret });
|
||||
this.frame_mut().extra.catch_unwind =
|
||||
Some(CatchUnwindData { catch_fn, data, dest: *dest, ret });
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
@ -134,7 +132,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
if let (true, Some(catch_unwind)) = (unwinding, extra.catch_unwind.take()) {
|
||||
// We've just popped a frame that was pushed by `try`,
|
||||
// and we are unwinding, so we should catch that.
|
||||
trace!("unwinding: found catch_panic frame during unwinding: {:?}", this.frame().instance);
|
||||
trace!(
|
||||
"unwinding: found catch_panic frame during unwinding: {:?}",
|
||||
this.frame().instance
|
||||
);
|
||||
|
||||
// We set the return value of `try` to 1, since there was a panic.
|
||||
this.write_scalar(Scalar::from_i32(1), &catch_unwind.dest)?;
|
||||
@ -164,11 +165,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
|
||||
/// Starta a panic in the interpreter with the given message as payload.
|
||||
fn start_panic(
|
||||
&mut self,
|
||||
msg: &str,
|
||||
unwind: Option<mir::BasicBlock>,
|
||||
) -> InterpResult<'tcx> {
|
||||
fn start_panic(&mut self, msg: &str, unwind: Option<mir::BasicBlock>) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
// First arg: message.
|
||||
|
@ -1,21 +1,23 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fs::{read_dir, remove_dir, remove_file, rename, DirBuilder, File, FileType, OpenOptions, ReadDir};
|
||||
use std::fs::{
|
||||
read_dir, remove_dir, remove_file, rename, DirBuilder, File, FileType, OpenOptions, ReadDir,
|
||||
};
|
||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||
use std::path::Path;
|
||||
use std::time::SystemTime;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use log::trace;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_target::abi::{Align, LayoutOf, Size};
|
||||
use rustc_middle::ty;
|
||||
use rustc_target::abi::{Align, LayoutOf, Size};
|
||||
|
||||
use crate::*;
|
||||
use stacked_borrows::Tag;
|
||||
use helpers::{check_arg_count, immty_from_int_checked, immty_from_uint_checked};
|
||||
use shims::time::system_time_to_duration;
|
||||
use stacked_borrows::Tag;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FileHandle {
|
||||
@ -23,13 +25,28 @@ struct FileHandle {
|
||||
writable: bool,
|
||||
}
|
||||
|
||||
trait FileDescriptor : std::fmt::Debug {
|
||||
trait FileDescriptor: std::fmt::Debug {
|
||||
fn as_file_handle<'tcx>(&self) -> InterpResult<'tcx, &FileHandle>;
|
||||
|
||||
fn read<'tcx>(&mut self, communicate_allowed: bool, bytes: &mut [u8]) -> InterpResult<'tcx, io::Result<usize>>;
|
||||
fn write<'tcx>(&mut self, communicate_allowed: bool, bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>>;
|
||||
fn seek<'tcx>(&mut self, communicate_allowed: bool, offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>>;
|
||||
fn close<'tcx>(self: Box<Self>, _communicate_allowed: bool) -> InterpResult<'tcx, io::Result<i32>>;
|
||||
fn read<'tcx>(
|
||||
&mut self,
|
||||
communicate_allowed: bool,
|
||||
bytes: &mut [u8],
|
||||
) -> InterpResult<'tcx, io::Result<usize>>;
|
||||
fn write<'tcx>(
|
||||
&mut self,
|
||||
communicate_allowed: bool,
|
||||
bytes: &[u8],
|
||||
) -> InterpResult<'tcx, io::Result<usize>>;
|
||||
fn seek<'tcx>(
|
||||
&mut self,
|
||||
communicate_allowed: bool,
|
||||
offset: SeekFrom,
|
||||
) -> InterpResult<'tcx, io::Result<u64>>;
|
||||
fn close<'tcx>(
|
||||
self: Box<Self>,
|
||||
_communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>>;
|
||||
|
||||
fn dup<'tcx>(&mut self) -> io::Result<Box<dyn FileDescriptor>>;
|
||||
}
|
||||
@ -39,22 +56,37 @@ impl FileDescriptor for FileHandle {
|
||||
Ok(&self)
|
||||
}
|
||||
|
||||
fn read<'tcx>(&mut self, communicate_allowed: bool, bytes: &mut [u8]) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
fn read<'tcx>(
|
||||
&mut self,
|
||||
communicate_allowed: bool,
|
||||
bytes: &mut [u8],
|
||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
||||
Ok(self.file.read(bytes))
|
||||
}
|
||||
|
||||
fn write<'tcx>(&mut self, communicate_allowed: bool, bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
fn write<'tcx>(
|
||||
&mut self,
|
||||
communicate_allowed: bool,
|
||||
bytes: &[u8],
|
||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
||||
Ok(self.file.write(bytes))
|
||||
}
|
||||
|
||||
fn seek<'tcx>(&mut self, communicate_allowed: bool, offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
|
||||
fn seek<'tcx>(
|
||||
&mut self,
|
||||
communicate_allowed: bool,
|
||||
offset: SeekFrom,
|
||||
) -> InterpResult<'tcx, io::Result<u64>> {
|
||||
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
||||
Ok(self.file.seek(offset))
|
||||
}
|
||||
|
||||
fn close<'tcx>(self: Box<Self>, communicate_allowed: bool) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
fn close<'tcx>(
|
||||
self: Box<Self>,
|
||||
communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
||||
// We sync the file if it was opened in a mode different than read-only.
|
||||
if self.writable {
|
||||
@ -88,7 +120,11 @@ impl FileDescriptor for io::Stdin {
|
||||
throw_unsup_format!("stdin cannot be used as FileHandle");
|
||||
}
|
||||
|
||||
fn read<'tcx>(&mut self, communicate_allowed: bool, bytes: &mut [u8]) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
fn read<'tcx>(
|
||||
&mut self,
|
||||
communicate_allowed: bool,
|
||||
bytes: &mut [u8],
|
||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
if !communicate_allowed {
|
||||
// We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
|
||||
helpers::isolation_error("`read` from stdin")?;
|
||||
@ -96,15 +132,26 @@ impl FileDescriptor for io::Stdin {
|
||||
Ok(Read::read(self, bytes))
|
||||
}
|
||||
|
||||
fn write<'tcx>(&mut self, _communicate_allowed: bool, _bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
fn write<'tcx>(
|
||||
&mut self,
|
||||
_communicate_allowed: bool,
|
||||
_bytes: &[u8],
|
||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
throw_unsup_format!("cannot write to stdin");
|
||||
}
|
||||
|
||||
fn seek<'tcx>(&mut self, _communicate_allowed: bool, _offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
|
||||
fn seek<'tcx>(
|
||||
&mut self,
|
||||
_communicate_allowed: bool,
|
||||
_offset: SeekFrom,
|
||||
) -> InterpResult<'tcx, io::Result<u64>> {
|
||||
throw_unsup_format!("cannot seek on stdin");
|
||||
}
|
||||
|
||||
fn close<'tcx>(self: Box<Self>, _communicate_allowed: bool) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
fn close<'tcx>(
|
||||
self: Box<Self>,
|
||||
_communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
throw_unsup_format!("stdin cannot be closed");
|
||||
}
|
||||
|
||||
@ -118,11 +165,19 @@ impl FileDescriptor for io::Stdout {
|
||||
throw_unsup_format!("stdout cannot be used as FileHandle");
|
||||
}
|
||||
|
||||
fn read<'tcx>(&mut self, _communicate_allowed: bool, _bytes: &mut [u8]) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
fn read<'tcx>(
|
||||
&mut self,
|
||||
_communicate_allowed: bool,
|
||||
_bytes: &mut [u8],
|
||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
throw_unsup_format!("cannot read from stdout");
|
||||
}
|
||||
|
||||
fn write<'tcx>(&mut self, _communicate_allowed: bool, bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
fn write<'tcx>(
|
||||
&mut self,
|
||||
_communicate_allowed: bool,
|
||||
bytes: &[u8],
|
||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
// We allow writing to stderr even with isolation enabled.
|
||||
let result = Write::write(self, bytes);
|
||||
// Stdout is buffered, flush to make sure it appears on the
|
||||
@ -135,11 +190,18 @@ impl FileDescriptor for io::Stdout {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn seek<'tcx>(&mut self, _communicate_allowed: bool, _offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
|
||||
fn seek<'tcx>(
|
||||
&mut self,
|
||||
_communicate_allowed: bool,
|
||||
_offset: SeekFrom,
|
||||
) -> InterpResult<'tcx, io::Result<u64>> {
|
||||
throw_unsup_format!("cannot seek on stdout");
|
||||
}
|
||||
|
||||
fn close<'tcx>(self: Box<Self>, _communicate_allowed: bool) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
fn close<'tcx>(
|
||||
self: Box<Self>,
|
||||
_communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
throw_unsup_format!("stdout cannot be closed");
|
||||
}
|
||||
|
||||
@ -153,21 +215,36 @@ impl FileDescriptor for io::Stderr {
|
||||
throw_unsup_format!("stderr cannot be used as FileHandle");
|
||||
}
|
||||
|
||||
fn read<'tcx>(&mut self, _communicate_allowed: bool, _bytes: &mut [u8]) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
fn read<'tcx>(
|
||||
&mut self,
|
||||
_communicate_allowed: bool,
|
||||
_bytes: &mut [u8],
|
||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
throw_unsup_format!("cannot read from stderr");
|
||||
}
|
||||
|
||||
fn write<'tcx>(&mut self, _communicate_allowed: bool, bytes: &[u8]) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
fn write<'tcx>(
|
||||
&mut self,
|
||||
_communicate_allowed: bool,
|
||||
bytes: &[u8],
|
||||
) -> InterpResult<'tcx, io::Result<usize>> {
|
||||
// We allow writing to stderr even with isolation enabled.
|
||||
// No need to flush, stderr is not buffered.
|
||||
Ok(Write::write(self, bytes))
|
||||
}
|
||||
|
||||
fn seek<'tcx>(&mut self, _communicate_allowed: bool, _offset: SeekFrom) -> InterpResult<'tcx, io::Result<u64>> {
|
||||
fn seek<'tcx>(
|
||||
&mut self,
|
||||
_communicate_allowed: bool,
|
||||
_offset: SeekFrom,
|
||||
) -> InterpResult<'tcx, io::Result<u64>> {
|
||||
throw_unsup_format!("cannot seek on stderr");
|
||||
}
|
||||
|
||||
fn close<'tcx>(self: Box<Self>, _communicate_allowed: bool) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
fn close<'tcx>(
|
||||
self: Box<Self>,
|
||||
_communicate_allowed: bool,
|
||||
) -> InterpResult<'tcx, io::Result<i32>> {
|
||||
throw_unsup_format!("stderr cannot be closed");
|
||||
}
|
||||
|
||||
@ -187,9 +264,7 @@ impl<'tcx> Default for FileHandler {
|
||||
handles.insert(0i32, Box::new(io::stdin()));
|
||||
handles.insert(1i32, Box::new(io::stdout()));
|
||||
handles.insert(2i32, Box::new(io::stderr()));
|
||||
FileHandler {
|
||||
handles
|
||||
}
|
||||
FileHandler { handles }
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,11 +278,8 @@ impl<'tcx> FileHandler {
|
||||
// between used FDs, the find_map combinator will return it. If the first such unused FD
|
||||
// is after all other used FDs, the find_map combinator will return None, and we will use
|
||||
// the FD following the greatest FD thus far.
|
||||
let candidate_new_fd = self
|
||||
.handles
|
||||
.range(min_fd..)
|
||||
.zip(min_fd..)
|
||||
.find_map(|((fd, _fh), counter)| {
|
||||
let candidate_new_fd =
|
||||
self.handles.range(min_fd..).zip(min_fd..).find_map(|((fd, _fh), counter)| {
|
||||
if *fd != counter {
|
||||
// There was a gap in the fds stored, return the first unused one
|
||||
// (note that this relies on BTreeMap iterating in key order)
|
||||
@ -220,7 +292,10 @@ impl<'tcx> FileHandler {
|
||||
let new_fd = candidate_new_fd.unwrap_or_else(|| {
|
||||
// find_map ran out of BTreeMap entries before finding a free fd, use one plus the
|
||||
// maximum fd in the map
|
||||
self.handles.last_key_value().map(|(fd, _)| fd.checked_add(1).unwrap()).unwrap_or(min_fd)
|
||||
self.handles
|
||||
.last_key_value()
|
||||
.map(|(fd, _)| fd.checked_add(1).unwrap())
|
||||
.unwrap_or(min_fd)
|
||||
});
|
||||
|
||||
self.handles.try_insert(new_fd, file_handle).unwrap();
|
||||
@ -292,7 +367,7 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, '
|
||||
immty_from_uint_checked(modified_sec, time_t_layout)?, // st_mtime
|
||||
immty_from_uint_checked(modified_nsec, long_layout)?, // st_mtime_nsec
|
||||
immty_from_uint_checked(0u128, time_t_layout)?, // st_ctime
|
||||
immty_from_uint_checked(0u128, long_layout)?, // st_ctime_nsec
|
||||
immty_from_uint_checked(0u128, long_layout)?, // st_ctime_nsec
|
||||
immty_from_uint_checked(created_sec, time_t_layout)?, // st_birthtime
|
||||
immty_from_uint_checked(created_nsec, long_layout)?, // st_birthtime_nsec
|
||||
immty_from_uint_checked(metadata.size, off_t_layout)?, // st_size
|
||||
@ -319,7 +394,10 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, '
|
||||
Ok((-1).into())
|
||||
}
|
||||
|
||||
fn file_type_to_d_type(&mut self, file_type: std::io::Result<FileType>) -> InterpResult<'tcx, i32> {
|
||||
fn file_type_to_d_type(
|
||||
&mut self,
|
||||
file_type: std::io::Result<FileType>,
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
match file_type {
|
||||
Ok(file_type) => {
|
||||
@ -353,10 +431,14 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, '
|
||||
Ok(this.eval_libc("DT_UNKNOWN")?.to_u8()?.into())
|
||||
}
|
||||
}
|
||||
Err(e) => return match e.raw_os_error() {
|
||||
Some(error) => Ok(error),
|
||||
None => throw_unsup_format!("the error {} couldn't be converted to a return value", e),
|
||||
}
|
||||
Err(e) =>
|
||||
return match e.raw_os_error() {
|
||||
Some(error) => Ok(error),
|
||||
None => throw_unsup_format!(
|
||||
"the error {} couldn't be converted to a return value",
|
||||
e
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -396,7 +478,11 @@ impl Default for DirHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_sync_file(file: &File, writable: bool, operation: fn(&File) -> std::io::Result<()>) -> std::io::Result<i32> {
|
||||
fn maybe_sync_file(
|
||||
file: &File,
|
||||
writable: bool,
|
||||
operation: fn(&File) -> std::io::Result<()>,
|
||||
) -> std::io::Result<i32> {
|
||||
if !writable && cfg!(windows) {
|
||||
// sync_all() and sync_data() will return an error on Windows hosts if the file is not opened
|
||||
// for writing. (FlushFileBuffers requires that the file handle have the
|
||||
@ -505,16 +591,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
this.try_unwrap_io_result(fd)
|
||||
}
|
||||
|
||||
fn fcntl(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, Tag>],
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
fn fcntl(&mut self, args: &[OpTy<'tcx, Tag>]) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.check_no_isolation("`fcntl`")?;
|
||||
|
||||
if args.len() < 2 {
|
||||
throw_ub_format!("incorrect number of arguments for fcntl: got {}, expected at least 2", args.len());
|
||||
throw_ub_format!(
|
||||
"incorrect number of arguments for fcntl: got {}, expected at least 2",
|
||||
args.len()
|
||||
);
|
||||
}
|
||||
let fd = this.read_scalar(&args[0])?.to_i32()?;
|
||||
let cmd = this.read_scalar(&args[1])?.to_i32()?;
|
||||
@ -552,12 +638,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
Ok(-1)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
None => return this.handle_not_found(),
|
||||
}
|
||||
} else if this.tcx.sess.target.os == "macos"
|
||||
&& cmd == this.eval_libc_i32("F_FULLFSYNC")?
|
||||
{
|
||||
} else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC")? {
|
||||
let &[_, _] = check_arg_count(args)?;
|
||||
if let Some(file_descriptor) = this.machine.file_handler.handles.get(&fd) {
|
||||
// FIXME: Support fullfsync for all FDs
|
||||
@ -585,12 +669,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
fd: i32,
|
||||
buf: Scalar<Tag>,
|
||||
count: u64,
|
||||
) -> InterpResult<'tcx, i64> {
|
||||
fn read(&mut self, fd: i32, buf: Scalar<Tag>, count: u64) -> InterpResult<'tcx, i64> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
// Isolation check is done via `FileDescriptor` trait.
|
||||
@ -637,12 +716,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
}
|
||||
|
||||
fn write(
|
||||
&mut self,
|
||||
fd: i32,
|
||||
buf: Scalar<Tag>,
|
||||
count: u64,
|
||||
) -> InterpResult<'tcx, i64> {
|
||||
fn write(&mut self, fd: i32, buf: Scalar<Tag>, count: u64) -> InterpResult<'tcx, i64> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
// Isolation check is done via `FileDescriptor` trait.
|
||||
@ -719,7 +793,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
fn symlink(
|
||||
&mut self,
|
||||
target_op: &OpTy<'tcx, Tag>,
|
||||
linkpath_op: &OpTy<'tcx, Tag>
|
||||
linkpath_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
#[cfg(unix)]
|
||||
fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> {
|
||||
@ -729,11 +803,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
#[cfg(windows)]
|
||||
fn create_link(src: &Path, dst: &Path) -> std::io::Result<()> {
|
||||
use std::os::windows::fs;
|
||||
if src.is_dir() {
|
||||
fs::symlink_dir(src, dst)
|
||||
} else {
|
||||
fs::symlink_file(src, dst)
|
||||
}
|
||||
if src.is_dir() { fs::symlink_dir(src, dst) } else { fs::symlink_file(src, dst) }
|
||||
}
|
||||
|
||||
let this = self.eval_context_mut();
|
||||
@ -842,11 +912,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
// set.
|
||||
// Other behaviors cannot be tested from `libstd` and thus are not implemented. If you
|
||||
// found this error, please open an issue reporting it.
|
||||
if !(
|
||||
path.is_absolute() ||
|
||||
dirfd == this.eval_libc_i32("AT_FDCWD")? ||
|
||||
(path.as_os_str().is_empty() && empty_path_flag)
|
||||
) {
|
||||
if !(path.is_absolute()
|
||||
|| dirfd == this.eval_libc_i32("AT_FDCWD")?
|
||||
|| (path.as_os_str().is_empty() && empty_path_flag))
|
||||
{
|
||||
throw_unsup_format!(
|
||||
"using statx is only supported with absolute paths, relative paths with the file \
|
||||
descriptor `AT_FDCWD`, and empty paths with the `AT_EMPTY_PATH` flag set and any \
|
||||
@ -889,20 +958,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
|
||||
// We need to set the corresponding bits of `mask` if the access, creation and modification
|
||||
// times were available. Otherwise we let them be zero.
|
||||
let (access_sec, access_nsec) = metadata.accessed.map(|tup| {
|
||||
mask |= this.eval_libc("STATX_ATIME")?.to_u32()?;
|
||||
InterpResult::Ok(tup)
|
||||
}).unwrap_or(Ok((0, 0)))?;
|
||||
let (access_sec, access_nsec) = metadata
|
||||
.accessed
|
||||
.map(|tup| {
|
||||
mask |= this.eval_libc("STATX_ATIME")?.to_u32()?;
|
||||
InterpResult::Ok(tup)
|
||||
})
|
||||
.unwrap_or(Ok((0, 0)))?;
|
||||
|
||||
let (created_sec, created_nsec) = metadata.created.map(|tup| {
|
||||
mask |= this.eval_libc("STATX_BTIME")?.to_u32()?;
|
||||
InterpResult::Ok(tup)
|
||||
}).unwrap_or(Ok((0, 0)))?;
|
||||
let (created_sec, created_nsec) = metadata
|
||||
.created
|
||||
.map(|tup| {
|
||||
mask |= this.eval_libc("STATX_BTIME")?.to_u32()?;
|
||||
InterpResult::Ok(tup)
|
||||
})
|
||||
.unwrap_or(Ok((0, 0)))?;
|
||||
|
||||
let (modified_sec, modified_nsec) = metadata.modified.map(|tup| {
|
||||
mask |= this.eval_libc("STATX_MTIME")?.to_u32()?;
|
||||
InterpResult::Ok(tup)
|
||||
}).unwrap_or(Ok((0, 0)))?;
|
||||
let (modified_sec, modified_nsec) = metadata
|
||||
.modified
|
||||
.map(|tup| {
|
||||
mask |= this.eval_libc("STATX_MTIME")?.to_u32()?;
|
||||
InterpResult::Ok(tup)
|
||||
})
|
||||
.unwrap_or(Ok((0, 0)))?;
|
||||
|
||||
let __u32_layout = this.libc_ty_layout("__u32")?;
|
||||
let __u64_layout = this.libc_ty_layout("__u64")?;
|
||||
@ -1006,10 +1084,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
this.try_unwrap_io_result(result)
|
||||
}
|
||||
|
||||
fn rmdir(
|
||||
&mut self,
|
||||
path_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
fn rmdir(&mut self, path_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.check_no_isolation("`rmdir`")?;
|
||||
@ -1087,7 +1162,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
name_place.layout.size.bytes(),
|
||||
)?;
|
||||
if !name_fits {
|
||||
throw_unsup_format!("a directory entry had a name too large to fit in libc::dirent64");
|
||||
throw_unsup_format!(
|
||||
"a directory entry had a name too large to fit in libc::dirent64"
|
||||
);
|
||||
}
|
||||
|
||||
let entry_place = this.deref_operand(entry_op)?;
|
||||
@ -1175,7 +1252,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
name_place.layout.size.bytes(),
|
||||
)?;
|
||||
if !name_fits {
|
||||
throw_unsup_format!("a directory entry had a name too large to fit in libc::dirent");
|
||||
throw_unsup_format!(
|
||||
"a directory entry had a name too large to fit in libc::dirent"
|
||||
);
|
||||
}
|
||||
|
||||
let entry_place = this.deref_operand(entry_op)?;
|
||||
@ -1194,8 +1273,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
|
||||
|
||||
let imms = [
|
||||
immty_from_uint_checked(ino, ino_t_layout)?, // d_ino
|
||||
immty_from_uint_checked(0u128, off_t_layout)?, // d_seekoff
|
||||
immty_from_uint_checked(ino, ino_t_layout)?, // d_ino
|
||||
immty_from_uint_checked(0u128, off_t_layout)?, // d_seekoff
|
||||
immty_from_uint_checked(0u128, c_ushort_layout)?, // d_reclen
|
||||
immty_from_uint_checked(file_name_len, c_ushort_layout)?, // d_namlen
|
||||
immty_from_int_checked(file_type, c_uchar_layout)?, // d_type
|
||||
@ -1352,7 +1431,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
&mut self,
|
||||
pathname_op: &OpTy<'tcx, Tag>,
|
||||
buf_op: &OpTy<'tcx, Tag>,
|
||||
bufsize_op: &OpTy<'tcx, Tag>
|
||||
bufsize_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, i64> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
@ -1365,7 +1444,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let result = std::fs::read_link(pathname);
|
||||
match result {
|
||||
Ok(resolved) => {
|
||||
let resolved = this.convert_path_separator(Cow::Borrowed(resolved.as_ref()), crate::shims::os_str::PathConversion::HostToTarget);
|
||||
let resolved = this.convert_path_separator(
|
||||
Cow::Borrowed(resolved.as_ref()),
|
||||
crate::shims::os_str::PathConversion::HostToTarget,
|
||||
);
|
||||
let mut path_bytes = crate::shims::os_str::os_str_to_bytes(resolved.as_ref())?;
|
||||
let bufsize: usize = bufsize.try_into().unwrap();
|
||||
if path_bytes.len() > bufsize {
|
||||
@ -1388,12 +1470,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
/// `time` is Ok. Returns `None` if `time` is an error. Fails if `time` happens before the unix
|
||||
/// epoch.
|
||||
fn extract_sec_and_nsec<'tcx>(
|
||||
time: std::io::Result<SystemTime>
|
||||
time: std::io::Result<SystemTime>,
|
||||
) -> InterpResult<'tcx, Option<(u64, u32)>> {
|
||||
time.ok().map(|time| {
|
||||
let duration = system_time_to_duration(&time)?;
|
||||
Ok((duration.as_secs(), duration.subsec_nanos()))
|
||||
}).transpose()
|
||||
time.ok()
|
||||
.map(|time| {
|
||||
let duration = system_time_to_duration(&time)?;
|
||||
Ok((duration.as_secs(), duration.subsec_nanos()))
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
|
||||
/// Stores a file's metadata in order to avoid code duplication in the different metadata related
|
||||
@ -1410,13 +1494,10 @@ impl FileMetadata {
|
||||
fn from_path<'tcx, 'mir>(
|
||||
ecx: &mut MiriEvalContext<'mir, 'tcx>,
|
||||
path: &Path,
|
||||
follow_symlink: bool
|
||||
follow_symlink: bool,
|
||||
) -> InterpResult<'tcx, Option<FileMetadata>> {
|
||||
let metadata = if follow_symlink {
|
||||
std::fs::metadata(path)
|
||||
} else {
|
||||
std::fs::symlink_metadata(path)
|
||||
};
|
||||
let metadata =
|
||||
if follow_symlink { std::fs::metadata(path) } else { std::fs::symlink_metadata(path) };
|
||||
|
||||
FileMetadata::from_meta(ecx, metadata)
|
||||
}
|
||||
|
@ -3,8 +3,7 @@ use rustc_middle::mir;
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Dlsym {
|
||||
}
|
||||
pub enum Dlsym {}
|
||||
|
||||
impl Dlsym {
|
||||
// Returns an error for unsupported symbols, and None if this symbol
|
||||
@ -13,7 +12,7 @@ impl Dlsym {
|
||||
Ok(match &*name {
|
||||
"__pthread_get_minstack" => None,
|
||||
"getrandom" => None, // std falls back to syscall(SYS_getrandom, ...) when this is NULL.
|
||||
"statx" => None, // std falls back to syscall(SYS_statx, ...) when this is NULL.
|
||||
"statx" => None, // std falls back to syscall(SYS_statx, ...) when this is NULL.
|
||||
_ => throw_unsup_format!("unsupported Linux dlsym: {}", name),
|
||||
})
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use rustc_middle::mir;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::*;
|
||||
use crate::helpers::{check_abi, check_arg_count};
|
||||
use crate::*;
|
||||
use shims::posix::fs::EvalContextExt as _;
|
||||
use shims::posix::linux::sync::futex;
|
||||
use shims::posix::sync::EvalContextExt as _;
|
||||
@ -133,24 +133,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
// other types might be treated differently by the calling convention.
|
||||
for arg in args {
|
||||
if !matches!(arg.layout.abi, rustc_target::abi::Abi::Scalar(_)) {
|
||||
throw_ub_format!("`syscall` arguments must all have scalar layout, but {} does not", arg.layout.ty);
|
||||
throw_ub_format!(
|
||||
"`syscall` arguments must all have scalar layout, but {} does not",
|
||||
arg.layout.ty
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let sys_getrandom = this
|
||||
.eval_libc("SYS_getrandom")?
|
||||
.to_machine_usize(this)?;
|
||||
let sys_getrandom = this.eval_libc("SYS_getrandom")?.to_machine_usize(this)?;
|
||||
|
||||
let sys_statx = this
|
||||
.eval_libc("SYS_statx")?
|
||||
.to_machine_usize(this)?;
|
||||
let sys_statx = this.eval_libc("SYS_statx")?.to_machine_usize(this)?;
|
||||
|
||||
let sys_futex = this
|
||||
.eval_libc("SYS_futex")?
|
||||
.to_machine_usize(this)?;
|
||||
let sys_futex = this.eval_libc("SYS_futex")?.to_machine_usize(this)?;
|
||||
|
||||
if args.is_empty() {
|
||||
throw_ub_format!("incorrect number of arguments for syscall: got 0, expected at least 1");
|
||||
throw_ub_format!(
|
||||
"incorrect number of arguments for syscall: got 0, expected at least 1"
|
||||
);
|
||||
}
|
||||
match this.read_scalar(&args[0])?.to_machine_usize(this)? {
|
||||
// `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
|
||||
@ -158,7 +157,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
id if id == sys_getrandom => {
|
||||
// The first argument is the syscall id, so skip over it.
|
||||
if args.len() < 4 {
|
||||
throw_ub_format!("incorrect number of arguments for `getrandom` syscall: got {}, expected at least 4", args.len());
|
||||
throw_ub_format!(
|
||||
"incorrect number of arguments for `getrandom` syscall: got {}, expected at least 4",
|
||||
args.len()
|
||||
);
|
||||
}
|
||||
getrandom(this, &args[1], &args[2], &args[3], dest)?;
|
||||
}
|
||||
@ -167,9 +169,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
id if id == sys_statx => {
|
||||
// The first argument is the syscall id, so skip over it.
|
||||
if args.len() < 6 {
|
||||
throw_ub_format!("incorrect number of arguments for `statx` syscall: got {}, expected at least 6", args.len());
|
||||
throw_ub_format!(
|
||||
"incorrect number of arguments for `statx` syscall: got {}, expected at least 6",
|
||||
args.len()
|
||||
);
|
||||
}
|
||||
let result = this.linux_statx(&args[1], &args[2], &args[3], &args[4], &args[5])?;
|
||||
let result =
|
||||
this.linux_statx(&args[1], &args[2], &args[3], &args[4], &args[5])?;
|
||||
this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
|
||||
}
|
||||
// `futex` is used by some synchonization primitives.
|
||||
@ -200,7 +206,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
|
||||
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
||||
// These shims are enabled only when the caller is in the standard library.
|
||||
"pthread_getattr_np" if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
|
||||
"pthread_getattr_np"
|
||||
if this.frame().instance.to_string().starts_with("std::sys::unix::") =>
|
||||
{
|
||||
check_abi(abi, Abi::C { unwind: false })?;
|
||||
let &[ref _thread, ref _attr] = check_arg_count(args)?;
|
||||
this.write_null(dest)?;
|
||||
|
@ -1,3 +1,3 @@
|
||||
pub mod foreign_items;
|
||||
pub mod dlsym;
|
||||
pub mod foreign_items;
|
||||
pub mod sync;
|
||||
|
@ -18,7 +18,10 @@ pub fn futex<'tcx>(
|
||||
// Therefore we don't use `check_arg_count` here, but only check for the
|
||||
// number of arguments to fall within a range.
|
||||
if args.len() < 4 {
|
||||
throw_ub_format!("incorrect number of arguments for `futex` syscall: got {}, expected at least 4", args.len());
|
||||
throw_ub_format!(
|
||||
"incorrect number of arguments for `futex` syscall: got {}, expected at least 4",
|
||||
args.len()
|
||||
);
|
||||
}
|
||||
|
||||
// The first three arguments (after the syscall number itself) are the same to all futex operations:
|
||||
@ -49,13 +52,18 @@ pub fn futex<'tcx>(
|
||||
// or *timeout expires. `timeout == null` for an infinite timeout.
|
||||
op if op & !futex_realtime == futex_wait => {
|
||||
if args.len() < 5 {
|
||||
throw_ub_format!("incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT`: got {}, expected at least 5", args.len());
|
||||
throw_ub_format!(
|
||||
"incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT`: got {}, expected at least 5",
|
||||
args.len()
|
||||
);
|
||||
}
|
||||
let timeout = &args[4];
|
||||
let timeout_time = if this.is_null(this.read_scalar(timeout)?.check_init()?)? {
|
||||
None
|
||||
} else {
|
||||
this.check_no_isolation("`futex` syscall with `op=FUTEX_WAIT` and non-null timeout")?;
|
||||
this.check_no_isolation(
|
||||
"`futex` syscall with `op=FUTEX_WAIT` and non-null timeout",
|
||||
)?;
|
||||
let duration = match this.read_timespec(timeout)? {
|
||||
Some(duration) => duration,
|
||||
None => {
|
||||
@ -74,7 +82,11 @@ pub fn futex<'tcx>(
|
||||
// Check the pointer for alignment and validity.
|
||||
// The API requires `addr` to be a 4-byte aligned pointer, and will
|
||||
// use the 4 bytes at the given address as an (atomic) i32.
|
||||
this.memory.check_ptr_access(addr.to_scalar()?, Size::from_bytes(4), Align::from_bytes(4).unwrap())?;
|
||||
this.memory.check_ptr_access(
|
||||
addr.to_scalar()?,
|
||||
Size::from_bytes(4),
|
||||
Align::from_bytes(4).unwrap(),
|
||||
)?;
|
||||
// Read an `i32` through the pointer, regardless of any wrapper types.
|
||||
// It's not uncommon for `addr` to be passed as another type than `*mut i32`, such as `*const AtomicI32`.
|
||||
// FIXME: this fails if `addr` is not a pointer type.
|
||||
@ -87,9 +99,14 @@ pub fn futex<'tcx>(
|
||||
// operations on the same futex word."
|
||||
// SeqCst is total order over all operations.
|
||||
// FIXME: check if this should be changed when weak memory orders are added.
|
||||
let futex_val = this.read_scalar_at_offset_atomic(
|
||||
&addr.into(), 0, this.machine.layouts.i32, AtomicReadOp::SeqCst
|
||||
)?.to_i32()?;
|
||||
let futex_val = this
|
||||
.read_scalar_at_offset_atomic(
|
||||
&addr.into(),
|
||||
0,
|
||||
this.machine.layouts.i32,
|
||||
AtomicReadOp::SeqCst,
|
||||
)?
|
||||
.to_i32()?;
|
||||
if val == futex_val {
|
||||
// The value still matches, so we block the trait make it wait for FUTEX_WAKE.
|
||||
this.block_thread(thread);
|
||||
|
@ -97,7 +97,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let &[ref info] = check_arg_count(args)?;
|
||||
let result = this.mach_timebase_info(info)?;
|
||||
this.write_scalar(Scalar::from_i32(result), dest)?;
|
||||
},
|
||||
}
|
||||
|
||||
// Access to command-line arguments
|
||||
"_NSGetArgc" => {
|
||||
@ -162,4 +162,3 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
pub mod foreign_items;
|
||||
pub mod dlsym;
|
||||
pub mod foreign_items;
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub mod foreign_items;
|
||||
pub mod dlsym;
|
||||
pub mod foreign_items;
|
||||
|
||||
mod fs;
|
||||
mod sync;
|
||||
|
@ -63,8 +63,10 @@ fn mutex_get_kind<'mir, 'tcx: 'mir>(
|
||||
) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
|
||||
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
|
||||
ecx.read_scalar_at_offset_atomic(
|
||||
mutex_op, offset, ecx.machine.layouts.i32,
|
||||
AtomicReadOp::Relaxed
|
||||
mutex_op,
|
||||
offset,
|
||||
ecx.machine.layouts.i32,
|
||||
AtomicReadOp::Relaxed,
|
||||
)
|
||||
}
|
||||
|
||||
@ -75,8 +77,11 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>(
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
|
||||
ecx.write_scalar_at_offset_atomic(
|
||||
mutex_op, offset, kind, ecx.machine.layouts.i32,
|
||||
AtomicWriteOp::Relaxed
|
||||
mutex_op,
|
||||
offset,
|
||||
kind,
|
||||
ecx.machine.layouts.i32,
|
||||
AtomicWriteOp::Relaxed,
|
||||
)
|
||||
}
|
||||
|
||||
@ -84,10 +89,7 @@ fn mutex_get_id<'mir, 'tcx: 'mir>(
|
||||
ecx: &MiriEvalContext<'mir, 'tcx>,
|
||||
mutex_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
|
||||
ecx.read_scalar_at_offset_atomic(
|
||||
mutex_op, 4, ecx.machine.layouts.u32,
|
||||
AtomicReadOp::Relaxed
|
||||
)
|
||||
ecx.read_scalar_at_offset_atomic(mutex_op, 4, ecx.machine.layouts.u32, AtomicReadOp::Relaxed)
|
||||
}
|
||||
|
||||
fn mutex_set_id<'mir, 'tcx: 'mir>(
|
||||
@ -96,8 +98,11 @@ fn mutex_set_id<'mir, 'tcx: 'mir>(
|
||||
id: impl Into<ScalarMaybeUninit<Tag>>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
ecx.write_scalar_at_offset_atomic(
|
||||
mutex_op, 4, id, ecx.machine.layouts.u32,
|
||||
AtomicWriteOp::Relaxed
|
||||
mutex_op,
|
||||
4,
|
||||
id,
|
||||
ecx.machine.layouts.u32,
|
||||
AtomicWriteOp::Relaxed,
|
||||
)
|
||||
}
|
||||
|
||||
@ -128,10 +133,7 @@ fn rwlock_get_id<'mir, 'tcx: 'mir>(
|
||||
ecx: &MiriEvalContext<'mir, 'tcx>,
|
||||
rwlock_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
|
||||
ecx.read_scalar_at_offset_atomic(
|
||||
rwlock_op, 4, ecx.machine.layouts.u32,
|
||||
AtomicReadOp::Relaxed
|
||||
)
|
||||
ecx.read_scalar_at_offset_atomic(rwlock_op, 4, ecx.machine.layouts.u32, AtomicReadOp::Relaxed)
|
||||
}
|
||||
|
||||
fn rwlock_set_id<'mir, 'tcx: 'mir>(
|
||||
@ -140,8 +142,11 @@ fn rwlock_set_id<'mir, 'tcx: 'mir>(
|
||||
id: impl Into<ScalarMaybeUninit<Tag>>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
ecx.write_scalar_at_offset_atomic(
|
||||
rwlock_op, 4, id, ecx.machine.layouts.u32,
|
||||
AtomicWriteOp::Relaxed
|
||||
rwlock_op,
|
||||
4,
|
||||
id,
|
||||
ecx.machine.layouts.u32,
|
||||
AtomicWriteOp::Relaxed,
|
||||
)
|
||||
}
|
||||
|
||||
@ -195,10 +200,7 @@ fn cond_get_id<'mir, 'tcx: 'mir>(
|
||||
ecx: &MiriEvalContext<'mir, 'tcx>,
|
||||
cond_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
|
||||
ecx.read_scalar_at_offset_atomic(
|
||||
cond_op, 4, ecx.machine.layouts.u32,
|
||||
AtomicReadOp::Relaxed
|
||||
)
|
||||
ecx.read_scalar_at_offset_atomic(cond_op, 4, ecx.machine.layouts.u32, AtomicReadOp::Relaxed)
|
||||
}
|
||||
|
||||
fn cond_set_id<'mir, 'tcx: 'mir>(
|
||||
@ -207,8 +209,11 @@ fn cond_set_id<'mir, 'tcx: 'mir>(
|
||||
id: impl Into<ScalarMaybeUninit<Tag>>,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
ecx.write_scalar_at_offset_atomic(
|
||||
cond_op, 4, id, ecx.machine.layouts.u32,
|
||||
AtomicWriteOp::Relaxed
|
||||
cond_op,
|
||||
4,
|
||||
id,
|
||||
ecx.machine.layouts.u32,
|
||||
AtomicWriteOp::Relaxed,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.tcx.sess.warn(
|
||||
"thread support is experimental and incomplete: weak memory effects are not emulated."
|
||||
"thread support is experimental and incomplete: weak memory effects are not emulated.",
|
||||
);
|
||||
|
||||
// Create the new thread
|
||||
@ -31,7 +31,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
)?;
|
||||
|
||||
// Read the function argument that will be sent to the new thread
|
||||
// before the thread starts executing since reading after the
|
||||
// before the thread starts executing since reading after the
|
||||
// context switch will incorrectly report a data-race.
|
||||
let fn_ptr = this.read_scalar(start_routine)?.check_init()?;
|
||||
let func_arg = this.read_immediate(arg)?;
|
||||
@ -130,10 +130,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn pthread_setname_np(
|
||||
&mut self,
|
||||
name: Scalar<Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
fn pthread_setname_np(&mut self, name: Scalar<Tag>) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
this.assert_target_os("macos", "pthread_setname_np");
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::time::{Duration, SystemTime, Instant};
|
||||
use std::convert::TryFrom;
|
||||
use std::time::{Duration, Instant, SystemTime};
|
||||
|
||||
use crate::stacked_borrows::Tag;
|
||||
use crate::*;
|
||||
@ -99,7 +99,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC;
|
||||
let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC;
|
||||
|
||||
let duration = system_time_to_duration(&SystemTime::now())? + Duration::from_secs(SECONDS_TO_UNIX_EPOCH);
|
||||
let duration = system_time_to_duration(&SystemTime::now())?
|
||||
+ Duration::from_secs(SECONDS_TO_UNIX_EPOCH);
|
||||
let duration_ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL))
|
||||
.map_err(|_| err_unsup_format!("programs running more than 2^64 Windows ticks after the Windows epoch are not supported"))?;
|
||||
|
||||
@ -115,7 +116,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn QueryPerformanceCounter(&mut self, lpPerformanceCount_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
|
||||
fn QueryPerformanceCounter(
|
||||
&mut self,
|
||||
lpPerformanceCount_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.assert_target_os("windows", "QueryPerformanceCounter");
|
||||
@ -124,14 +128,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
// QueryPerformanceCounter uses a hardware counter as its basis.
|
||||
// Miri will emulate a counter with a resolution of 1 nanosecond.
|
||||
let duration = Instant::now().duration_since(this.machine.time_anchor);
|
||||
let qpc = i64::try_from(duration.as_nanos())
|
||||
.map_err(|_| err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported"))?;
|
||||
this.write_scalar(Scalar::from_i64(qpc), &this.deref_operand(lpPerformanceCount_op)?.into())?;
|
||||
let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
|
||||
err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
|
||||
})?;
|
||||
this.write_scalar(
|
||||
Scalar::from_i64(qpc),
|
||||
&this.deref_operand(lpPerformanceCount_op)?.into(),
|
||||
)?;
|
||||
Ok(-1) // return non-zero on success
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn QueryPerformanceFrequency(&mut self, lpFrequency_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
|
||||
fn QueryPerformanceFrequency(
|
||||
&mut self,
|
||||
lpFrequency_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.assert_target_os("windows", "QueryPerformanceFrequency");
|
||||
@ -142,7 +153,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
// is consistent across all processors.
|
||||
// Miri emulates a "hardware" performance counter with a resolution of 1ns,
|
||||
// and thus 10^9 counts per second.
|
||||
this.write_scalar(Scalar::from_i64(1_000_000_000), &this.deref_operand(lpFrequency_op)?.into())?;
|
||||
this.write_scalar(
|
||||
Scalar::from_i64(1_000_000_000),
|
||||
&this.deref_operand(lpFrequency_op)?.into(),
|
||||
)?;
|
||||
Ok(-1) // Return non-zero on success
|
||||
}
|
||||
|
||||
@ -155,8 +169,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
// This returns a u64, with time units determined dynamically by `mach_timebase_info`.
|
||||
// We return plain nanoseconds.
|
||||
let duration = Instant::now().duration_since(this.machine.time_anchor);
|
||||
u64::try_from(duration.as_nanos())
|
||||
.map_err(|_| err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported").into())
|
||||
u64::try_from(duration.as_nanos()).map_err(|_| {
|
||||
err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported")
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
fn mach_timebase_info(&mut self, info_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
|
||||
@ -169,10 +185,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
|
||||
// Since our emulated ticks in `mach_absolute_time` *are* nanoseconds,
|
||||
// no scaling needs to happen.
|
||||
let (numer, denom) = (1,1);
|
||||
let (numer, denom) = (1, 1);
|
||||
let imms = [
|
||||
immty_from_int_checked(numer, this.machine.layouts.u32)?,
|
||||
immty_from_int_checked(denom, this.machine.layouts.u32)?
|
||||
immty_from_int_checked(denom, this.machine.layouts.u32)?,
|
||||
];
|
||||
|
||||
this.write_packed_immediates(&info, &imms)?;
|
||||
|
@ -1,14 +1,14 @@
|
||||
//! Implement thread-local storage.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::btree_map::Entry as BTreeEntry;
|
||||
use std::collections::hash_map::Entry as HashMapEntry;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use log::trace;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_middle::ty;
|
||||
use rustc_target::abi::{Size, HasDataLayout};
|
||||
use rustc_target::abi::{HasDataLayout, Size};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use crate::*;
|
||||
@ -63,7 +63,11 @@ impl<'tcx> Default for TlsData<'tcx> {
|
||||
impl<'tcx> TlsData<'tcx> {
|
||||
/// Generate a new TLS key with the given destructor.
|
||||
/// `max_size` determines the integer size the key has to fit in.
|
||||
pub fn create_tls_key(&mut self, dtor: Option<ty::Instance<'tcx>>, max_size: Size) -> InterpResult<'tcx, TlsKey> {
|
||||
pub fn create_tls_key(
|
||||
&mut self,
|
||||
dtor: Option<ty::Instance<'tcx>>,
|
||||
max_size: Size,
|
||||
) -> InterpResult<'tcx, TlsKey> {
|
||||
let new_key = self.next_key;
|
||||
self.next_key += 1;
|
||||
self.keys.try_insert(new_key, TlsEntry { data: Default::default(), dtor }).unwrap();
|
||||
@ -105,7 +109,7 @@ impl<'tcx> TlsData<'tcx> {
|
||||
&mut self,
|
||||
key: TlsKey,
|
||||
thread_id: ThreadId,
|
||||
new_data: Option<Scalar<Tag>>
|
||||
new_data: Option<Scalar<Tag>>,
|
||||
) -> InterpResult<'tcx> {
|
||||
match self.keys.get_mut(&key) {
|
||||
Some(TlsEntry { data, .. }) => {
|
||||
@ -138,14 +142,18 @@ impl<'tcx> TlsData<'tcx> {
|
||||
&mut self,
|
||||
thread: ThreadId,
|
||||
dtor: ty::Instance<'tcx>,
|
||||
data: Scalar<Tag>
|
||||
data: Scalar<Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
if self.dtors_running.contains_key(&thread) {
|
||||
// UB, according to libstd docs.
|
||||
throw_ub_format!("setting thread's local storage destructor while destructors are already running");
|
||||
throw_ub_format!(
|
||||
"setting thread's local storage destructor while destructors are already running"
|
||||
);
|
||||
}
|
||||
if self.macos_thread_dtors.insert(thread, (dtor, data)).is_some() {
|
||||
throw_unsup_format!("setting more than one thread local storage destructor for the same thread is not supported");
|
||||
throw_unsup_format!(
|
||||
"setting more than one thread local storage destructor for the same thread is not supported"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -181,9 +189,7 @@ impl<'tcx> TlsData<'tcx> {
|
||||
Some(key) => Excluded(key),
|
||||
None => Unbounded,
|
||||
};
|
||||
for (&key, TlsEntry { data, dtor }) in
|
||||
thread_local.range_mut((start, Unbounded))
|
||||
{
|
||||
for (&key, TlsEntry { data, dtor }) in thread_local.range_mut((start, Unbounded)) {
|
||||
match data.entry(thread_id) {
|
||||
BTreeEntry::Occupied(entry) => {
|
||||
if let Some(dtor) = dtor {
|
||||
@ -237,7 +243,13 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
// (that would be basically https://github.com/rust-lang/miri/issues/450),
|
||||
// we specifically look up the static in libstd that we know is placed
|
||||
// in that section.
|
||||
let thread_callback = this.eval_path_scalar(&["std", "sys", "windows", "thread_local_key", "p_thread_callback"])?;
|
||||
let thread_callback = this.eval_path_scalar(&[
|
||||
"std",
|
||||
"sys",
|
||||
"windows",
|
||||
"thread_local_key",
|
||||
"p_thread_callback",
|
||||
])?;
|
||||
let thread_callback = this.memory.get_fn(thread_callback.check_init()?)?.as_instance()?;
|
||||
|
||||
// The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`.
|
||||
@ -297,12 +309,11 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let dtor = match this.machine.tls.fetch_tls_dtor(last_key, active_thread) {
|
||||
dtor @ Some(_) => dtor,
|
||||
// We ran each dtor once, start over from the beginning.
|
||||
None => {
|
||||
this.machine.tls.fetch_tls_dtor(None, active_thread)
|
||||
}
|
||||
None => this.machine.tls.fetch_tls_dtor(None, active_thread),
|
||||
};
|
||||
if let Some((instance, ptr, key)) = dtor {
|
||||
this.machine.tls.dtors_running.get_mut(&active_thread).unwrap().last_dtor_key = Some(key);
|
||||
this.machine.tls.dtors_running.get_mut(&active_thread).unwrap().last_dtor_key =
|
||||
Some(key);
|
||||
trace!("Running TLS dtor {:?} on {:?} at {:?}", instance, ptr, active_thread);
|
||||
assert!(!this.is_null(ptr).unwrap(), "data can't be NULL when dtor is called!");
|
||||
|
||||
@ -326,7 +337,6 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
|
||||
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
|
||||
/// Schedule an active thread's TLS destructor to run on the active thread.
|
||||
/// Note that this function does not run the destructors itself, it just
|
||||
/// schedules them one by one each time it is called and reenables the
|
||||
@ -349,7 +359,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
// relevant function, reenabling the thread, and going back to
|
||||
// the scheduler.
|
||||
this.schedule_windows_tls_dtors()?;
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
// The remaining dtors make some progress each time around the scheduler loop,
|
||||
@ -361,12 +371,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
// We have scheduled a MacOS dtor to run on the thread. Execute it
|
||||
// to completion and come back here. Scheduling a destructor
|
||||
// destroys it, so we will not enter this branch again.
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
if this.schedule_next_pthread_tls_dtor()? {
|
||||
// We have scheduled a pthread destructor and removed it from the
|
||||
// destructors list. Run it to completion and come back here.
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// All dtors done!
|
||||
|
@ -5,8 +5,7 @@ use crate::*;
|
||||
use helpers::check_abi;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Dlsym {
|
||||
}
|
||||
pub enum Dlsym {}
|
||||
|
||||
impl Dlsym {
|
||||
// Returns an error for unsupported symbols, and None if this symbol
|
||||
|
@ -75,7 +75,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
"WriteFile" => {
|
||||
check_abi(abi, Abi::System { unwind: false })?;
|
||||
let &[ref handle, ref buf, ref n, ref written_ptr, ref overlapped] = check_arg_count(args)?;
|
||||
let &[ref handle, ref buf, ref n, ref written_ptr, ref overlapped] =
|
||||
check_arg_count(args)?;
|
||||
this.read_scalar(overlapped)?.to_machine_usize(this)?; // this is a poiner, that we ignore
|
||||
let handle = this.read_scalar(handle)?.to_machine_isize(this)?;
|
||||
let buf = this.read_scalar(buf)?.check_init()?;
|
||||
@ -95,17 +96,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
};
|
||||
res.ok().map(|n| n as u32)
|
||||
} else {
|
||||
throw_unsup_format!("on Windows, writing to anything except stdout/stderr is not supported")
|
||||
throw_unsup_format!(
|
||||
"on Windows, writing to anything except stdout/stderr is not supported"
|
||||
)
|
||||
};
|
||||
// If there was no error, write back how much was written.
|
||||
if let Some(n) = written {
|
||||
this.write_scalar(Scalar::from_u32(n), &written_place.into())?;
|
||||
}
|
||||
// Return whether this was a success.
|
||||
this.write_scalar(
|
||||
Scalar::from_i32(if written.is_some() { 1 } else { 0 }),
|
||||
dest,
|
||||
)?;
|
||||
this.write_scalar(Scalar::from_i32(if written.is_some() { 1 } else { 0 }), dest)?;
|
||||
}
|
||||
|
||||
// Allocation
|
||||
@ -297,11 +297,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let ptr = this.read_scalar(ptr)?.check_init()?;
|
||||
let len = this.read_scalar(len)?.to_u32()?;
|
||||
let flags = this.read_scalar(flags)?.to_u32()?;
|
||||
if flags != 2 { // BCRYPT_USE_SYSTEM_PREFERRED_RNG
|
||||
throw_unsup_format!("BCryptGenRandom is supported only with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag");
|
||||
if flags != 2 {
|
||||
// BCRYPT_USE_SYSTEM_PREFERRED_RNG
|
||||
throw_unsup_format!(
|
||||
"BCryptGenRandom is supported only with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag"
|
||||
);
|
||||
}
|
||||
if algorithm.to_machine_usize(this)? != 0 {
|
||||
throw_unsup_format!("BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG");
|
||||
throw_unsup_format!(
|
||||
"BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
|
||||
);
|
||||
}
|
||||
this.gen_random(ptr, len.into())?;
|
||||
this.write_null(dest)?; // STATUS_SUCCESS
|
||||
@ -342,27 +347,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
|
||||
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
|
||||
// These shims are enabled only when the caller is in the standard library.
|
||||
"GetProcessHeap" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
|
||||
"GetProcessHeap"
|
||||
if this.frame().instance.to_string().starts_with("std::sys::windows::") =>
|
||||
{
|
||||
check_abi(abi, Abi::System { unwind: false })?;
|
||||
let &[] = check_arg_count(args)?;
|
||||
// Just fake a HANDLE
|
||||
this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
|
||||
}
|
||||
"SetConsoleTextAttribute" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
|
||||
"SetConsoleTextAttribute"
|
||||
if this.frame().instance.to_string().starts_with("std::sys::windows::") =>
|
||||
{
|
||||
check_abi(abi, Abi::System { unwind: false })?;
|
||||
#[allow(non_snake_case)]
|
||||
let &[ref _hConsoleOutput, ref _wAttribute] = check_arg_count(args)?;
|
||||
// Pretend these does not exist / nothing happened, by returning zero.
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
"AddVectoredExceptionHandler" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
|
||||
"AddVectoredExceptionHandler"
|
||||
if this.frame().instance.to_string().starts_with("std::sys::windows::") =>
|
||||
{
|
||||
check_abi(abi, Abi::System { unwind: false })?;
|
||||
#[allow(non_snake_case)]
|
||||
let &[ref _First, ref _Handler] = check_arg_count(args)?;
|
||||
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
|
||||
this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
|
||||
}
|
||||
"SetThreadStackGuarantee" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
|
||||
"SetThreadStackGuarantee"
|
||||
if this.frame().instance.to_string().starts_with("std::sys::windows::") =>
|
||||
{
|
||||
check_abi(abi, Abi::System { unwind: false })?;
|
||||
#[allow(non_snake_case)]
|
||||
let &[_StackSizeInBytes] = check_arg_count(args)?;
|
||||
@ -373,21 +386,31 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
| "EnterCriticalSection"
|
||||
| "LeaveCriticalSection"
|
||||
| "DeleteCriticalSection"
|
||||
if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
|
||||
if this.frame().instance.to_string().starts_with("std::sys::windows::") =>
|
||||
{
|
||||
check_abi(abi, Abi::System { unwind: false })?;
|
||||
#[allow(non_snake_case)]
|
||||
let &[ref _lpCriticalSection] = check_arg_count(args)?;
|
||||
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
|
||||
assert_eq!(
|
||||
this.get_total_thread_count(),
|
||||
1,
|
||||
"concurrency on Windows is not supported"
|
||||
);
|
||||
// Nothing to do, not even a return value.
|
||||
// (Windows locks are reentrant, and we have only 1 thread,
|
||||
// so not doing any futher checks here is at least not incorrect.)
|
||||
}
|
||||
"TryEnterCriticalSection"
|
||||
if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
|
||||
if this.frame().instance.to_string().starts_with("std::sys::windows::") =>
|
||||
{
|
||||
check_abi(abi, Abi::System { unwind: false })?;
|
||||
#[allow(non_snake_case)]
|
||||
let &[ref _lpCriticalSection] = check_arg_count(args)?;
|
||||
assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
|
||||
assert_eq!(
|
||||
this.get_total_thread_count(),
|
||||
1,
|
||||
"concurrency on Windows is not supported"
|
||||
);
|
||||
// There is only one thread, so this always succeeds and returns TRUE.
|
||||
this.write_scalar(Scalar::from_i32(1), dest)?;
|
||||
}
|
||||
@ -398,4 +421,3 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub mod foreign_items;
|
||||
pub mod dlsym;
|
||||
pub mod foreign_items;
|
||||
|
||||
mod sync;
|
||||
|
@ -22,10 +22,7 @@ fn srwlock_get_or_create_id<'mir, 'tcx: 'mir>(
|
||||
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
#[allow(non_snake_case)]
|
||||
fn AcquireSRWLockExclusive(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
fn AcquireSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let id = srwlock_get_or_create_id(this, lock_op)?;
|
||||
let active_thread = this.get_active_thread();
|
||||
@ -47,10 +44,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn TryAcquireSRWLockExclusive(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, u8> {
|
||||
fn TryAcquireSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, u8> {
|
||||
let this = self.eval_context_mut();
|
||||
let id = srwlock_get_or_create_id(this, lock_op)?;
|
||||
let active_thread = this.get_active_thread();
|
||||
@ -65,27 +59,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn ReleaseSRWLockExclusive(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
fn ReleaseSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let id = srwlock_get_or_create_id(this, lock_op)?;
|
||||
let active_thread = this.get_active_thread();
|
||||
|
||||
if !this.rwlock_writer_unlock(id, active_thread) {
|
||||
// The docs do not say anything about this case, but it seems better to not allow it.
|
||||
throw_ub_format!("calling ReleaseSRWLockExclusive on an SRWLock that is not exclusively locked by the current thread");
|
||||
throw_ub_format!(
|
||||
"calling ReleaseSRWLockExclusive on an SRWLock that is not exclusively locked by the current thread"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn AcquireSRWLockShared(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
fn AcquireSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let id = srwlock_get_or_create_id(this, lock_op)?;
|
||||
let active_thread = this.get_active_thread();
|
||||
@ -100,10 +90,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn TryAcquireSRWLockShared(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, u8> {
|
||||
fn TryAcquireSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, u8> {
|
||||
let this = self.eval_context_mut();
|
||||
let id = srwlock_get_or_create_id(this, lock_op)?;
|
||||
let active_thread = this.get_active_thread();
|
||||
@ -117,17 +104,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn ReleaseSRWLockShared(
|
||||
&mut self,
|
||||
lock_op: &OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
fn ReleaseSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let id = srwlock_get_or_create_id(this, lock_op)?;
|
||||
let active_thread = this.get_active_thread();
|
||||
|
||||
if !this.rwlock_reader_unlock(id, active_thread) {
|
||||
// The docs do not say anything about this case, but it seems better to not allow it.
|
||||
throw_ub_format!("calling ReleaseSRWLockShared on an SRWLock that is not locked by the current thread");
|
||||
throw_ub_format!(
|
||||
"calling ReleaseSRWLockShared on an SRWLock that is not locked by the current thread"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -9,10 +9,10 @@ use std::rc::Rc;
|
||||
use log::trace;
|
||||
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir::Mutability;
|
||||
use rustc_middle::mir::RetagKind;
|
||||
use rustc_middle::ty;
|
||||
use rustc_target::abi::{Align, LayoutOf, Size};
|
||||
use rustc_hir::Mutability;
|
||||
|
||||
use crate::*;
|
||||
|
||||
@ -157,7 +157,11 @@ impl fmt::Display for RefKind {
|
||||
|
||||
/// Utilities for initialization and ID generation
|
||||
impl GlobalState {
|
||||
pub fn new(tracked_pointer_tag: Option<PtrId>, tracked_call_id: Option<CallId>, track_raw: bool) -> Self {
|
||||
pub fn new(
|
||||
tracked_pointer_tag: Option<PtrId>,
|
||||
tracked_call_id: Option<CallId>,
|
||||
track_raw: bool,
|
||||
) -> Self {
|
||||
GlobalState {
|
||||
next_ptr_id: NonZeroU64::new(1).unwrap(),
|
||||
base_ptr_ids: FxHashMap::default(),
|
||||
@ -211,7 +215,9 @@ impl GlobalState {
|
||||
fn err_sb_ub(msg: String) -> InterpError<'static> {
|
||||
err_machine_stop!(TerminationInfo::ExperimentalUb {
|
||||
msg,
|
||||
url: format!("https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md"),
|
||||
url: format!(
|
||||
"https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md"
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
@ -300,10 +306,7 @@ impl<'tcx> Stack {
|
||||
tag, item
|
||||
)))?
|
||||
} else {
|
||||
Err(err_sb_ub(format!(
|
||||
"deallocating while item is protected: {:?}",
|
||||
item
|
||||
)))?
|
||||
Err(err_sb_ub(format!("deallocating while item is protected: {:?}", item)))?
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -312,14 +315,21 @@ impl<'tcx> Stack {
|
||||
|
||||
/// Test if a memory `access` using pointer tagged `tag` is granted.
|
||||
/// If yes, return the index of the item that granted it.
|
||||
fn access(&mut self, access: AccessKind, ptr: Pointer<Tag>, global: &GlobalState) -> InterpResult<'tcx> {
|
||||
fn access(
|
||||
&mut self,
|
||||
access: AccessKind,
|
||||
ptr: Pointer<Tag>,
|
||||
global: &GlobalState,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Two main steps: Find granting item, remove incompatible items above.
|
||||
|
||||
// Step 1: Find granting item.
|
||||
let granting_idx = self.find_granting(access, ptr.tag).ok_or_else(|| {
|
||||
err_sb_ub(format!(
|
||||
"no item granting {} to tag {:?} at {} found in borrow stack.",
|
||||
access, ptr.tag, ptr.erase_tag(),
|
||||
access,
|
||||
ptr.tag,
|
||||
ptr.erase_tag(),
|
||||
))
|
||||
})?;
|
||||
|
||||
@ -379,7 +389,12 @@ impl<'tcx> Stack {
|
||||
/// `weak` controls whether this operation is weak or strong: weak granting does not act as
|
||||
/// an access, and they add the new item directly on top of the one it is derived
|
||||
/// from instead of all the way at the top of the stack.
|
||||
fn grant(&mut self, derived_from: Pointer<Tag>, new: Item, global: &GlobalState) -> InterpResult<'tcx> {
|
||||
fn grant(
|
||||
&mut self,
|
||||
derived_from: Pointer<Tag>,
|
||||
new: Item,
|
||||
global: &GlobalState,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Figure out which access `perm` corresponds to.
|
||||
let access =
|
||||
if new.perm.grants(AccessKind::Write) { AccessKind::Write } else { AccessKind::Read };
|
||||
@ -480,12 +495,17 @@ impl Stacks {
|
||||
// `ExternStatic` is used for extern statics, and thus must also be listed here.
|
||||
// `Env` we list because we can get away with precise tracking there.
|
||||
// The base pointer is not unique, so the base permission is `SharedReadWrite`.
|
||||
MemoryKind::Machine(MiriMemoryKind::Global | MiriMemoryKind::ExternStatic | MiriMemoryKind::Tls | MiriMemoryKind::Env) =>
|
||||
(extra.borrow_mut().global_base_ptr(id), Permission::SharedReadWrite),
|
||||
MemoryKind::Machine(
|
||||
MiriMemoryKind::Global
|
||||
| MiriMemoryKind::ExternStatic
|
||||
| MiriMemoryKind::Tls
|
||||
| MiriMemoryKind::Env,
|
||||
) => (extra.borrow_mut().global_base_ptr(id), Permission::SharedReadWrite),
|
||||
// Everything else we handle like raw pointers for now.
|
||||
_ => {
|
||||
let mut extra = extra.borrow_mut();
|
||||
let tag = if extra.track_raw { Tag::Tagged(extra.new_ptr()) } else { Tag::Untagged };
|
||||
let tag =
|
||||
if extra.track_raw { Tag::Tagged(extra.new_ptr()) } else { Tag::Untagged };
|
||||
(tag, Permission::SharedReadWrite)
|
||||
}
|
||||
};
|
||||
@ -584,9 +604,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let this = self.eval_context_mut();
|
||||
// We want a place for where the ptr *points to*, so we get one.
|
||||
let place = this.ref_to_mplace(val)?;
|
||||
let size = this
|
||||
.size_and_align_of_mplace(&place)?
|
||||
.map(|(size, _)| size);
|
||||
let size = this.size_and_align_of_mplace(&place)?.map(|(size, _)| size);
|
||||
// FIXME: If we cannot determine the size (because the unsized tail is an `extern type`),
|
||||
// bail out -- we cannot reasonably figure out which memory range to reborrow.
|
||||
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/276.
|
||||
@ -667,7 +685,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
|
||||
/// After a stack frame got pushed, retag the return place so that we are sure
|
||||
/// it does not alias with anything.
|
||||
///
|
||||
///
|
||||
/// This is a HACK because there is nothing in MIR that would make the retag
|
||||
/// explicit. Also see https://github.com/rust-lang/rust/issues/71117.
|
||||
fn retag_return_place(&mut self) -> InterpResult<'tcx> {
|
||||
@ -690,7 +708,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let ptr_layout = this.layout_of(this.tcx.mk_mut_ptr(return_place.layout.ty))?;
|
||||
let val = ImmTy::from_immediate(return_place.to_ref(), ptr_layout);
|
||||
// Reborrow it.
|
||||
let val = this.retag_reference(&val, RefKind::Unique { two_phase: false }, /*protector*/ true)?;
|
||||
let val = this.retag_reference(
|
||||
&val,
|
||||
RefKind::Unique { two_phase: false },
|
||||
/*protector*/ true,
|
||||
)?;
|
||||
// And use reborrowed pointer for return place.
|
||||
let return_place = this.ref_to_mplace(&val)?;
|
||||
this.frame_mut().return_place = Some(return_place.into());
|
||||
|
47
src/sync.rs
47
src/sync.rs
@ -66,7 +66,7 @@ struct Mutex {
|
||||
/// released to during unlock and acquired from during
|
||||
/// locking, and therefore stores the clock of the last
|
||||
/// thread to release this mutex.
|
||||
data_race: VClock
|
||||
data_race: VClock,
|
||||
}
|
||||
|
||||
declare_id!(RwLockId);
|
||||
@ -98,7 +98,7 @@ struct RwLock {
|
||||
/// is stored to the main data_race variable once all
|
||||
/// readers are finished.
|
||||
/// Has to be stored separately since reader lock acquires
|
||||
/// must load the clock of the last write and must not
|
||||
/// must load the clock of the last write and must not
|
||||
/// add happens-before orderings between shared reader
|
||||
/// locks.
|
||||
data_race_reader: VClock,
|
||||
@ -251,11 +251,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
/// count. If the lock count reaches 0, release the lock and potentially
|
||||
/// give to a new owner. If the lock was not locked by `expected_owner`,
|
||||
/// return `None`.
|
||||
fn mutex_unlock(
|
||||
&mut self,
|
||||
id: MutexId,
|
||||
expected_owner: ThreadId,
|
||||
) -> Option<usize> {
|
||||
fn mutex_unlock(&mut self, id: MutexId, expected_owner: ThreadId) -> Option<usize> {
|
||||
let this = self.eval_context_mut();
|
||||
let mutex = &mut this.machine.threads.sync.mutexes[id];
|
||||
if let Some(current_owner) = mutex.owner {
|
||||
@ -307,9 +303,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let rwlock = &this.machine.threads.sync.rwlocks[id];
|
||||
trace!(
|
||||
"rwlock_is_locked: {:?} writer is {:?} and there are {} reader threads (some of which could hold multiple read locks)",
|
||||
id, rwlock.writer, rwlock.readers.len(),
|
||||
id,
|
||||
rwlock.writer,
|
||||
rwlock.readers.len(),
|
||||
);
|
||||
rwlock.writer.is_some()|| rwlock.readers.is_empty().not()
|
||||
rwlock.writer.is_some() || rwlock.readers.is_empty().not()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -360,7 +358,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
|
||||
// The thread was a reader. If the lock is not held any more, give it to a writer.
|
||||
if this.rwlock_is_locked(id).not() {
|
||||
|
||||
// All the readers are finished, so set the writer data-race handle to the value
|
||||
// of the union of all reader data race handles, since the set of readers
|
||||
// happen-before the writers
|
||||
@ -373,11 +370,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
|
||||
#[inline]
|
||||
/// Put the reader in the queue waiting for the lock and block it.
|
||||
fn rwlock_enqueue_and_block_reader(
|
||||
&mut self,
|
||||
id: RwLockId,
|
||||
reader: ThreadId,
|
||||
) {
|
||||
fn rwlock_enqueue_and_block_reader(&mut self, id: RwLockId, reader: ThreadId) {
|
||||
let this = self.eval_context_mut();
|
||||
assert!(this.rwlock_is_write_locked(id), "read-queueing on not write locked rwlock");
|
||||
this.machine.threads.sync.rwlocks[id].reader_queue.push_back(reader);
|
||||
@ -437,11 +430,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
|
||||
#[inline]
|
||||
/// Put the writer in the queue waiting for the lock.
|
||||
fn rwlock_enqueue_and_block_writer(
|
||||
&mut self,
|
||||
id: RwLockId,
|
||||
writer: ThreadId,
|
||||
) {
|
||||
fn rwlock_enqueue_and_block_writer(&mut self, id: RwLockId, writer: ThreadId) {
|
||||
let this = self.eval_context_mut();
|
||||
assert!(this.rwlock_is_locked(id), "write-queueing on unlocked rwlock");
|
||||
this.machine.threads.sync.rwlocks[id].writer_queue.push_back(writer);
|
||||
@ -482,14 +471,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
if let Some(data_race) = data_race {
|
||||
data_race.validate_lock_release(&mut condvar.data_race, current_thread);
|
||||
}
|
||||
condvar.waiters
|
||||
.pop_front()
|
||||
.map(|waiter| {
|
||||
if let Some(data_race) = data_race {
|
||||
data_race.validate_lock_acquire(&mut condvar.data_race, waiter.thread);
|
||||
}
|
||||
(waiter.thread, waiter.mutex)
|
||||
})
|
||||
condvar.waiters.pop_front().map(|waiter| {
|
||||
if let Some(data_race) = data_race {
|
||||
data_race.validate_lock_acquire(&mut condvar.data_race, waiter.thread);
|
||||
}
|
||||
(waiter.thread, waiter.mutex)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -511,7 +498,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let this = self.eval_context_mut();
|
||||
let current_thread = this.get_active_thread();
|
||||
let futex = &mut this.machine.threads.sync.futexes.get_mut(&addr.erase_tag())?;
|
||||
let data_race = &this.memory.extra.data_race;
|
||||
let data_race = &this.memory.extra.data_race;
|
||||
|
||||
// Each futex-wake happens-before the end of the futex wait
|
||||
if let Some(data_race) = data_race {
|
||||
@ -519,7 +506,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
let res = futex.waiters.pop_front().map(|waiter| {
|
||||
if let Some(data_race) = data_race {
|
||||
data_race.validate_lock_acquire(&futex.data_race, waiter.thread);
|
||||
data_race.validate_lock_acquire(&futex.data_race, waiter.thread);
|
||||
}
|
||||
waiter.thread
|
||||
});
|
||||
|
@ -3,8 +3,8 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::convert::TryFrom;
|
||||
use std::rc::Rc;
|
||||
use std::num::TryFromIntError;
|
||||
use std::rc::Rc;
|
||||
use std::time::{Duration, Instant, SystemTime};
|
||||
|
||||
use log::trace;
|
||||
@ -141,17 +141,19 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> {
|
||||
|
||||
/// Get the name of the current thread, or `<unnamed>` if it was not set.
|
||||
fn thread_name(&self) -> &[u8] {
|
||||
if let Some(ref thread_name) = self.thread_name {
|
||||
thread_name
|
||||
} else {
|
||||
b"<unnamed>"
|
||||
}
|
||||
if let Some(ref thread_name) = self.thread_name { thread_name } else { b"<unnamed>" }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx> std::fmt::Debug for Thread<'mir, 'tcx> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}({:?}, {:?})", String::from_utf8_lossy(self.thread_name()), self.state, self.join_status)
|
||||
write!(
|
||||
f,
|
||||
"{}({:?}, {:?})",
|
||||
String::from_utf8_lossy(self.thread_name()),
|
||||
self.state,
|
||||
self.join_status
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,7 +330,11 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
|
||||
}
|
||||
|
||||
/// Mark that the active thread tries to join the thread with `joined_thread_id`.
|
||||
fn join_thread(&mut self, joined_thread_id: ThreadId, data_race: &Option<Rc<data_race::GlobalState>>) -> InterpResult<'tcx> {
|
||||
fn join_thread(
|
||||
&mut self,
|
||||
joined_thread_id: ThreadId,
|
||||
data_race: &Option<Rc<data_race::GlobalState>>,
|
||||
) -> InterpResult<'tcx> {
|
||||
if self.threads[joined_thread_id].join_status != ThreadJoinStatus::Joinable {
|
||||
throw_ub_format!("trying to join a detached or already joined thread");
|
||||
}
|
||||
@ -431,7 +437,10 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
|
||||
|
||||
/// Wakes up threads joining on the active one and deallocates thread-local statics.
|
||||
/// The `AllocId` that can now be freed is returned.
|
||||
fn thread_terminated(&mut self, data_race: &Option<Rc<data_race::GlobalState>>) -> Vec<AllocId> {
|
||||
fn thread_terminated(
|
||||
&mut self,
|
||||
data_race: &Option<Rc<data_race::GlobalState>>,
|
||||
) -> Vec<AllocId> {
|
||||
let mut free_tls_statics = Vec::new();
|
||||
{
|
||||
let mut thread_local_statics = self.thread_local_alloc_ids.borrow_mut();
|
||||
@ -470,7 +479,10 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
|
||||
/// used in stateless model checkers such as Loom: run the active thread as
|
||||
/// long as we can and switch only when we have to (the active thread was
|
||||
/// blocked, terminated, or has explicitly asked to be preempted).
|
||||
fn schedule(&mut self, data_race: &Option<Rc<data_race::GlobalState>>) -> InterpResult<'tcx, SchedulingAction> {
|
||||
fn schedule(
|
||||
&mut self,
|
||||
data_race: &Option<Rc<data_race::GlobalState>>,
|
||||
) -> InterpResult<'tcx, SchedulingAction> {
|
||||
// Check whether the thread has **just** terminated (`check_terminated`
|
||||
// checks whether the thread has popped all its stack and if yes, sets
|
||||
// the thread state to terminated).
|
||||
@ -546,7 +558,10 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mi
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
/// Get a thread-specific allocation id for the given thread-local static.
|
||||
/// If needed, allocate a new one.
|
||||
fn get_or_create_thread_local_alloc_id(&mut self, def_id: DefId) -> InterpResult<'tcx, AllocId> {
|
||||
fn get_or_create_thread_local_alloc_id(
|
||||
&mut self,
|
||||
def_id: DefId,
|
||||
) -> InterpResult<'tcx, AllocId> {
|
||||
let this = self.eval_context_mut();
|
||||
let tcx = this.tcx;
|
||||
if let Some(new_alloc_id) = this.machine.threads.get_thread_local_alloc_id(def_id) {
|
||||
@ -562,7 +577,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
}
|
||||
let allocation = tcx.eval_static_initializer(def_id)?;
|
||||
// Create a fresh allocation with this content.
|
||||
let new_alloc_id = this.memory.allocate_with(allocation.clone(), MiriMemoryKind::Tls.into()).alloc_id;
|
||||
let new_alloc_id =
|
||||
this.memory.allocate_with(allocation.clone(), MiriMemoryKind::Tls.into()).alloc_id;
|
||||
this.machine.threads.set_thread_local_alloc_id(def_id, new_alloc_id);
|
||||
Ok(new_alloc_id)
|
||||
}
|
||||
@ -654,9 +670,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
||||
let this = self.eval_context_mut();
|
||||
if let Some(data_race) = &this.memory.extra.data_race {
|
||||
if let Ok(string) = String::from_utf8(new_thread_name.clone()) {
|
||||
data_race.thread_set_name(
|
||||
this.machine.threads.active_thread, string
|
||||
);
|
||||
data_race.thread_set_name(this.machine.threads.active_thread, string);
|
||||
}
|
||||
}
|
||||
this.machine.threads.set_thread_name(new_thread_name);
|
||||
|
@ -1,11 +1,6 @@
|
||||
use rustc_index::vec::Idx;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
convert::TryFrom,
|
||||
fmt::Debug,
|
||||
ops::Index,
|
||||
};
|
||||
use std::{cmp::Ordering, convert::TryFrom, fmt::Debug, ops::Index};
|
||||
|
||||
/// A vector clock index, this is associated with a thread id
|
||||
/// but in some cases one vector index may be shared with
|
||||
|
Loading…
x
Reference in New Issue
Block a user