X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fhelpers.rs;h=f7be3de8e481227ba2d0077d757ec4d146bf9822;hb=cf9340113efcf5c7bea4a143f42c8ea47da4550e;hp=351a6e7e54a6f002ec05c9373ae8c28805e8f254;hpb=fe9056ee77f9a6804ec6ca7e0471bfe8e250cfb5;p=rust.git diff --git a/src/helpers.rs b/src/helpers.rs index 351a6e7e54a..f7be3de8e48 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,7 +1,12 @@ -use std::mem; +use std::{mem, iter}; +use std::ffi::{OsStr, OsString}; -use rustc::ty::{self, layout::{self, Size, Align}}; use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX}; +use rustc::mir; +use rustc::ty::{ + self, + layout::{self, LayoutOf, Size, TyLayout}, +}; use rand::RngCore; @@ -41,7 +46,7 @@ fn resolve_path(&self, path: &[&str]) -> InterpResult<'tcx, ty::Instance<'tcx>> }) .ok_or_else(|| { let path = path.iter().map(|&s| s.to_owned()).collect(); - InterpError::PathNotFound(path).into() + err_unsup!(PathNotFound(path)).into() }) } @@ -53,7 +58,7 @@ fn write_null(&mut self, dest: PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { /// Test if this immediate equals 0. fn is_null(&self, val: Scalar) -> InterpResult<'tcx, bool> { let this = self.eval_context_ref(); - let null = Scalar::from_int(0, this.memory().pointer_size()); + let null = Scalar::from_int(0, this.memory.pointer_size()); this.ptr_eq(val, null) } @@ -67,38 +72,42 @@ fn test_null(&self, val: Scalar) -> InterpResult<'tcx, Option>> }) } + /// Get the `Place` for a local + fn local_place(&mut self, local: mir::Local) -> InterpResult<'tcx, PlaceTy<'tcx, Tag>> { + let this = self.eval_context_mut(); + let place = mir::Place { base: mir::PlaceBase::Local(local), projection: Box::new([]) }; + this.eval_place(&place) + } + /// Generate some random bytes, and write them to `dest`. fn gen_random( &mut self, - len: usize, ptr: Scalar, + len: usize, ) -> InterpResult<'tcx> { + // Some programs pass in a null pointer and a length of 0 + // to their platform's random-generation function (e.g. getrandom()) + // on Linux. For compatibility with these programs, we don't perform + // any additional checks - it's okay if the pointer is invalid, + // since we wouldn't actually be writing to it. + if len == 0 { + return Ok(()); + } let this = self.eval_context_mut(); - let ptr = match this.memory().check_ptr_access(ptr, Size::from_bytes(len as u64), Align::from_bytes(1).unwrap())? { - Some(ptr) => ptr, - None => return Ok(()), // zero-sized access - }; + let mut data = vec![0; len]; - let data = match &mut this.memory_mut().extra.rng { - Some(rng) => { - let mut rng = rng.borrow_mut(); - let mut data = vec![0; len]; - rng.fill_bytes(&mut data); - data - } - None => { - return err!(Unimplemented( - "miri does not support gathering system entropy in deterministic mode! - Use '-Zmiri-seed=' to enable random number generation. - WARNING: Miri does *not* generate cryptographically secure entropy - - do not use Miri to run any program that needs secure random number generation".to_owned(), - )); - } - }; - let tcx = &{this.tcx.tcx}; - this.memory_mut().get_mut(ptr.alloc_id)? - .write_bytes(tcx, ptr, &data) + if this.machine.communicate { + // Fill the buffer using the host's rng. + getrandom::getrandom(&mut data) + .map_err(|err| err_unsup_format!("getrandom failed: {}", err))?; + } + else { + let rng = this.memory.extra.rng.get_mut(); + rng.fill_bytes(&mut data); + } + + this.memory.write_bytes(ptr, data.iter().copied()) } /// Visits the memory covered by `place`, sensitive to freezing: the 3rd parameter @@ -199,7 +208,7 @@ fn ecx(&self) -> &MiriEvalContext<'mir, '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.sty { + let is_unsafe_cell = match v.layout.ty.kind { ty::Adt(adt, _) => Some(adt.did) == self.ecx.tcx.lang_items().unsafe_cell_type(), _ => false, }; @@ -210,8 +219,26 @@ fn visit_value(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> // This is `Freeze`, there cannot be an `UnsafeCell` Ok(()) } else { - // Proceed further - self.walk_value(v) + // 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 { .. } => { + // 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 + // memory would be subject to Stacked Borrows rules, leading + // to all sorts of "funny" recursion. + // We only end up here if the type is *not* freeze, so we just call the + // `UnsafeCell` action. + (self.unsafe_cell_action)(v) + } + layout::Variants::Single { .. } => { + // Proceed further, try to find where exactly that `UnsafeCell` + // is hiding. + self.walk_value(v) + } + } } } @@ -261,4 +288,175 @@ fn visit_primitive(&mut self, _v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> } } } + + /// 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])? + .ok_or_else(|| err_unsup_format!("Path libc::{} cannot be resolved.", name))? + .not_undef() + } + + /// Helper function to get a `libc` constant as an `i32`. + fn eval_libc_i32(&mut self, name: &str) -> InterpResult<'tcx, i32> { + self.eval_libc(name)?.to_i32() + } + + /// Helper function to get the `TyLayout` of a `libc` type + fn libc_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyLayout<'tcx>> { + let this = self.eval_context_mut(); + let ty = this.resolve_path(&["libc", name])?.ty(*this.tcx); + this.layout_of(ty) + } + + // Writes several `ImmTy`s contiguosly into memory. This is useful when you have to pack + // different values into a struct. + fn write_packed_immediates( + &mut self, + place: &MPlaceTy<'tcx, Tag>, + imms: &[ImmTy<'tcx, Tag>], + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let mut offset = Size::from_bytes(0); + + for &imm in imms { + this.write_immediate_to_mplace( + *imm, + place.offset(offset, None, imm.layout, &*this.tcx)?, + )?; + offset += imm.layout.size; + } + Ok(()) + } + + /// Helper function used inside the shims of foreign functions to check that isolation is + /// disabled. It returns an error using the `name` of the foreign function if this is not the + /// case. + fn check_no_isolation(&mut self, name: &str) -> InterpResult<'tcx> { + if !self.eval_context_mut().machine.communicate { + throw_unsup_format!("`{}` not available when isolation is enabled. Pass the flag `-Zmiri-disable-isolation` to disable it.", name) + } + Ok(()) + } + + /// 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()) + } + + /// Gets the last error variable. + fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + let errno_place = this.machine.last_error.unwrap(); + this.read_scalar(errno_place.into())?.not_undef() + } + + /// Sets the last OS error using a `std::io::Error`. This function tries to produce the most + /// similar OS error from the `std::io::ErrorKind` and sets it as the last OS error. + 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.tcx.sess.target.target; + let last_error = if target.options.target_family == Some("unix".to_owned()) { + this.eval_libc(match e.kind() { + ConnectionRefused => "ECONNREFUSED", + ConnectionReset => "ECONNRESET", + PermissionDenied => "EPERM", + BrokenPipe => "EPIPE", + NotConnected => "ENOTCONN", + ConnectionAborted => "ECONNABORTED", + AddrNotAvailable => "EADDRNOTAVAIL", + AddrInUse => "EADDRINUSE", + NotFound => "ENOENT", + Interrupted => "EINTR", + InvalidInput => "EINVAL", + TimedOut => "ETIMEDOUT", + AlreadyExists => "EEXIST", + WouldBlock => "EWOULDBLOCK", + _ => throw_unsup_format!("The {} error cannot be transformed into a raw os error", e) + })? + } else { + // FIXME: we have to implement the windows' equivalent of this. + throw_unsup_format!("Setting the last OS error from an io::Error is unsupported for {}.", target.target_os) + }; + this.set_last_error(last_error) + } + + /// Helper function that consumes an `std::io::Result` and returns an + /// `InterpResult<'tcx,T>::Ok` instead. In case the result is an error, this function returns + /// `Ok(-1)` and sets the last OS error accordingly. + /// + /// This function uses `T: From` instead of `i32` directly because some IO related + /// functions return different integer types (like `read`, that returns an `i64`) + fn try_unwrap_io_result>( + &mut self, + result: std::io::Result, + ) -> InterpResult<'tcx, T> { + match result { + Ok(ok) => Ok(ok), + Err(e) => { + self.eval_context_mut().set_last_error_from_io_error(e)?; + Ok((-1).into()) + } + } + } + + /// Helper function to read an OsString from a null-terminated sequence of bytes, which is what + /// the Unix APIs usually handle. + fn read_os_string_from_c_string(&mut self, scalar: Scalar) -> InterpResult<'tcx, OsString> { + let bytes = self.eval_context_mut().memory.read_c_str(scalar)?; + Ok(bytes_to_os_str(bytes)?.into()) + } + + /// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what + /// the Unix APIs usually handle. This function returns `Ok(false)` without trying to write if + /// `size` is not large enough to fit the contents of `os_string` plus a null terminator. It + /// returns `Ok(true)` if the writing process was successful. + fn write_os_str_to_c_string( + &mut self, + os_str: &OsStr, + scalar: Scalar, + size: u64 + ) -> InterpResult<'tcx, bool> { + 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 overflow. + if size <= bytes.len() as u64 { + return Ok(false); + } + // FIXME: We should use `Iterator::chain` instead when rust-lang/rust#65704 lands. + self.eval_context_mut().memory.write_bytes(scalar, bytes.iter().copied().chain(iter::once(0u8)))?; + Ok(true) + } +} + +#[cfg(target_os = "unix")] +fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> { + std::os::unix::ffi::OsStringExt::into_bytes(os_str) +} + +#[cfg(target_os = "unix")] +fn bytes_to_os_str<'tcx, 'a>(bytes: &'a[u8]) -> InterpResult<'tcx, &'a OsStr> { + Ok(std::os::unix::ffi::OsStringExt::from_bytes(bytes)) +} + +// On non-unix platforms the best we can do to transform bytes from/to OS strings is to do the +// intermediate transformation into strings. Which invalidates non-utf8 paths that are actually +// valid. +#[cfg(not(target_os = "unix"))] +fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> { + os_str + .to_str() + .map(|s| s.as_bytes()) + .ok_or_else(|| err_unsup_format!("{:?} is not a valid utf-8 string", os_str).into()) +} + +#[cfg(not(target_os = "unix"))] +fn bytes_to_os_str<'tcx, 'a>(bytes: &'a[u8]) -> InterpResult<'tcx, &'a OsStr> { + let s = std::str::from_utf8(bytes) + .map_err(|_| err_unsup_format!("{:?} is not a valid utf-8 string", bytes))?; + Ok(&OsStr::new(s)) }