]> git.lizzy.rs Git - rust.git/commitdiff
implement stub validity check for basic types (bool, int, float); acquire locks for...
authorRalf Jung <post@ralfj.de>
Tue, 20 Jun 2017 04:18:43 +0000 (21:18 -0700)
committerOliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>
Tue, 25 Jul 2017 08:20:45 +0000 (10:20 +0200)
src/librustc_mir/interpret/lvalue.rs
src/librustc_mir/interpret/memory.rs
src/librustc_mir/interpret/step.rs

index 34d8e77b615aee823c2058154cacda3306372dd7..ee80b151e689c1bbf299d1e4aea8a7061e96097e 100644 (file)
@@ -1,13 +1,14 @@
+use rustc::hir::Mutability as TyMutability;
 use rustc::mir;
 use rustc::ty::layout::{Size, Align};
-use rustc::ty::{self, Ty};
+use rustc::ty::{self, Ty, TypeAndMut};
 use rustc_data_structures::indexed_vec::Idx;
 use syntax::ast::Mutability;
 
 use error::{EvalError, EvalResult};
 use eval_context::EvalContext;
-use memory::MemoryPointer;
-use value::{PrimVal, Value, Pointer};
+use memory::{MemoryPointer, AccessKind};
+use value::{PrimVal, Pointer, Value};
 
 #[derive(Copy, Clone, Debug)]
 pub enum Lvalue<'tcx> {
@@ -349,6 +350,32 @@ pub fn lvalue_field(
         Ok(Lvalue::Ptr { ptr, extra, aligned: aligned && !packed })
     }
 
+    fn val_to_lvalue(&mut self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
+        Ok(match self.tcx.struct_tail(ty).sty {
+            ty::TyDynamic(..) => {
+                let (ptr, vtable) = val.into_ptr_vtable_pair(&mut self.memory)?;
+                Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable), aligned: true }
+            },
+            ty::TyStr | ty::TySlice(_) => {
+                let (ptr, len) = val.into_slice(&mut self.memory)?;
+                Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len), aligned: true }
+            },
+            _ => Lvalue::Ptr { ptr: val.into_ptr(&mut self.memory)?, extra: LvalueExtra::None, aligned: true },
+        })
+    }
+
+    fn lvalue_index(&mut self, base: Lvalue<'tcx>, outer_ty: Ty<'tcx>, n: u64) -> EvalResult<'tcx, Lvalue<'tcx>> {
+        // Taking the outer type here may seem odd; it's needed because for array types, the outer type gives away the length.
+        let base = self.force_allocation(base)?;
+        let (base_ptr, _, aligned) = base.to_ptr_extra_aligned();
+
+        let (elem_ty, len) = base.elem_ty_and_len(outer_ty);
+        let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
+        assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len);
+        let ptr = base_ptr.offset(n * elem_size, self.memory.layout)?;
+        Ok(Lvalue::Ptr { ptr, extra: LvalueExtra::None, aligned })
+    }
+
     fn eval_lvalue_projection(
         &mut self,
         base: Lvalue<'tcx>,
@@ -388,32 +415,15 @@ fn eval_lvalue_projection(
 
                 trace!("deref to {} on {:?}", pointee_type, val);
 
-                match self.tcx.struct_tail(pointee_type).sty {
-                    ty::TyDynamic(..) => {
-                        let (ptr, vtable) = val.into_ptr_vtable_pair(&mut self.memory)?;
-                        (ptr, LvalueExtra::Vtable(vtable), true)
-                    },
-                    ty::TyStr | ty::TySlice(_) => {
-                        let (ptr, len) = val.into_slice(&mut self.memory)?;
-                        (ptr, LvalueExtra::Length(len), true)
-                    },
-                    _ => (val.into_ptr(&mut self.memory)?, LvalueExtra::None, true),
-                }
+                return self.val_to_lvalue(val, pointee_type);
             }
 
             Index(ref operand) => {
                 // FIXME(solson)
-                let base = self.force_allocation(base)?;
-                let (base_ptr, _, aligned) = base.to_ptr_extra_aligned();
-
-                let (elem_ty, len) = base.elem_ty_and_len(base_ty);
-                let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized");
                 let n_ptr = self.eval_operand(operand)?;
                 let usize = self.tcx.types.usize;
                 let n = self.value_to_primval(n_ptr, usize)?.to_u64()?;
-                assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len);
-                let ptr = base_ptr.offset(n * elem_size, &self)?;
-                (ptr, LvalueExtra::None, aligned)
+                return self.lvalue_index(base, base_ty, n);
             }
 
             ConstantIndex { offset, min_length, from_end } => {
@@ -456,3 +466,61 @@ pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> {
         self.monomorphize(lvalue.ty(self.mir(), self.tcx).to_ty(self.tcx), self.substs())
     }
 }
