format much of Miri

This commit is contained in:
Ralf Jung 2021-05-16 11:28:01 +02:00
parent 7b3566096c
commit 4e231bab5e
35 changed files with 750 additions and 512 deletions

View File

@ -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;

View File

@ -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,
)
}

View File

@ -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)
})();

View File

@ -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()
)
}
}

View File

@ -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::*;

View File

@ -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.

View File

@ -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)]

View File

@ -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]
);

View File

@ -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"
);
}
}

View File

@ -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),
}
}
}

View File

@ -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(())
}

View File

@ -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> {}

View File

@ -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() {

View File

@ -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
};
}
}

View File

@ -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.

View File

@ -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)
}

View File

@ -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),
})
}

View File

@ -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)?;

View File

@ -1,3 +1,3 @@
pub mod foreign_items;
pub mod dlsym;
pub mod foreign_items;
pub mod sync;

View File

@ -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);

View File

@ -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)
}
}

View File

@ -1,2 +1,2 @@
pub mod foreign_items;
pub mod dlsym;
pub mod foreign_items;

View File

@ -1,5 +1,5 @@
pub mod foreign_items;
pub mod dlsym;
pub mod foreign_items;
mod fs;
mod sync;

View File

@ -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,
)
}

View File

@ -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");

View File

@ -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)?;

View File

@ -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!

View File

@ -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

View File

@ -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)
}
}

View File

@ -1,4 +1,4 @@
pub mod foreign_items;
pub mod dlsym;
pub mod foreign_items;
mod sync;

View File

@ -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(())

View File

@ -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());

View File

@ -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
});

View File

@ -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);

View File

@ -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