X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fhelpers.rs;h=f7be3de8e481227ba2d0077d757ec4d146bf9822;hb=cf9340113efcf5c7bea4a143f42c8ea47da4550e;hp=e3f818414da3d9a79fdf64b8193b5b08a57b8508;hpb=78311a713218b9923cd5ab73b62a9fe2485c7d22;p=rust.git diff --git a/src/helpers.rs b/src/helpers.rs index e3f818414da..f7be3de8e48 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,10 +1,11 @@ -use std::mem; +use std::{mem, iter}; +use std::ffi::{OsStr, OsString}; use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc::mir; use rustc::ty::{ self, - layout::{self, Align, LayoutOf, Size, TyLayout}, + layout::{self, LayoutOf, Size, TyLayout}, }; use rand::RngCore; @@ -57,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) } @@ -94,12 +95,6 @@ fn gen_random( } let this = self.eval_context_mut(); - let ptr = this.memory().check_ptr_access( - ptr, - Size::from_bytes(len as u64), - Align::from_bytes(1).unwrap() - )?.expect("we already checked for size 0"); - let mut data = vec![0; len]; if this.machine.communicate { @@ -108,12 +103,11 @@ fn gen_random( .map_err(|err| err_unsup_format!("getrandom failed: {}", err))?; } else { - let rng = this.memory_mut().extra.rng.get_mut(); + let rng = this.memory.extra.rng.get_mut(); rng.fill_bytes(&mut data); } - let tcx = &{this.tcx.tcx}; - this.memory_mut().get_mut(ptr.alloc_id)?.write_bytes(tcx, ptr, &data) + this.memory.write_bytes(ptr, data.iter().copied()) } /// Visits the memory covered by `place`, sensitive to freezing: the 3rd parameter @@ -311,8 +305,7 @@ fn eval_libc_i32(&mut self, name: &str) -> InterpResult<'tcx, 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 tcx = &{ this.tcx.tcx }; - let ty = this.resolve_path(&["libc", name])?.ty(*tcx); + let ty = this.resolve_path(&["libc", name])?.ty(*this.tcx); this.layout_of(ty) } @@ -325,14 +318,12 @@ fn write_packed_immediates( ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let tcx = &{ this.tcx.tcx }; - let mut offset = Size::from_bytes(0); for &imm in imms { this.write_immediate_to_mplace( *imm, - place.offset(offset, None, imm.layout, tcx)?, + place.offset(offset, None, imm.layout, &*this.tcx)?, )?; offset += imm.layout.size; } @@ -348,4 +339,124 @@ fn check_no_isolation(&mut self, name: &str) -> InterpResult<'tcx> { } 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)) }