]> git.lizzy.rs Git - rust.git/blobdiff - src/helpers.rs
avoid using unchecked casts or arithmetic
[rust.git] / src / helpers.rs
index ff9a16a024569719f8353155ad36e1f38580c85f..ecb3a5d8bce9c346fccd49daf983559e1ec60324 100644 (file)
@@ -1,13 +1,14 @@
 use std::ffi::OsStr;
 use std::{iter, mem};
+use std::convert::TryFrom;
 
-use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
 use rustc::mir;
 use rustc::ty::{
     self,
     layout::{self, LayoutOf, Size, TyLayout},
     List, TyCtxt,
 };
+use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
 use rustc_span::source_map::DUMMY_SP;
 
 use rand::RngCore;
@@ -76,12 +77,12 @@ fn test_null(&self, val: Scalar<Tag>) -> InterpResult<'tcx, Option<Scalar<Tag>>>
     /// 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: List::empty() };
+        let place = mir::Place { local: local, projection: List::empty() };
         this.eval_place(&place)
     }
 
     /// Generate some random bytes, and write them to `dest`.
-    fn gen_random(&mut self, ptr: Scalar<Tag>, len: usize) -> InterpResult<'tcx> {
+    fn gen_random(&mut self, ptr: Scalar<Tag>, len: u64) -> 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
@@ -92,7 +93,7 @@ fn gen_random(&mut self, ptr: Scalar<Tag>, len: usize) -> InterpResult<'tcx> {
         }
         let this = self.eval_context_mut();
 
-        let mut data = vec![0; len];
+        let mut data = vec![0; usize::try_from(len).unwrap()];
 
         if this.machine.communicate {
             // Fill the buffer using the host's rng.
@@ -300,18 +301,15 @@ fn visit_aggregate(
             }
 
             // We have to do *something* for unions.
-            fn visit_union(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
+            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) }
             }
-
-            // We should never get to a primitive, but always short-circuit somewhere above.
-            fn visit_primitive(&mut self, _v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
-                bug!("we should always short-circuit before coming to a primitive")
-            }
         }
     }
 
@@ -339,7 +337,7 @@ fn libc_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyLayout<'tcx>> {
     // 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();
@@ -349,7 +347,7 @@ fn write_packed_immediates(
         for &imm in imms {
             this.write_immediate_to_mplace(
                 *imm,
-                place.offset(offset, None, imm.layout, &*this.tcx)?,
+                place.offset(offset, MemPlaceMeta::None, imm.layout, &*this.tcx)?,
             )?;
             offset += imm.layout.size;
         }
@@ -359,15 +357,27 @@ fn write_packed_immediates(
     /// 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 {
+    fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
+        if !self.eval_context_ref().machine.communicate {
             throw_unsup_format!(
                 "`{}` not available when isolation is enabled. Pass the flag `-Zmiri-disable-isolation` to disable it.",
-                name
+                name,
             )
         }
         Ok(())
     }
+    /// Helper function used inside the shims of foreign functions to assert that the target
+    /// platform is `platform`. It panics showing a message with the `name` of the foreign function
+    /// if this is not the case.
+    fn assert_platform(&self, platform: &str, name: &str) {
+        assert_eq!(
+            self.eval_context_ref().tcx.sess.target.target.target_os,
+            platform,
+            "`{}` is only available on the `{}` platform",
+            name,
+            platform,
+        )
+    }
 
     /// Sets the last error variable.
     fn set_last_error(&mut self, scalar: Scalar<Tag>) -> InterpResult<'tcx> {
@@ -377,8 +387,8 @@ fn set_last_error(&mut self, scalar: Scalar<Tag>) -> InterpResult<'tcx> {
     }
 
     /// Gets the last error variable.
-    fn get_last_error(&mut self) -> InterpResult<'tcx, Scalar<Tag>> {
-        let this = self.eval_context_mut();
+    fn get_last_error(&self) -> InterpResult<'tcx, Scalar<Tag>> {
+        let this = self.eval_context_ref();
         let errno_place = this.machine.last_error.unwrap();
         this.read_scalar(errno_place.into())?.not_undef()
     }
@@ -462,15 +472,16 @@ fn bytes_to_os_str<'tcx, 'a>(bytes: &'a [u8]) -> InterpResult<'tcx, &'a OsStr> {
     }
 
     /// 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.
+    /// the Unix APIs usually handle. This function returns `Ok((false, length))` 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, length))` if the writing process was successful. The
+    /// string length returned does not include the null terminator.
     fn write_os_str_to_c_str(
         &mut self,
         os_str: &OsStr,
         scalar: Scalar<Tag>,
         size: u64,
-    ) -> InterpResult<'tcx, bool> {
+    ) -> InterpResult<'tcx, (bool, u64)> {
         #[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)
@@ -489,21 +500,22 @@ fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]>
         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 out-of-bounds access.
-        if size <= bytes.len() as u64 {
-            return Ok(false);
+        let string_length = u64::try_from(bytes.len()).unwrap();
+        if size <= string_length {
+            return Ok((false, string_length));
         }
         self.eval_context_mut()
             .memory
             .write_bytes(scalar, bytes.iter().copied().chain(iter::once(0u8)))?;
-        Ok(true)
+        Ok((true, string_length))
     }
 
     fn alloc_os_str_as_c_str(
         &mut self,
         os_str: &OsStr,
-        memkind: MemoryKind<MiriMemoryKind>
+        memkind: MemoryKind<MiriMemoryKind>,
     ) -> Pointer<Tag> {
-        let size = os_str.len() as u64 + 1; // Make space for `0` terminator.
+        let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator.
         let this = self.eval_context_mut();
 
         let arg_type = this.tcx.mk_array(this.tcx.types.u8, size);
@@ -518,9 +530,9 @@ pub fn immty_from_int_checked<'tcx>(
     layout: TyLayout<'tcx>,
 ) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> {
     let int = int.into();
-    Ok(ImmTy::try_from_int(int, layout).ok_or_else(||
+    Ok(ImmTy::try_from_int(int, layout).ok_or_else(|| {
         err_unsup_format!("Signed value {:#x} does not fit in {} bits", int, layout.size.bits())
-    )?)
+    })?)
 }
 
 pub fn immty_from_uint_checked<'tcx>(
@@ -528,7 +540,7 @@ pub fn immty_from_uint_checked<'tcx>(
     layout: TyLayout<'tcx>,
 ) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> {
     let int = int.into();
-    Ok(ImmTy::try_from_uint(int, layout).ok_or_else(||
+    Ok(ImmTy::try_from_uint(int, layout).ok_or_else(|| {
         err_unsup_format!("Signed value {:#x} does not fit in {} bits", int, layout.size.bits())
-    )?)
+    })?)
 }