+
+// Validity checks
+impl<'a, 'tcx> EvalContext<'a, 'tcx> {
+    pub(super) fn acquire_valid(&mut self, lvalue: Lvalue<'tcx>, ty: Ty<'tcx>, outer_mutbl: TyMutability) -> EvalResult<'tcx> {
+        use rustc::ty::TypeVariants::*;
+        use rustc::ty::RegionKind::*;
+        use self::TyMutability::*;
+
+        trace!("Validating {:?} at type {}, outer mutability {:?}", lvalue, ty, outer_mutbl);
+        match ty.sty {
+            TyChar | TyInt(_) | TyUint(_) | TyRawPtr(_) => {
+                // TODO: Make sure these are not undef.
+                // We could do a bounds-check and other sanity checks on the lvalue, but it would be a bug in miri for this to ever fail.
+                Ok(())
+            }
+            TyBool | TyFloat(_) | TyStr => {
+                // TODO: Check if these are valid bool/float/UTF-8, respectively (and in particular, not undef).
+                Ok(())
+            }
+            TyRef(region, TypeAndMut { ty: pointee_ty, mutbl }) => {
+                // Acquire lock
+                let val = self.read_lvalue(lvalue)?;
+                let (len, _) = self.size_and_align_of_dst(pointee_ty, val)?;
+                let ptr = val.into_ptr(&mut self.memory)?.to_ptr()?;
+                let combined_mutbl = match outer_mutbl { MutMutable => mutbl, MutImmutable => MutImmutable };
+                let access = match combined_mutbl { MutMutable => AccessKind::Write, MutImmutable => AccessKind::Read };
+                let region = match *region {
+                    ReScope(extent) => Some(extent),
+                    _ => None,
+                };
+                self.memory.acquire_lock(ptr, len, region, access)?;
+
+                // Recurse
+                let pointee_lvalue = self.val_to_lvalue(val, pointee_ty)?;
+                self.acquire_valid(pointee_lvalue, pointee_ty, combined_mutbl)
+            }
+            TySlice(elem_ty) => {
+                let len = match lvalue {
+                    Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => len,
+                    _ => bug!("acquire_valid of a TySlice given non-slice lvalue: {:?}", lvalue),
+                };
+                for i in 0..len {
+                    let inner_lvalue = self.lvalue_index(lvalue, ty, i)?;
+                    self.acquire_valid(inner_lvalue, elem_ty, outer_mutbl)?;
+                }
+                Ok(())
+            }
+            TyFnPtr(_sig) => {
+                // TODO: The function names here could need some improvement.
+                let ptr = self.read_lvalue(lvalue)?.into_ptr(&mut self.memory)?.to_ptr()?;
+                self.memory.get_fn(ptr)?;
+                // TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
+                Ok(())
+            }
+            _ => unimplemented!("Unimplemented type encountered when checking validity.")
+        }
+    }
+}
index 7a87ef8c2a552920a8d11fd93c6385c67fc2a0d8..e7ed1c002cd5bb4084dd243da18fec4a6d00f24c 100644 (file)
@@ -76,9 +76,9 @@ pub enum AccessKind {
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub struct DynamicLifetime {
-    frame: usize,
-    region: Option<CodeExtent>, // "None" indicates "until the function ends"
+struct DynamicLifetime {
+    pub frame: usize,
+    pub region: Option<CodeExtent>, // "None" indicates "until the function ends"
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -524,6 +524,10 @@ pub(crate) fn check_locks(&self, ptr: MemoryPointer, len: u64, access: AccessKin
 
     /// Acquire the lock for the given lifetime
     pub(crate) fn acquire_lock(&mut self, ptr: MemoryPointer, len: u64, region: Option<CodeExtent>, kind: AccessKind) -> EvalResult<'tcx> {
+        trace!("Acquiring {:?} lock at {:?}, size {}", kind, ptr, len);
+        if len == 0 {
+            return Ok(());
+        }
         self.check_bounds(ptr.offset(len, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
         self.check_locks(ptr, len, kind)?; // make sure we have the access we are acquiring
         let lifetime = DynamicLifetime { frame: self.cur_frame, region };
@@ -534,6 +538,7 @@ pub(crate) fn acquire_lock(&mut self, ptr: MemoryPointer, len: u64, region: Opti
 
     /// Release a lock prematurely
     pub(crate) fn release_lock_until(&mut self, ptr: MemoryPointer, len: u64, release_until: Option<CodeExtent>) -> EvalResult<'tcx> {
+        // TODO: More tracing.
         // Make sure there are no read locks and no *other* write locks here
         if let Err(_) = self.check_locks(ptr, len, AccessKind::Write) {
             return Err(EvalError::InvalidMemoryLockRelease { ptr, len });
@@ -559,6 +564,7 @@ pub(crate) fn release_lock_until(&mut self, ptr: MemoryPointer, len: u64, releas
     }
 
     pub(crate) fn locks_lifetime_ended(&mut self, ending_region: Option<CodeExtent>) {
+        // TODO: More tracing.
         let cur_frame = self.cur_frame;
         let has_ended =  |lock: &LockInfo| -> bool {
             if lock.lifetime.frame != cur_frame {
index 1bb1d1e94800c5af438eb0c265acbf88a68e3ed5..9f3fae569eb71f0725d04043b19f95f90114ce13 100644 (file)
@@ -5,7 +5,7 @@
 use rustc::hir::def_id::DefId;
 use rustc::hir;
 use rustc::mir::visit::{Visitor, LvalueContext};
-use rustc::mir;
+use rustc::mir::{self, ValidationOp};
 use rustc::traits::Reveal;
 use rustc::ty::layout::Layout;
 use rustc::ty::{subst, self};
@@ -130,8 +130,18 @@ fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
                 self.deallocate_local(old_val)?;
             }
 
-            // Validity checks.  Not yet implemented.
-            Validate(_, _) => {}
+            // Validity checks.
+            Validate(ref op, ref lvalues) => {
+                match *op {
+                    ValidationOp::Acquire => {
+                        for operand in lvalues {
+                            let lvalue = self.eval_lvalue(&operand.lval)?;
+                            self.acquire_valid(lvalue, operand.ty, hir::MutMutable)?;
+                        }
+                    }
+                    _ => { /* not yet implemented */ }
+                }
+            }
 
             // Just a borrowck thing
             EndRegion(..) => {}