]> git.lizzy.rs Git - rust.git/blobdiff - src/helpers.rs
Fix bug with reporting wrong thread for races with reads & add thread ids to data...
[rust.git] / src / helpers.rs
index 8b20ee2f0d8355daf8cda62e1c07885c09fb123a..4c989db0170b5408c71116555a8fcfc0702aab17 100644 (file)
@@ -1,6 +1,7 @@
 use std::convert::{TryFrom, TryInto};
 use std::mem;
 use std::num::NonZeroUsize;
+use std::time::Duration;
 
 use log::trace;
 
@@ -58,7 +59,7 @@ fn eval_path_scalar(
         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);
     }
@@ -67,7 +68,7 @@ fn eval_path_scalar(
     fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar<Tag>> {
         self.eval_context_mut()
             .eval_path_scalar(&["libc", name])?
-            .not_undef()
+            .check_init()
     }
 
     /// Helper function to get a `libc` constant as an `i32`.
@@ -80,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<Tag>> {
         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`.
@@ -92,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)
     }
 
@@ -280,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,
@@ -291,6 +292,9 @@ 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
@@ -341,13 +345,8 @@ fn visit_aggregate(
                 }
             }
 
-            // We have to do *something* for unions.
-            fn visit_union(&mut self, v: MPlaceTy<'tcx, Tag>, _fields: NonZeroUsize) -> InterpResult<'tcx> {
-                // 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`")
             }
         }
     }
@@ -378,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,
@@ -398,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<Tag>) -> 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<Tag>> {
-        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<Tag>> {
+        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
@@ -417,9 +430,9 @@ fn get_last_error(&self) -> InterpResult<'tcx, Scalar<Tag>> {
     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",
@@ -469,6 +482,66 @@ fn try_unwrap_io_result<T: From<i32>>(
             }
         }
     }
+
+    fn read_scalar_at_offset(
+        &self,
+        op: OpTy<'tcx, Tag>,
+        offset: u64,
+        layout: TyAndLayout<'tcx>,
+    ) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
+        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<ScalarMaybeUninit<Tag>>,
+        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<Duration>> {
+        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.
@@ -480,6 +553,13 @@ fn try_unwrap_io_result<T: From<i32>>(
     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>(
     int: impl Into<i128>,
     layout: TyAndLayout<'tcx>,