]> git.lizzy.rs Git - rust.git/commitdiff
Improve miri's error reporting in check_in_alloc
authorLooMaclin <loo.maclin@protonmail.com>
Tue, 2 Apr 2019 02:58:25 +0000 (05:58 +0300)
committerLooMaclin <loo.maclin@protonmail.com>
Tue, 2 Apr 2019 02:58:25 +0000 (05:58 +0300)
src/librustc/mir/interpret/allocation.rs
src/librustc/mir/interpret/error.rs
src/librustc/mir/interpret/mod.rs
src/librustc/mir/interpret/pointer.rs
src/librustc_mir/hair/pattern/_match.rs
src/librustc_mir/interpret/memory.rs
src/librustc_mir/interpret/operand.rs
src/librustc_mir/interpret/validity.rs

index 80fef910cc71811e98ea867c67b7dec3ef251d76..f00da804e9535b23c923d99b95fcff7d5c51aa18 100644 (file)
@@ -7,7 +7,7 @@
 
 use crate::ty::layout::{Size, Align};
 use syntax::ast::Mutability;
-use std::iter;
+use std::{iter, fmt::{self, Display}};
 use crate::mir;
 use std::ops::{Deref, DerefMut};
 use rustc_data_structures::sorted_map::SortedMap;
@@ -22,6 +22,44 @@ pub enum InboundsCheck {
     MaybeDead,
 }
 
+/// Used by `check_in_alloc` to indicate whether the pointer needs to be just inbounds
+/// or also inbounds of a *live* allocation.
+#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)]
+pub enum CheckInAllocMsg {
+    ReadCStr,
+    CheckBytes,
+    WriteBytes,
+    WriteRepeat,
+    ReadScalar,
+    WriteScalar,
+    SlicePatCoveredByConst,
+    ReadDiscriminant,
+    CheckAlign,
+    ReadBytes,
+    CopyRepeatedly,
+    CheckBounds,
+}
+
+impl Display for CheckInAllocMsg {
+
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", match *self {
+            CheckInAllocMsg::ReadCStr => "read C str",
+            CheckInAllocMsg::CheckBytes => "check bytes",
+            CheckInAllocMsg::WriteBytes => "write bytes",
+            CheckInAllocMsg::WriteRepeat => "write repeat",
+            CheckInAllocMsg::ReadScalar => "read scalar",
+            CheckInAllocMsg::WriteScalar => "write scalar",
+            CheckInAllocMsg::SlicePatCoveredByConst => "slice pat covered by const",
+            CheckInAllocMsg::ReadDiscriminant => "read discriminant",
+            CheckInAllocMsg::CheckAlign => "check align",
+            CheckInAllocMsg::ReadBytes => "read bytes",
+            CheckInAllocMsg::CopyRepeatedly => "copy repeatedly",
+            CheckInAllocMsg::CheckBounds => "check bounds",
+        })
+    }
+}
+
 #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
 pub struct Allocation<Tag=(),Extra=()> {
     /// The actual bytes of the allocation.
@@ -140,9 +178,10 @@ impl<'tcx, Tag, Extra> Allocation<Tag, Extra> {
     fn check_bounds_ptr(
         &self,
         ptr: Pointer<Tag>,
+        msg: CheckInAllocMsg,
     ) -> EvalResult<'tcx> {
         let allocation_size = self.bytes.len() as u64;
-        ptr.check_in_alloc(Size::from_bytes(allocation_size), InboundsCheck::Live)
+        ptr.check_in_alloc(Size::from_bytes(allocation_size), msg)
     }
 
     /// Checks if the memory range beginning at `ptr` and of size `Size` is "in-bounds".
@@ -152,9 +191,10 @@ pub fn check_bounds(
         cx: &impl HasDataLayout,
         ptr: Pointer<Tag>,
         size: Size,
+        msg: CheckInAllocMsg,
     ) -> EvalResult<'tcx> {
         // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
