diff options
| author | Ralf Jung <post@ralfj.de> | 2018-10-19 17:11:23 +0200 |
|---|---|---|
| committer | Ralf Jung <post@ralfj.de> | 2018-10-28 11:21:41 +0100 |
| commit | f5e8830278d5e677635dfd1fa774ae733700b4e8 (patch) | |
| tree | b1cdac699325246020c53a6fe3ad8417d3de7080 /src | |
| parent | 048900c5b6bd8d5f6499b60703b3b5773b614608 (diff) | |
| download | rust-f5e8830278d5e677635dfd1fa774ae733700b4e8.tar.gz rust-f5e8830278d5e677635dfd1fa774ae733700b4e8.zip | |
validity in non-const mode relies on ref_to_mplace checking bounds; (de)reference hooks work on places
Diffstat (limited to 'src')
| -rw-r--r-- | src/librustc_mir/const_eval.rs | 28 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/machine.rs | 36 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/place.rs | 70 | ||||
| -rw-r--r-- | src/librustc_mir/interpret/validity.rs | 106 |
4 files changed, 119 insertions, 121 deletions
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 41a70c88f56..3a4f4b36dd5 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -20,8 +20,8 @@ use rustc::hir::{self, def_id::DefId}; use rustc::hir::def::Def; use rustc::mir::interpret::{ConstEvalErr, ErrorHandled}; use rustc::mir; -use rustc::ty::{self, Ty, TyCtxt, Instance, query::TyCtxtAt}; -use rustc::ty::layout::{self, Size, LayoutOf, TyLayout}; +use rustc::ty::{self, TyCtxt, Instance, query::TyCtxtAt}; +use rustc::ty::layout::{self, LayoutOf, TyLayout}; use rustc::ty::subst::Subst; use rustc::traits::Reveal; use rustc_data_structures::indexed_vec::IndexVec; @@ -32,7 +32,7 @@ use syntax::ast::Mutability; use syntax::source_map::{Span, DUMMY_SP}; use interpret::{self, - PlaceTy, MemPlace, OpTy, Operand, Value, Pointer, Scalar, ConstValue, + PlaceTy, MemPlace, OpTy, Operand, Value, Scalar, ConstValue, EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup, Allocation, AllocId, MemoryKind, snapshot, RefTracking, @@ -465,28 +465,6 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx> &ecx.stack[..], ) } - - #[inline(always)] - fn tag_reference( - _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, - _ptr: Pointer<Self::PointerTag>, - _pointee_ty: Ty<'tcx>, - _pointee_size: Size, - _borrow_kind: Option<hir::Mutability>, - ) -> EvalResult<'tcx, Self::PointerTag> { - Ok(()) - } - - #[inline(always)] - fn tag_dereference( - _ecx: &EvalContext<'a, 'mir, 'tcx, Self>, - _ptr: Pointer<Self::PointerTag>, - _pointee_ty: Ty<'tcx>, - _pointee_size: Size, - _borrow_kind: Option<hir::Mutability>, - ) -> EvalResult<'tcx, Self::PointerTag> { - Ok(()) - } } /// Project to a field of a (variant of a) const diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 7184be6cd16..3d45728f4ec 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -21,7 +21,7 @@ use rustc::ty::{self, Ty, layout::{Size, TyLayout}, query::TyCtxtAt}; use super::{ Allocation, AllocId, EvalResult, Scalar, - EvalContext, PlaceTy, OpTy, Pointer, MemoryKind, + EvalContext, PlaceTy, OpTy, Pointer, MemPlace, MemoryKind, }; /// Classifying memory accesses @@ -205,26 +205,32 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized { } /// Executed when evaluating the `&` operator: Creating a new reference. - /// This has the chance to adjust the tag. + /// This has the chance to adjust the tag. It should not change anything else! /// `mutability` can be `None` in case a raw ptr is being created. + #[inline] fn tag_reference( - ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, - ptr: Pointer<Self::PointerTag>, - pointee_ty: Ty<'tcx>, - pointee_size: Size, - mutability: Option<hir::Mutability>, - ) -> EvalResult<'tcx, Self::PointerTag>; + _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, + place: MemPlace<Self::PointerTag>, + _ty: Ty<'tcx>, + _size: Size, + _mutability: Option<hir::Mutability>, + ) -> EvalResult<'tcx, MemPlace<Self::PointerTag>> { + Ok(place) + } /// Executed when evaluating the `*` operator: Following a reference. - /// This has the change to adjust the tag. + /// This has the change to adjust the tag. It should not change anything else! /// `mutability` can be `None` in case a raw ptr is being dereferenced. + #[inline] fn tag_dereference( - ecx: &EvalContext<'a, 'mir, 'tcx, Self>, - ptr: Pointer<Self::PointerTag>, - pointee_ty: Ty<'tcx>, - pointee_size: Size, - mutability: Option<hir::Mutability>, - ) -> EvalResult<'tcx, Self::PointerTag>; + _ecx: &EvalContext<'a, 'mir, 'tcx, Self>, + place: MemPlace<Self::PointerTag>, + _ty: Ty<'tcx>, + _size: Size, + _mutability: Option<hir::Mutability>, + ) -> EvalResult<'tcx, MemPlace<Self::PointerTag>> { + Ok(place) + } /// Execute a validation operation #[inline] diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 0bd9094ebcd..bbdda8ed68d 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -276,26 +276,25 @@ where let align = layout.align; let meta = val.to_meta()?; - - let ptr = match val.to_scalar_ptr()? { - Scalar::Ptr(ptr) if M::ENABLE_PTR_TRACKING_HOOKS => { - // Machine might want to track the `*` operator - let (size, _) = self.size_and_align_of(meta, layout)? - .expect("ref_to_mplace cannot determine size"); - let mutbl = match val.layout.ty.sty { - // `builtin_deref` considers boxes immutable, that's useless for our purposes - ty::Ref(_, _, mutbl) => Some(mutbl), - ty::Adt(def, _) if def.is_box() => Some(hir::MutMutable), - ty::RawPtr(_) => None, - _ => bug!("Unexpected pointer type {}", val.layout.ty.sty), - }; - let tag = M::tag_dereference(self, ptr, pointee_type, size, mutbl)?; - Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag)) - } - other => other, + let ptr = val.to_scalar_ptr()?; + let mplace = MemPlace { ptr, align, meta }; + // Pointer tag tracking might want to adjust the tag. + let mplace = if M::ENABLE_PTR_TRACKING_HOOKS { + let (size, _) = self.size_and_align_of(meta, layout)? + // for extern types, just cover what we can + .unwrap_or_else(|| layout.size_and_align()); + let mutbl = match val.layout.ty.sty { + // `builtin_deref` considers boxes immutable, that's useless for our purposes + ty::Ref(_, _, mutbl) => Some(mutbl), + ty::Adt(def, _) if def.is_box() => Some(hir::MutMutable), + ty::RawPtr(_) => None, + _ => bug!("Unexpected pointer type {}", val.layout.ty.sty), + }; + M::tag_dereference(self, mplace, pointee_type, size, mutbl)? + } else { + mplace }; - - Ok(MPlaceTy { mplace: MemPlace { ptr, align, meta }, layout }) + Ok(MPlaceTy { mplace, layout }) } /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space. @@ -305,24 +304,25 @@ where place: MPlaceTy<'tcx, M::PointerTag>, borrow_kind: Option<mir::BorrowKind>, ) -> EvalResult<'tcx, Value<M::PointerTag>> { - let ptr = match place.ptr { - Scalar::Ptr(ptr) if M::ENABLE_PTR_TRACKING_HOOKS => { - // Machine might want to track the `&` operator - let (size, _) = self.size_and_align_of_mplace(place)? - .expect("create_ref cannot determine size"); - let mutbl = match borrow_kind { - Some(mir::BorrowKind::Mut { .. }) => Some(hir::MutMutable), - Some(_) => Some(hir::MutImmutable), - None => None, - }; - let tag = M::tag_reference(self, ptr, place.layout.ty, size, mutbl)?; - Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag)) - }, - other => other, + // Pointer tag tracking might want to adjust the tag + let place = if M::ENABLE_PTR_TRACKING_HOOKS { + let (size, _) = self.size_and_align_of_mplace(place)? + // for extern types, just cover what we can + .unwrap_or_else(|| place.layout.size_and_align()); + let mutbl = match borrow_kind { + Some(mir::BorrowKind::Mut { .. }) | + Some(mir::BorrowKind::Unique) => + Some(hir::MutMutable), + Some(_) => Some(hir::MutImmutable), + None => None, + }; + M::tag_reference(self, *place, place.layout.ty, size, mutbl)? + } else { + *place }; Ok(match place.meta { - None => Value::Scalar(ptr.into()), - Some(meta) => Value::ScalarPair(ptr.into(), meta.into()), + None => Value::Scalar(place.ptr.into()), + Some(meta) => Value::ScalarPair(place.ptr.into(), meta.into()), }) } diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index 25e2ff6edb7..226717538a2 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -12,7 +12,7 @@ use std::fmt::Write; use std::hash::Hash; use syntax_pos::symbol::Symbol; -use rustc::ty::layout::{self, Size, Align, TyLayout}; +use rustc::ty::layout::{self, Size, Align, TyLayout, LayoutOf}; use rustc::ty; use rustc_data_structures::fx::FxHashSet; use rustc::mir::interpret::{ @@ -176,19 +176,27 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> // undef. We should fix that, but let's start low. } } - _ if ty.is_box() || ty.is_region_ptr() || ty.is_unsafe_ptr() => { - // Handle fat pointers. We also check fat raw pointers, - // their metadata must be valid! - // This also checks that the ptr itself is initialized, which - // seems reasonable even for raw pointers. - let place = try_validation!(self.ref_to_mplace(value), - "undefined data in pointer", path); + ty::RawPtr(..) => { + // No undef allowed here. Eventually this should be consistent with + // the integer types. + let _ptr = try_validation!(value.to_scalar_ptr(), + "undefined address in pointer", path); + let _meta = try_validation!(value.to_meta(), + "uninitialized data in fat pointer metadata", path); + } + _ if ty.is_box() || ty.is_region_ptr() => { + // Handle fat pointers. // Check metadata early, for better diagnostics - if place.layout.is_unsized() { - let tail = self.tcx.struct_tail(place.layout.ty); + let ptr = try_validation!(value.to_scalar_ptr(), + "undefined address in pointer", path); + let meta = try_validation!(value.to_meta(), + "uninitialized data in fat pointer metadata", path); + let layout = self.layout_of(value.layout.ty.builtin_deref(true).unwrap().ty)?; + if layout.is_unsized() { + let tail = self.tcx.struct_tail(layout.ty); match tail.sty { ty::Dynamic(..) => { - let vtable = try_validation!(place.meta.unwrap().to_ptr(), + let vtable = try_validation!(meta.unwrap().to_ptr(), "non-pointer vtable in fat pointer", path); try_validation!(self.read_drop_type_from_vtable(vtable), "invalid drop fn in vtable", path); @@ -197,7 +205,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> // FIXME: More checks for the vtable. } ty::Slice(..) | ty::Str => { - try_validation!(place.meta.unwrap().to_usize(self), + try_validation!(meta.unwrap().to_usize(self), "non-integer slice length in fat pointer", path); } ty::Foreign(..) => { @@ -207,17 +215,17 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> bug!("Unexpected unsized type tail: {:?}", tail), } } - // for safe ptrs, also check the ptr values itself - if !ty.is_unsafe_ptr() { - // Make sure this is non-NULL and aligned - let (size, align) = self.size_and_align_of(place.meta, place.layout)? - // for the purpose of validity, consider foreign types to have - // alignment and size determined by the layout (size will be 0, - // alignment should take attributes into account). - .unwrap_or_else(|| place.layout.size_and_align()); - match self.memory.check_align(place.ptr, align) { - Ok(_) => {}, - Err(err) => match err.kind { + // Make sure this is non-NULL and aligned + let (size, align) = self.size_and_align_of(meta, layout)? + // for the purpose of validity, consider foreign types to have + // alignment and size determined by the layout (size will be 0, + // alignment should take attributes into account). + .unwrap_or_else(|| layout.size_and_align()); + match self.memory.check_align(ptr, align) { + Ok(_) => {}, + Err(err) => { + error!("{:?} is not aligned to {:?}", ptr, align); + match err.kind { EvalErrorKind::InvalidNullPointerUsage => return validation_failure!("NULL reference", path), EvalErrorKind::AlignmentCheckFailed { .. } => @@ -225,41 +233,47 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> _ => return validation_failure!( "dangling (out-of-bounds) reference (might be NULL at \ - run-time)", + run-time)", path ), } } - // non-ZST also have to be dereferenceable + } + // Turn ptr into place. + // `ref_to_mplace` also calls the machine hook for (re)activating the tag, + // which in turn will (in full miri) check if the pointer is dereferencable. + let place = self.ref_to_mplace(value)?; + // Recursive checking + if let Some(ref_tracking) = ref_tracking { + assert!(const_mode, "We should only do recursie checking in const mode"); if size != Size::ZERO { + // Non-ZST also have to be dereferencable let ptr = try_validation!(place.ptr.to_ptr(), "integer pointer in non-ZST reference", path); - if const_mode { - // Skip validation entirely for some external statics - let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id); - if let Some(AllocType::Static(did)) = alloc_kind { - // `extern static` cannot be validated as they have no body. - // FIXME: Statics from other crates are also skipped. - // They might be checked at a different type, but for now we - // want to avoid recursing too deeply. This is not sound! - if !did.is_local() || self.tcx.is_foreign_item(did) { - return Ok(()); - } + // Skip validation entirely for some external statics + let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id); + if let Some(AllocType::Static(did)) = alloc_kind { + // `extern static` cannot be validated as they have no body. + // FIXME: Statics from other crates are also skipped. + // They might be checked at a different type, but for now we + // want to avoid recursing too deeply. This is not sound! + if !did.is_local() || self.tcx.is_foreign_item(did) { + return Ok(()); } } + // Maintain the invariant that the place we are checking is + // already verified to be in-bounds. try_validation!(self.memory.check_bounds(ptr, size, false), "dangling (not entirely in bounds) reference", path); } - if let Some(ref_tracking) = ref_tracking { - // Check if we have encountered this pointer+layout combination - // before. Proceed recursively even for integer pointers, no - // reason to skip them! They are (recursively) valid for some ZST, - // but not for others (e.g. `!` is a ZST). - let op = place.into(); - if ref_tracking.seen.insert(op) { - trace!("Recursing below ptr {:#?}", *op); - ref_tracking.todo.push((op, path_clone_and_deref(path))); - } + // Check if we have encountered this pointer+layout combination + // before. Proceed recursively even for integer pointers, no + // reason to skip them! They are (recursively) valid for some ZST, + // but not for others (e.g. `!` is a ZST). + let op = place.into(); + if ref_tracking.seen.insert(op) { + trace!("Recursing below ptr {:#?}", *op); + ref_tracking.todo.push((op, path_clone_and_deref(path))); } } } |
