X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fhelpers.rs;h=7f99aa1997068ae3099f0d01112423af4b71e488;hb=879000b133aed8cc1893c84eb5319b491a4756d9;hp=0426115773d9e6099595f0dd5fa41910d8259bdb;hpb=5657f08bea978ddd9114c5a25a07e115ee609425;p=rust.git diff --git a/src/helpers.rs b/src/helpers.rs index 0426115773d..7f99aa19970 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,13 +1,15 @@ use std::convert::{TryFrom, TryInto}; use std::mem; use std::num::NonZeroUsize; +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::{Align, FieldsShape, LayoutOf, Size, Variants}; +use rustc_target::spec::abi::Abi; use rand::RngCore; @@ -17,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 { - 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(); @@ -38,7 +38,8 @@ fn try_resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> Option: crate::MiriEvalContextExt<'mir, 'tcx> { @@ -51,23 +52,18 @@ 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> { + fn eval_path_scalar(&mut self, path: &[&str]) -> InterpResult<'tcx, ScalarMaybeUninit> { 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> { - 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`. @@ -99,12 +95,14 @@ fn libc_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyAndLayout<'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) } /// 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) } @@ -159,11 +157,21 @@ fn gen_random(&mut self, ptr: Scalar, len: u64) -> InterpResult<'tcx> { fn call_function( &mut self, f: ty::Instance<'tcx>, + caller_abi: Abi, args: &[Immediate], - dest: Option>, + 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)?; @@ -173,11 +181,15 @@ fn call_function( 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(()) } @@ -186,7 +198,7 @@ fn call_function( /// 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, Size, bool) -> InterpResult<'tcx>, ) -> InterpResult<'tcx> { @@ -235,7 +247,7 @@ fn visit_freeze_sensitive( 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); @@ -259,7 +271,7 @@ fn visit_freeze_sensitive( /// 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, @@ -268,7 +280,7 @@ struct UnsafeCellVisitor<'ecx, 'mir, 'tcx, 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>; @@ -278,9 +290,9 @@ fn ecx(&self) -> &MiriEvalContext<'mir, 'tcx> { } // 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 { + let is_unsafe_cell = match v.layout.ty.kind() { ty::Adt(adt, _) => Some(adt.did) == self.ecx.tcx.lang_items().unsafe_cell_type(), _ => false, @@ -321,7 +333,7 @@ fn visit_value(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { // 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>>, ) -> InterpResult<'tcx> { match place.layout.fields { @@ -344,7 +356,11 @@ fn visit_aggregate( } } - 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`") } } @@ -354,7 +370,7 @@ fn visit_union(&mut self, _v: MPlaceTy<'tcx, Tag>, _fields: NonZeroUsize) -> Int // 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(); @@ -364,7 +380,7 @@ fn write_packed_immediates( 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; } @@ -386,7 +402,7 @@ fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> { /// 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, @@ -394,18 +410,34 @@ fn assert_target_os(&self, target_os: &str, name: &str) { ) } + /// Get last error variable as a place, lazily allocating thread-local storage for it if + /// necessary. + fn last_error_place(&mut self) -> InterpResult<'tcx, MPlaceTy<'tcx, Tag>> { + let this = self.eval_context_mut(); + if let Some(errno_place) = this.active_thread_ref().last_error { + Ok(errno_place) + } else { + // 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.active_thread_mut().last_error = Some(errno_place); + Ok(errno_place) + } + } + /// Sets the last error variable. fn set_last_error(&mut self, scalar: Scalar) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let errno_place = this.machine.last_error.unwrap(); - this.write_scalar(scalar, errno_place.into()) + let errno_place = this.last_error_place()?; + this.write_scalar(scalar, &errno_place.into()) } /// Gets the last error variable. - fn get_last_error(&self) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_ref(); - let errno_place = this.machine.last_error.unwrap(); - this.read_scalar(errno_place.into())?.check_init() + fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + let errno_place = this.last_error_place()?; + 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 @@ -413,9 +445,9 @@ fn get_last_error(&self) -> InterpResult<'tcx, Scalar> { 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", @@ -435,14 +467,23 @@ fn set_last_error_from_io_error(&mut self, e: std::io::Error) -> InterpResult<'t 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) } @@ -468,7 +509,7 @@ fn try_unwrap_io_result>( fn read_scalar_at_offset( &self, - op: OpTy<'tcx, Tag>, + op: &OpTy<'tcx, Tag>, offset: u64, layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, ScalarMaybeUninit> { @@ -478,12 +519,12 @@ fn read_scalar_at_offset( // 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>, layout: TyAndLayout<'tcx>, @@ -494,13 +535,114 @@ fn write_scalar_at_offset( // 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`. 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, Option> { + 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 = 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 = nanoseconds_scalar.to_machine_isize(this)?; + + 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? + } + Duration::new(seconds, nanoseconds) + }) + } + + fn read_c_str<'a>(&'a self, sptr: Scalar) -> 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) -> InterpResult<'tcx, Vec> { + 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); } @@ -509,7 +651,7 @@ fn write_scalar_at_offset( 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, ))) }