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::{Align, FieldsShape, LayoutOf, Size, Variants};
+use rustc_target::spec::abi::Abi;
use rand::RngCore;
/// 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.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();
}
}
None
- })
+ },
+ )
}
-/// This error indicates that the value in a `timespec` C struct was invalid.
-pub struct TimespecError;
-
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
/// Gets an instance for a path.
fn resolve_path(&self, path: &[&str]) -> ty::Instance<'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 };
- let const_val = this.const_eval_raw(cid)?;
- let const_val = this.read_scalar(const_val.into())?;
+ let const_val = this.eval_to_allocation(cid)?;
+ let const_val = this.read_scalar(&const_val.into())?;
return Ok(const_val);
}
/// 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`.
/// 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)
}
/// Write a 0 of the appropriate size to `dest`.
- fn write_null(&mut self, dest: PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
+ fn write_null(&mut self, dest: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
self.eval_context_mut().write_scalar(Scalar::from_int(0, dest.layout.size), dest)
}
fn call_function(
&mut self,
f: ty::Instance<'tcx>,
+ caller_abi: Abi,
args: &[Immediate<Tag>],
- dest: Option<PlaceTy<'tcx, Tag>>,
+ dest: Option<&PlaceTy<'tcx, Tag>>,
stack_pop: StackPopCleanup,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
+ 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 this.machine.enforce_abi && callee_abi != caller_abi {
+ throw_ub_format!(
+ "calling a function with ABI {} using caller ABI {}",
+ callee_abi.name(),
+ caller_abi.name()
+ )
+ }
// Push frame.
let mir = &*this.load_mir(f.def, None)?;
let mut callee_args = this.frame().body.args_iter();
for arg in args {
let callee_arg = this.local_place(
- callee_args.next().expect("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)?;
+ this.write_immediate(*arg, &callee_arg)?;
+ }
+ if callee_args.next().is_some() {
+ throw_ub_format!("callee has more arguments than expected");
}
- callee_args.next().expect_none("callee has more arguments than expected");
Ok(())
}
/// will be true if this is frozen, false if this is in an `UnsafeCell`.
fn visit_freeze_sensitive(
&self,
- place: MPlaceTy<'tcx, Tag>,
+ place: &MPlaceTy<'tcx, Tag>,
size: Size,
mut action: impl FnMut(Pointer<Tag>, Size, bool) -> InterpResult<'tcx>,
) -> InterpResult<'tcx> {
trace!("unsafe_cell_action on {:?}", place.ptr);
// We need a size to go on.
let unsafe_cell_size = this
- .size_and_align_of_mplace(place)?
+ .size_and_align_of_mplace(&place)?
.map(|(size, _)| size)
// for extern types, just cover what we can
.unwrap_or_else(|| place.layout.size);
/// whether we are inside an `UnsafeCell` or not.
struct UnsafeCellVisitor<'ecx, 'mir, 'tcx, F>
where
- F: FnMut(MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>,
+ F: FnMut(&MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>,
{
ecx: &'ecx MiriEvalContext<'mir, 'tcx>,
unsafe_cell_action: F,
impl<'ecx, 'mir, 'tcx: 'mir, F> ValueVisitor<'mir, 'tcx, Evaluator<'mir, 'tcx>>
for UnsafeCellVisitor<'ecx, 'mir, 'tcx, F>
where
- F: FnMut(MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>,
+ F: FnMut(&MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>,
{
type V = MPlaceTy<'tcx, Tag>;
}
// Hook to detect `UnsafeCell`.
- fn visit_value(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
+ fn visit_value(&mut self, v: &MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
let is_unsafe_cell = match v.layout.ty.kind() {
ty::Adt(adt, _) =>
// Make sure we visit aggregrates in increasing offset order.
fn visit_aggregate(
&mut self,
- place: MPlaceTy<'tcx, Tag>,
+ place: &MPlaceTy<'tcx, Tag>,
fields: impl Iterator<Item = InterpResult<'tcx, MPlaceTy<'tcx, Tag>>>,
) -> InterpResult<'tcx> {
match place.layout.fields {
}
}
- 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`")
}
}
// different values into a struct.
fn write_packed_immediates(
&mut self,
- place: MPlaceTy<'tcx, Tag>,
+ place: &MPlaceTy<'tcx, Tag>,
imms: &[ImmTy<'tcx, Tag>],
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
for &imm in imms {
this.write_immediate_to_mplace(
*imm,
- place.offset(offset, MemPlaceMeta::None, imm.layout, &*this.tcx)?,
+ &place.offset(offset, MemPlaceMeta::None, imm.layout, &*this.tcx)?,
)?;
offset += imm.layout.size;
}
/// if this is not the case.
fn assert_target_os(&self, target_os: &str, name: &str) {
assert_eq!(
- self.eval_context_ref().tcx.sess.target.target.target_os,
+ self.eval_context_ref().tcx.sess.target.os,
target_os,
"`{}` is only available on the `{}` target OS",
name,
// Allocate new place, set initial value to 0.
let errno_layout = this.machine.layouts.u32;
let errno_place = this.allocate(errno_layout, MiriMemoryKind::Machine.into());
- this.write_scalar(Scalar::from_u32(0), errno_place.into())?;
+ this.write_scalar(Scalar::from_u32(0), &errno_place.into())?;
this.active_thread_mut().last_error = Some(errno_place);
Ok(errno_place)
}
fn set_last_error(&mut self, scalar: Scalar<Tag>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let errno_place = this.last_error_place()?;
- this.write_scalar(scalar, errno_place.into())
+ this.write_scalar(scalar, &errno_place.into())
}
/// Gets the last error variable.
fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar<Tag>> {
let this = self.eval_context_mut();
let errno_place = this.last_error_place()?;
- this.read_scalar(errno_place.into())?.check_init()
+ this.read_scalar(&errno_place.into())?.check_init()
}
/// Sets the last OS error using a `std::io::Error`. This function tries to produce the most
fn set_last_error_from_io_error(&mut self, e: std::io::Error) -> InterpResult<'tcx> {
use std::io::ErrorKind::*;
let this = self.eval_context_mut();
- let target = &this.tcx.sess.target.target;
- let target_os = &target.target_os;
- let last_error = if target.options.target_family == Some("unix".to_owned()) {
+ let target = &this.tcx.sess.target;
+ let target_os = &target.os;
+ let last_error = if target.families.contains(&"unix".to_owned()) {
this.eval_libc(match e.kind() {
ConnectionRefused => "ECONNREFUSED",
ConnectionReset => "ECONNRESET",
throw_unsup_format!("io error {} cannot be transformed into a raw os error", e)
}
})?
- } else if target_os == "windows" {
+ } 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)
}
fn read_scalar_at_offset(
&self,
- op: OpTy<'tcx, Tag>,
+ op: &OpTy<'tcx, Tag>,
offset: u64,
layout: TyAndLayout<'tcx>,
) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
// Ensure that the following read at an offset is within bounds
assert!(op_place.layout.size >= offset + layout.size);
let value_place = op_place.offset(offset, MemPlaceMeta::None, layout, this)?;
- this.read_scalar(value_place.into())
+ this.read_scalar(&value_place.into())
}
fn write_scalar_at_offset(
&mut self,
- op: OpTy<'tcx, Tag>,
+ op: &OpTy<'tcx, Tag>,
offset: u64,
value: impl Into<ScalarMaybeUninit<Tag>>,
layout: TyAndLayout<'tcx>,
// Ensure that the following read at an offset is within bounds
assert!(op_place.layout.size >= offset + layout.size);
let value_place = op_place.offset(offset, MemPlaceMeta::None, layout, this)?;
- this.write_scalar(value, value_place.into())
+ this.write_scalar(value, &value_place.into())
}
- /// Parse a `timespec` struct and return it as a `std::time::Duration`. The outer `Result` is
- /// for interpreter errors encountered while reading memory, and the inner `Result` indicates
- /// whether the value in the `timespec` struct is invalid. Some libc functions will return
- /// `EINVAL` if the struct's value is invalid.
+ /// Parse a `timespec` struct and return it as a `std::time::Duration`. It returns `None`
+ /// if the value in the `timespec` struct is invalid. Some libc functions will return
+ /// `EINVAL` in this case.
fn read_timespec(
&mut self,
- timespec_ptr_op: OpTy<'tcx, Tag>,
- ) -> InterpResult<'tcx, Result<Duration, TimespecError>> {
+ timespec_ptr_op: &OpTy<'tcx, Tag>,
+ ) -> InterpResult<'tcx, Option<Duration>> {
let this = self.eval_context_mut();
let tp = this.deref_operand(timespec_ptr_op)?;
- let seconds_place = this.mplace_field(tp, 0)?;
- let seconds_scalar = this.read_scalar(seconds_place.into())?;
+ let seconds_place = this.mplace_field(&tp, 0)?;
+ let seconds_scalar = this.read_scalar(&seconds_place.into())?;
let seconds = seconds_scalar.to_machine_isize(this)?;
- let nanoseconds_place = this.mplace_field(tp, 1)?;
- let nanoseconds_scalar = this.read_scalar(nanoseconds_place.into())?;
+ let nanoseconds_place = this.mplace_field(&tp, 1)?;
+ let nanoseconds_scalar = this.read_scalar(&nanoseconds_place.into())?;
let nanoseconds = nanoseconds_scalar.to_machine_isize(this)?;
- let seconds: u64 = if let Ok(s) = seconds.try_into() {
- s
- } else {
- return Ok(Err(TimespecError));
- };
- let nanoseconds: u32 = if let Ok(ns) = nanoseconds.try_into() {
- if ns >= 1_000_000_000 {
- return Ok(Err(TimespecError));
+ Ok(try {
+ // tv_sec must be non-negative.
+ let seconds: u64 = seconds.try_into().ok()?;
+ // tv_nsec must be non-negative.
+ let nanoseconds: u32 = nanoseconds.try_into().ok()?;
+ if nanoseconds >= 1_000_000_000 {
+ // tv_nsec must not be greater than 999,999,999.
+ None?
}
- ns
- } else {
- return Ok(Err(TimespecError));
- };
- Ok(Ok(Duration::new(seconds, nanoseconds)))
+ Duration::new(seconds, nanoseconds)
+ })
+ }
+
+ fn read_c_str<'a>(&'a self, sptr: Scalar<Tag>) -> InterpResult<'tcx, &'a [u8]>
+ where
+ 'tcx: 'a,
+ 'mir: 'a,
+ {
+ let this = self.eval_context_ref();
+ let size1 = Size::from_bytes(1);
+ let ptr = this.force_ptr(sptr)?; // We need to read at least 1 byte, so we can eagerly get a ptr.
+
+ // Step 1: determine the length.
+ let mut len = Size::ZERO;
+ loop {
+ // FIXME: We are re-getting the allocation each time around the loop.
+ // Would be nice if we could somehow "extend" an existing AllocRange.
+ let alloc = this.memory.get(ptr.offset(len, this)?.into(), size1, Align::ONE)?.unwrap(); // not a ZST, so we will get a result
+ let byte = alloc.read_scalar(alloc_range(Size::ZERO, size1))?.to_u8()?;
+ if byte == 0 {
+ break;
+ } else {
+ len = len + size1;
+ }
+ }
+
+ // Step 2: get the bytes.
+ this.memory.read_bytes(ptr.into(), len)
+ }
+
+ fn read_wide_str(&self, sptr: Scalar<Tag>) -> InterpResult<'tcx, Vec<u16>> {
+ let this = self.eval_context_ref();
+ let size2 = Size::from_bytes(2);
+ let align2 = Align::from_bytes(2).unwrap();
+
+ let mut ptr = this.force_ptr(sptr)?; // We need to read at least 1 wchar, so we can eagerly get a ptr.
+ let mut wchars = Vec::new();
+ loop {
+ // FIXME: We are re-getting the allocation each time around the loop.
+ // Would be nice if we could somehow "extend" an existing AllocRange.
+ let alloc = this.memory.get(ptr.into(), size2, align2)?.unwrap(); // not a ZST, so we will get a result
+ let wchar = alloc.read_scalar(alloc_range(Size::ZERO, size2))?.to_u16()?;
+ if wchar == 0 {
+ break;
+ } else {
+ wchars.push(wchar);
+ ptr = ptr.offset(size2, this)?;
+ }
+ }
+
+ Ok(wchars)
+ }
+
+ /// Check that the ABI is what we expect.
+ fn check_abi<'a>(&self, abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> {
+ if self.eval_context_ref().machine.enforce_abi && abi != exp_abi {
+ throw_ub_format!(
+ "calling a function with ABI {} using caller ABI {}",
+ exp_abi.name(),
+ abi.name()
+ )
+ }
+ Ok(())
+ }
+
+ fn in_std(&self) -> bool {
+ let this = self.eval_context_ref();
+ this.tcx.def_path(this.frame().instance.def_id()).krate
+ == this.tcx.def_path(this.tcx.lang_items().start_fn().unwrap()).krate
}
}
/// 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);
}
pub fn isolation_error(name: &str) -> InterpResult<'static> {
throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
- "`{}` not available when isolation is enabled",
+ "{} not available when isolation is enabled",
name,
)))
}