-        self.check_bounds_ptr(ptr.offset(size, cx)?)
+        self.check_bounds_ptr(ptr.offset(size, cx)?, msg)
     }
 }
 
@@ -173,11 +213,12 @@ fn get_bytes_internal<MemoryExtra>(
         ptr: Pointer<Tag>,
         size: Size,
         check_defined_and_ptr: bool,
+        msg: CheckInAllocMsg,
     ) -> EvalResult<'tcx, &[u8]>
         // FIXME: Working around https://github.com/rust-lang/rust/issues/56209
         where Extra: AllocationExtra<Tag, MemoryExtra>
     {
-        self.check_bounds(cx, ptr, size)?;
+        self.check_bounds(cx, ptr, size, msg)?;
 
         if check_defined_and_ptr {
             self.check_defined(ptr, size)?;
@@ -201,11 +242,12 @@ pub fn get_bytes<MemoryExtra>(
         cx: &impl HasDataLayout,
         ptr: Pointer<Tag>,
         size: Size,
+        msg: CheckInAllocMsg,
     ) -> EvalResult<'tcx, &[u8]>
         // FIXME: Working around https://github.com/rust-lang/rust/issues/56209
         where Extra: AllocationExtra<Tag, MemoryExtra>
     {
-        self.get_bytes_internal(cx, ptr, size, true)
+        self.get_bytes_internal(cx, ptr, size, true, msg)
     }
 
     /// It is the caller's responsibility to handle undefined and pointer bytes.
@@ -216,11 +258,12 @@ pub fn get_bytes_with_undef_and_ptr<MemoryExtra>(
         cx: &impl HasDataLayout,
         ptr: Pointer<Tag>,
         size: Size,
+        msg: CheckInAllocMsg,
     ) -> EvalResult<'tcx, &[u8]>
         // FIXME: Working around https://github.com/rust-lang/rust/issues/56209
         where Extra: AllocationExtra<Tag, MemoryExtra>
     {
-        self.get_bytes_internal(cx, ptr, size, false)
+        self.get_bytes_internal(cx, ptr, size, false, msg)
     }
 
     /// Just calling this already marks everything as defined and removes relocations,
@@ -230,12 +273,13 @@ pub fn get_bytes_mut<MemoryExtra>(
         cx: &impl HasDataLayout,
         ptr: Pointer<Tag>,
         size: Size,
+        msg: CheckInAllocMsg,
     ) -> EvalResult<'tcx, &mut [u8]>
         // FIXME: Working around https://github.com/rust-lang/rust/issues/56209
         where Extra: AllocationExtra<Tag, MemoryExtra>
     {
         assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`");
-        self.check_bounds(cx, ptr, size)?;
+        self.check_bounds(cx, ptr, size, msg)?;
 
         self.mark_definedness(ptr, size, true)?;
         self.clear_relocations(cx, ptr, size)?;
@@ -269,7 +313,7 @@ pub fn read_c_str<MemoryExtra>(
                 // Go through `get_bytes` for checks and AllocationExtra hooks.
                 // We read the null, so we include it in the request, but we want it removed
                 // from the result!
-                Ok(&self.get_bytes(cx, ptr, size_with_null)?[..size])
+                Ok(&self.get_bytes(cx, ptr, size_with_null, CheckInAllocMsg::ReadCStr)?[..size])
             }
             None => err!(UnterminatedCString(ptr.erase_tag())),
         }
@@ -289,7 +333,7 @@ pub fn check_bytes<MemoryExtra>(
         where Extra: AllocationExtra<Tag, MemoryExtra>
     {
         // Check bounds and relocations on the edges
-        self.get_bytes_with_undef_and_ptr(cx, ptr, size)?;
+        self.get_bytes_with_undef_and_ptr(cx, ptr, size, CheckInAllocMsg::CheckBytes)?;
         // Check undef and ptr
         if !allow_ptr_and_undef {
             self.check_defined(ptr, size)?;
@@ -310,7 +354,7 @@ pub fn write_bytes<MemoryExtra>(
         // FIXME: Working around https://github.com/rust-lang/rust/issues/56209
         where Extra: AllocationExtra<Tag, MemoryExtra>
     {
-        let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64))?;
+        let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64), CheckInAllocMsg::WriteBytes)?;
         bytes.clone_from_slice(src);
         Ok(())
     }
@@ -326,7 +370,7 @@ pub fn write_repeat<MemoryExtra>(
         // FIXME: Working around https://github.com/rust-lang/rust/issues/56209
         where Extra: AllocationExtra<Tag, MemoryExtra>
     {
-        let bytes = self.get_bytes_mut(cx, ptr, count)?;
+        let bytes = self.get_bytes_mut(cx, ptr, count, CheckInAllocMsg::WriteRepeat)?;
         for b in bytes {
             *b = val;
         }
@@ -351,7 +395,7 @@ pub fn read_scalar<MemoryExtra>(
         where Extra: AllocationExtra<Tag, MemoryExtra>
     {
         // get_bytes_unchecked tests relocation edges
-        let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size)?;
+        let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size, CheckInAllocMsg::ReadScalar)?;
         // Undef check happens *after* we established that the alignment is correct.
         // We must not return Ok() for unaligned pointers!
         if self.check_defined(ptr, size).is_err() {
@@ -428,7 +472,7 @@ pub fn write_scalar<MemoryExtra>(
         };
 
         let endian = cx.data_layout().endian;
-        let dst = self.get_bytes_mut(cx, ptr, type_size)?;
+        let dst = self.get_bytes_mut(cx, ptr, type_size, CheckInAllocMsg::WriteScalar)?;
         write_target_uint(endian, dst, bytes).unwrap();
 
         // See if we have to also write a relocation
index fc04c7672db68757380b368122ad5aac323db927..a3acc03fb6318b12def7df8cf5c11fd863f91c30 100644 (file)
@@ -8,7 +8,7 @@
 use rustc_target::spec::abi::Abi;
 use rustc_macros::HashStable;
 
-use super::{RawConst, Pointer, InboundsCheck, ScalarMaybeUndef};
+use super::{RawConst, Pointer, CheckInAllocMsg, ScalarMaybeUndef};
 
 use backtrace::Backtrace;
 
@@ -243,7 +243,7 @@ pub enum EvalErrorKind<'tcx, O> {
     InvalidDiscriminant(ScalarMaybeUndef),
     PointerOutOfBounds {
         ptr: Pointer,
-        check: InboundsCheck,
+        msg: CheckInAllocMsg,
         allocation_size: Size,
     },
     InvalidNullPointerUsage,
@@ -460,13 +460,9 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         use self::EvalErrorKind::*;
         match *self {
-            PointerOutOfBounds { ptr, check, allocation_size } => {
+            PointerOutOfBounds { ptr, msg, allocation_size } => {
                 write!(f, "Pointer must be in-bounds{} at offset {}, but is outside bounds of \
-                           allocation {} which has size {}",
-                       match check {
-                           InboundsCheck::Live => " and live",
-                           InboundsCheck::MaybeDead => "",
-                       },
+                           allocation {} which has size {}", msg,
                        ptr.offset.bytes(), ptr.alloc_id, allocation_size.bytes())
             },
             ValidationFailure(ref err) => {
index 0dd8316852780b99c1a2e9fa44c3c624141ce682..c6881cfa99f3fe9122a705c341e98eb8317dd09d 100644 (file)
@@ -19,7 +19,7 @@ macro_rules! err {
 
 pub use self::allocation::{
     InboundsCheck, Allocation, AllocationExtra,
-    Relocations, UndefMask,
+    Relocations, UndefMask, CheckInAllocMsg,
 };
 
 pub use self::pointer::{Pointer, PointerArithmetic};
index 9216cb494cef9ccae392230d9abbfcdb5f2de7e9..bf9694693aa177444fabd77623b8d8726c3c585e 100644 (file)
@@ -3,7 +3,7 @@
 use rustc_macros::HashStable;
 
 use super::{
-    AllocId, EvalResult, InboundsCheck,
+    AllocId, EvalResult, CheckInAllocMsg
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -157,12 +157,12 @@ pub fn erase_tag(self) -> Pointer {
     pub fn check_in_alloc(
         self,
         allocation_size: Size,
-        check: InboundsCheck,
+        msg: CheckInAllocMsg,
     ) -> EvalResult<'tcx, ()> {
         if self.offset > allocation_size {
             err!(PointerOutOfBounds {
                 ptr: self.erase_tag(),
-                check,
+                msg,
                 allocation_size,
             })
         } else {
index 303ffcb3bfb3ab653a5d17137982f0c59bb8add8..014c5ced37b25e110be7179a8615d2c49ec0db5d 100644 (file)
 use rustc::ty::layout::{Integer, IntegerExt, VariantIdx, Size};
 
 use rustc::mir::Field;
-use rustc::mir::interpret::{ConstValue, Scalar, truncate};
+use rustc::mir::interpret::{ConstValue, Scalar, truncate, CheckInAllocMsg};
 use rustc::util::common::ErrorReported;
 
 use syntax::attr::{SignedInt, UnsignedInt};
@@ -1418,7 +1418,7 @@ fn slice_pat_covered_by_const<'tcx>(
                 return Ok(false);
             }
             let n = n.assert_usize(tcx).unwrap();
-            alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
+            alloc.get_bytes(&tcx, ptr, Size::from_bytes(n), CheckInAllocMsg::SlicePatCoveredByConst).unwrap()
         },
         // a slice fat pointer to a zero length slice
         (ConstValue::Slice(Scalar::Bits { .. }, 0), ty::Slice(t)) => {
@@ -1443,7 +1443,7 @@ fn slice_pat_covered_by_const<'tcx>(
             tcx.alloc_map
                 .lock()
                 .unwrap_memory(ptr.alloc_id)
-                .get_bytes(&tcx, ptr, Size::from_bytes(n))
+                .get_bytes(&tcx, ptr, Size::from_bytes(n), CheckInAllocMsg::SlicePatCoveredByConst)
                 .unwrap()
         },
         _ => bug!(
index 6ea200d4e4fad3519335096d9b912e41c95e31a6..302bc84e65c4ab2ccb46daf8c0639cc9fde5160c 100644 (file)
@@ -20,7 +20,7 @@
 use super::{
     Pointer, AllocId, Allocation, GlobalId, AllocationExtra,
     EvalResult, Scalar, EvalErrorKind, AllocKind, PointerArithmetic,
-    Machine, AllocMap, MayLeak, ErrorHandled, InboundsCheck,
+    Machine, AllocMap, MayLeak, ErrorHandled, InboundsCheck, CheckInAllocMsg,
 };
 
 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
@@ -252,7 +252,7 @@ pub fn check_align(
             Scalar::Ptr(ptr) => {
                 // check this is not NULL -- which we can ensure only if this is in-bounds
                 // of some (potentially dead) allocation.
-                let align = self.check_bounds_ptr(ptr, InboundsCheck::MaybeDead)?;
+                let align = self.check_bounds_ptr(ptr, CheckInAllocMsg::CheckAlign)?;
                 (ptr.offset.bytes(), align)
             }
             Scalar::Bits { bits, size } => {
@@ -292,10 +292,10 @@ pub fn check_align(
     pub fn check_bounds_ptr(
         &self,
         ptr: Pointer<M::PointerTag>,
-        liveness: InboundsCheck,
+        msg: CheckInAllocMsg,
     ) -> EvalResult<'tcx, Align> {
-        let (allocation_size, align) = self.get_size_and_align(ptr.alloc_id, liveness)?;
-        ptr.check_in_alloc(allocation_size, liveness)?;
+        let (allocation_size, align) = self.get_size_and_align(ptr.alloc_id, msg)?;
+        ptr.check_in_alloc(allocation_size, msg)?;
         Ok(align)
     }
 }
@@ -423,7 +423,7 @@ pub fn get_mut(
     pub fn get_size_and_align(
         &self,
         id: AllocId,
-        liveness: InboundsCheck,
+        msg: CheckInAllocMsg,
     ) -> EvalResult<'static, (Size, Align)> {
         if let Ok(alloc) = self.get(id) {
             return Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align));
@@ -439,7 +439,7 @@ pub fn get_size_and_align(
                 let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
                 Ok((layout.size, layout.align.abi))
             }
-            _ => match liveness {
+            _ => match msg {
                 InboundsCheck::MaybeDead => {
                     // Must be a deallocated pointer
                     Ok(*self.dead_alloc_map.get(&id).expect(
@@ -604,7 +604,7 @@ pub fn read_bytes(
             Ok(&[])
         } else {
             let ptr = ptr.to_ptr()?;
-            self.get(ptr.alloc_id)?.get_bytes(self, ptr, size)
+            self.get(ptr.alloc_id)?.get_bytes(self, ptr, size, CheckInAllocMsg::ReadBytes)
         }
     }
 }
@@ -729,10 +729,10 @@ pub fn copy_repeatedly(
 
         // This checks relocation edges on the src.
         let src_bytes = self.get(src.alloc_id)?
-            .get_bytes_with_undef_and_ptr(&tcx, src, size)?
+            .get_bytes_with_undef_and_ptr(&tcx, src, size, CheckInAllocMsg::CopyRepeatedly)?
             .as_ptr();
         let dest_bytes = self.get_mut(dest.alloc_id)?
-            .get_bytes_mut(&tcx, dest, size * length)?
+            .get_bytes_mut(&tcx, dest, size * length, CheckInAllocMsg::CopyRepeatedly)?
             .as_mut_ptr();
 
         // SAFE: The above indexing would have panicked if there weren't at least `size` bytes
index 38a9371b92723ced693e6819f58bf8d2b0b22ce4..b90ec42de7ef0bd8a00f2630d10d16836ba533be 100644 (file)
@@ -7,7 +7,7 @@
 use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt, VariantIdx};
 
 use rustc::mir::interpret::{
-    GlobalId, AllocId, InboundsCheck,
+    GlobalId, AllocId, CheckInAllocMsg,
     ConstValue, Pointer, Scalar,
     EvalResult, EvalErrorKind,
     sign_extend, truncate,
@@ -667,7 +667,7 @@ pub fn read_discriminant(
                     ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) => {
                         // The niche must be just 0 (which an inbounds pointer value never is)
                         let ptr_valid = niche_start == 0 && variants_start == variants_end &&
-                            self.memory.check_bounds_ptr(ptr, InboundsCheck::MaybeDead).is_ok();
+                            self.memory.check_bounds_ptr(ptr, CheckInAllocMsg::ReadDiscriminant).is_ok();
                         if !ptr_valid {
                             return err!(InvalidDiscriminant(raw_discr.erase_tag()));
                         }
index 3323ec387bfd59b38633cc5dbb8ad17f9807b99a..23c1681cbd8797f7b1ae912e64877b326753ec80 100644 (file)
@@ -7,7 +7,7 @@
 use rustc::ty;
 use rustc_data_structures::fx::FxHashSet;
 use rustc::mir::interpret::{
-    Scalar, AllocKind, EvalResult, EvalErrorKind,
+    Scalar, AllocKind, EvalResult, EvalErrorKind, CheckInAllocMsg,
 };
 
 use super::{
@@ -394,7 +394,7 @@ fn visit_primitive(&mut self, value: OpTy<'tcx, M::PointerTag>) -> EvalResult<'t
                         try_validation!(
                             self.ecx.memory
                                 .get(ptr.alloc_id)?
-                                .check_bounds(self.ecx, ptr, size),
+                                .check_bounds(self.ecx, ptr, size, CheckInAllocMsg::CheckBounds),
                             "dangling (not entirely in bounds) reference", self.path);
                     }
                     // Check if we have encountered this pointer+layout combination