X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fhelpers.rs;h=4c989db0170b5408c71116555a8fcfc0702aab17;hb=296ba8b1c84f65d123fa5b35f6f2e47e73710b08;hp=7b1ac1aacfc8be50acf0e790df18e7040734a47e;hpb=5ce2466892126a3ea92138df67f09243a22dcb96;p=rust.git diff --git a/src/helpers.rs b/src/helpers.rs index 7b1ac1aacfc..4c989db0170 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,21 +1,20 @@ -use std::convert::TryFrom; +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, - layout::{self, LayoutOf, Size, TyAndLayout}, - List, TyCtxt, -}; +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 rand::RngCore; use crate::*; -impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} /// Gets an instance for a path. fn try_resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> Option { @@ -56,11 +55,11 @@ fn resolve_path(&self, path: &[&str]) -> ty::Instance<'tcx> { fn eval_path_scalar( &mut self, path: &[&str], - ) -> InterpResult<'tcx, ScalarMaybeUndef> { + ) -> 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.eval_to_allocation(cid)?; let const_val = this.read_scalar(const_val.into())?; return Ok(const_val); } @@ -69,7 +68,7 @@ fn eval_path_scalar( fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar> { self.eval_context_mut() .eval_path_scalar(&["libc", name])? - .not_undef() + .check_init() } /// Helper function to get a `libc` constant as an `i32`. @@ -82,7 +81,7 @@ fn eval_libc_i32(&mut self, name: &str) -> InterpResult<'tcx, i32> { fn eval_windows(&mut self, module: &str, name: &str) -> InterpResult<'tcx, Scalar> { self.eval_context_mut() .eval_path_scalar(&["std", "sys", "windows", module, name])? - .not_undef() + .check_init() } /// Helper function to get a `windows` constant as an `u64`. @@ -94,14 +93,14 @@ fn eval_windows_u64(&mut self, module: &str, name: &str) -> InterpResult<'tcx, u /// Helper function to get the `TyAndLayout` of a `libc` type fn libc_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyAndLayout<'tcx>> { let this = self.eval_context_mut(); - let ty = this.resolve_path(&["libc", name]).monomorphic_ty(*this.tcx); + let ty = this.resolve_path(&["libc", name]).ty(*this.tcx, ty::ParamEnv::reveal_all()); this.layout_of(ty) } /// 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]).monomorphic_ty(*this.tcx); + let ty = this.resolve_path(&["std", "sys", "windows", "c", name]).ty(*this.tcx, ty::ParamEnv::reveal_all()); this.layout_of(ty) } @@ -267,7 +266,7 @@ struct UnsafeCellVisitor<'ecx, 'mir, 'tcx, F> unsafe_cell_action: F, } - impl<'ecx, 'mir, 'tcx, F> ValueVisitor<'mir, 'tcx, Evaluator<'tcx>> + 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>, @@ -282,7 +281,7 @@ fn ecx(&self) -> &MiriEvalContext<'mir, 'tcx> { // Hook to detect `UnsafeCell`. 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, @@ -293,12 +292,15 @@ fn visit_value(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { } else if self.ecx.type_is_freeze(v.layout.ty) { // This is `Freeze`, there cannot be an `UnsafeCell` Ok(()) + } else if matches!(v.layout.fields, FieldsShape::Union(..)) { + // A (non-frozen) union. We fall back to whatever the type says. + (self.unsafe_cell_action)(v) } else { // We want to not actually read from memory for this visit. So, before // walking this value, we have to make sure it is not a // `Variants::Multiple`. match v.layout.variants { - layout::Variants::Multiple { .. } => { + Variants::Multiple { .. } => { // A multi-variant enum, or generator, or so. // Treat this like a union: without reading from memory, // we cannot determine the variant we are in. Reading from @@ -308,7 +310,7 @@ fn visit_value(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { // `UnsafeCell` action. (self.unsafe_cell_action)(v) } - layout::Variants::Single { .. } => { + Variants::Single { .. } => { // Proceed further, try to find where exactly that `UnsafeCell` // is hiding. self.walk_value(v) @@ -324,34 +326,27 @@ fn visit_aggregate( fields: impl Iterator>>, ) -> InterpResult<'tcx> { match place.layout.fields { - layout::FieldsShape::Array { .. } => { + FieldsShape::Array { .. } => { // For the array layout, we know the iterator will yield sorted elements so // we can avoid the allocation. self.walk_aggregate(place, fields) } - layout::FieldsShape::Arbitrary { .. } => { + FieldsShape::Arbitrary { .. } => { // Gather the subplaces and sort them before visiting. let mut places = fields.collect::>>>()?; places.sort_by_key(|place| place.ptr.assert_ptr().offset); self.walk_aggregate(place, places.into_iter().map(Ok)) } - layout::FieldsShape::Union { .. } => { + FieldsShape::Union { .. } | FieldsShape::Primitive => { // Uh, what? - bug!("a union is not an aggregate we should ever visit") + bug!("unions/primitives are not aggregates we should ever visit") } } } - // We have to do *something* for unions. - fn visit_union(&mut self, v: MPlaceTy<'tcx, Tag>, fields: usize) -> InterpResult<'tcx> { - assert!(fields > 0); // we should never reach "pseudo-unions" with 0 fields, like primitives - - // With unions, we fall back to whatever the type says, to hopefully be consistent - // with LLVM IR. - // FIXME: are we consistent, and is this really the behavior we want? - let frozen = self.ecx.type_is_freeze(v.layout.ty); - if frozen { Ok(()) } else { (self.unsafe_cell_action)(v) } + fn visit_union(&mut self, _v: MPlaceTy<'tcx, Tag>, _fields: NonZeroUsize) -> InterpResult<'tcx> { + bug!("we should have already handled unions in `visit_value`") } } } @@ -382,19 +377,17 @@ fn write_packed_immediates( /// case. fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> { if !self.eval_context_ref().machine.communicate { - throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!( - "`{}` not available when isolation is enabled", - name, - ))) + isolation_error(name)?; } Ok(()) } + /// Helper function used inside the shims of foreign functions to assert that the target OS /// is `target_os`. It panics showing a message with the `name` of the foreign function /// 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, @@ -402,18 +395,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(); + 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())?.not_undef() + 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 @@ -421,9 +430,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.os_family == Some("unix".to_owned()) { this.eval_libc(match e.kind() { ConnectionRefused => "ECONNREFUSED", ConnectionReset => "ECONNRESET", @@ -473,6 +482,82 @@ fn try_unwrap_io_result>( } } } + + fn read_scalar_at_offset( + &self, + op: OpTy<'tcx, Tag>, + offset: u64, + layout: TyAndLayout<'tcx>, + ) -> InterpResult<'tcx, ScalarMaybeUninit> { + let this = self.eval_context_ref(); + let op_place = this.deref_operand(op)?; + let offset = Size::from_bytes(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()) + } + + fn write_scalar_at_offset( + &mut self, + op: OpTy<'tcx, Tag>, + offset: u64, + value: impl Into>, + layout: TyAndLayout<'tcx>, + ) -> InterpResult<'tcx, ()> { + let this = self.eval_context_mut(); + let op_place = this.deref_operand(op)?; + let offset = Size::from_bytes(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()) + } + + /// 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) + }) + } +} + +/// 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>]> { + if let Ok(ops) = args.try_into() { + return Ok(ops); + } + throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N) +} + +pub fn isolation_error(name: &str) -> InterpResult<'static> { + throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!( + "{} not available when isolation is enabled", + name, + ))) } pub fn immty_from_int_checked<'tcx>(