about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2022-07-03 11:29:49 -0400
committerRalf Jung <post@ralfj.de>2022-07-03 22:42:50 -0400
commit7fc77806d4f66819cf9d7ebd53a3338686c08b7d (patch)
treeb55790741997f2b71f21043bf4e8d73e26d40f07
parentb04bfb4aea99436a62f6a98056e805eb9b0629cc (diff)
downloadrust-7fc77806d4f66819cf9d7ebd53a3338686c08b7d.tar.gz
rust-7fc77806d4f66819cf9d7ebd53a3338686c08b7d.zip
fix interpreter validity check on Box
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs34
-rw-r--r--compiler/rustc_const_eval/src/lib.rs1
2 files changed, 29 insertions, 6 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 847694cbd10..600d25548d4 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -594,13 +594,35 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                 Ok(true)
             }
             ty::Adt(def, ..) if def.is_box() => {
-                let unique = self.ecx.operand_field(value, 0)?;
-                let nonnull = self.ecx.operand_field(&unique, 0)?;
-                let ptr = self.ecx.operand_field(&nonnull, 0)?;
-                self.check_safe_pointer(&ptr, "box")?;
+                // Box is special, very special. We carefully assert all the assumptions we make
+                // here; if this needs to be adjusted, remember to also adjust all the other
+                // visitors -- in particular the Stacked Borrows retagging visitor in Miri.
+                // Did I mention that this is a gross hack? Anyway...
 
-                // Check other fields of Box
-                self.walk_value(value)?;
+                // `Box` has two fields: the pointer we care about, and the allocator.
+                assert_eq!(value.layout.fields.count(), 2, "`Box` must have exactly 2 fields");
+                let (unique_ptr, alloc) =
+                    (self.ecx.operand_field(value, 0)?, self.ecx.operand_field(value, 1)?);
+                // Unfortunately there is some type junk in the way here: `unique_ptr` is a `Unique`...
+                // (which means another 2 fields, the second of which is a `PhantomData`)
+                assert_eq!(unique_ptr.layout.fields.count(), 2);
+                let (nonnull_ptr, phantom) = (
+                    self.ecx.operand_field(&unique_ptr, 0)?,
+                    self.ecx.operand_field(&unique_ptr, 1)?,
+                );
+                assert!(
+                    phantom.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
+                    "2nd field of `Unique` should be PhantomData but is {:?}",
+                    phantom.layout.ty,
+                );
+                // ... that contains a `NonNull`... (gladly, only a single field here)
+                assert_eq!(nonnull_ptr.layout.fields.count(), 1);
+                let raw_ptr = self.ecx.operand_field(&nonnull_ptr, 0)?; // the actual raw ptr
+                // ... whose only field finally is a raw ptr we can dereference.
+                self.check_safe_pointer(&raw_ptr, "box")?;
+                // The second `Box` field is the allocator, which we recursively check for validity
+                // like in regular structs.
+                self.walk_value(&alloc)?;
                 Ok(true)
             }
             ty::FnPtr(_sig) => {
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index 5bf91879066..2d42ae236ad 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -21,6 +21,7 @@ Rust MIR: a lowered representation of Rust.
 #![feature(trusted_step)]
 #![feature(try_blocks)]
 #![feature(yeet_expr)]
+#![feature(is_some_with)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]