]> git.lizzy.rs Git - rust.git/commitdiff
validation: accept pointers in integer arrays
authorRalf Jung <post@ralfj.de>
Thu, 11 Oct 2018 09:15:30 +0000 (11:15 +0200)
committerRalf Jung <post@ralfj.de>
Sat, 13 Oct 2018 07:09:03 +0000 (09:09 +0200)
src/librustc_mir/interpret/memory.rs
src/librustc_mir/interpret/validity.rs
src/test/ui/consts/const-eval/ub-ref.rs
src/test/ui/consts/const-eval/ub-ref.stderr

index 7d3ae19e1a30c69cf519d60f633a86c17c779616..781673a9914720274c23cb2e23b4afd0e3aa0d5d 100644 (file)
@@ -650,7 +650,7 @@ fn get_bytes(
     }
 
     /// It is the caller's responsibility to handle undefined and pointer bytes.
-    /// However, this still checks that there are no relocations on the egdes.
+    /// However, this still checks that there are no relocations on the *egdes*.
     #[inline]
     fn get_bytes_with_undef_and_ptr(
         &self,
@@ -842,6 +842,28 @@ pub fn read_c_str(&self, ptr: Pointer<M::PointerTag>) -> EvalResult<'tcx, &[u8]>
         }
     }
 
+    pub fn check_bytes(
+        &self,
+        ptr: Scalar<M::PointerTag>,
+        size: Size,
+        allow_ptr: bool,
+    ) -> EvalResult<'tcx> {
+        // Empty accesses don't need to be valid pointers, but they should still be non-NULL
+        let align = Align::from_bytes(1, 1).unwrap();
+        if size.bytes() == 0 {
+            self.check_align(ptr, align)?;
+            return Ok(());
+        }
+        let ptr = ptr.to_ptr()?;
+        self.get_bytes_with_undef_and_ptr(ptr, size, align)?;
+        // Check undef, and maybe ptr
+        self.check_defined(ptr, size)?;
+        if !allow_ptr {
+            self.check_relocations(ptr, size)?;
+        }
+        Ok(())
+    }
+
     pub fn read_bytes(&self, ptr: Scalar<M::PointerTag>, size: Size) -> EvalResult<'tcx, &[u8]> {
         // Empty accesses don't need to be valid pointers, but they should still be non-NULL
         let align = Align::from_bytes(1, 1).unwrap();
index fb81ab4e8c49d8267d131a154eca09c9b86b9c41..2c5a3a1812820acf01edada3b487e4f6e7348f02 100644 (file)
@@ -496,9 +496,10 @@ pub fn validate_operand(
                     }
                     // Special handling for arrays/slices of builtin integer types
                     ty::Array(tys, ..) | ty::Slice(tys) if {
-                        // This optimization applies only for integer types
+                        // This optimization applies only for integer and floating point types
+                        // (i.e., types that can hold arbitrary bytes).
                         match tys.sty {
-                            ty::Int(..) | ty::Uint(..) => true,
+                            ty::Int(..) | ty::Uint(..) | ty::Float(..) => true,
                             _ => false,
                         }
                     } => {
@@ -510,9 +511,15 @@ pub fn validate_operand(
                         // This is the size in bytes of the whole array.
                         let size = Size::from_bytes(ty_size * len);
 
-                        match self.memory.read_bytes(dest.ptr, size) {
+                        // In run-time mode, we accept points in here.  This is actually more
+                        // permissive than a per-element check would be, e.g. we accept
+                        // an &[u8] that contains a pointer even though bytewise checking would
+                        // reject it.  However, that's good: We don't inherently want
+                        // to reject those pointers, we just do not have the machinery to
+                        // talk about parts of a pointer.
+                        match self.memory.check_bytes(dest.ptr, size, /*allow_ptr*/!const_mode) {
                             // In the happy case, we needn't check anything else.
-                            Ok(_) => {},
+                            Ok(()) => {},
                             // Some error happened, try to provide a more detailed description.
                             Err(err) => {
                                 // For some errors we might be able to provide extra information
index 7ee13f20dd2d90c87cd6749f987d698286a9a3a4..584dc0691698ae520ee7a4e90f44804da0e797e8 100644 (file)
@@ -21,6 +21,9 @@
 const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) };
 //~^ ERROR this constant likely exhibits undefined behavior
 
+const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
+//~^ ERROR this constant likely exhibits undefined behavior
+
 const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
 //~^ ERROR this constant likely exhibits undefined behavior
 
index 9907c780d2ccba674935f4dd3c457cbf8fe2a2d1..8bcb6d190b89a8b3e7a977b342cdd6b95bad0f68 100644 (file)
@@ -25,11 +25,19 @@ LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) };
 error[E0080]: this constant likely exhibits undefined behavior
   --> $DIR/ub-ref.rs:24:1
    |
+LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a raw memory access tried to access part of a pointer value as raw bytes
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
+
+error[E0080]: this constant likely exhibits undefined behavior
+  --> $DIR/ub-ref.rs:27:1
+   |
 LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered integer pointer in non-ZST reference
    |
    = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
 
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
 
 For more information about this error, try `rustc --explain E0080`.