summary refs log tree commit diff
path: root/compiler/rustc_const_eval
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2025-07-15 17:16:53 +0200
committerRalf Jung <post@ralfj.de>2025-07-16 08:02:47 +0200
commit42ec0280274acfa754ae88061ee6e77e7461fad1 (patch)
tree6f08f993d588cbafa1edf2364d79f744bcf61143 /compiler/rustc_const_eval
parente27f16a499074ba9a87f7f7641d9f64c572863bc (diff)
downloadrust-42ec0280274acfa754ae88061ee6e77e7461fad1.tar.gz
rust-42ec0280274acfa754ae88061ee6e77e7461fad1.zip
type_id_eq: check that the hash fully matches the type
Diffstat (limited to 'compiler/rustc_const_eval')
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs99
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs6
2 files changed, 57 insertions, 48 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 22d29eda913..e24a355891d 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -4,8 +4,9 @@
 
 use std::assert_matches::assert_matches;
 
-use rustc_abi::{FieldIdx, Size};
+use rustc_abi::{FieldIdx, HasDataLayout, Size};
 use rustc_apfloat::ieee::{Double, Half, Quad, Single};
+use rustc_middle::mir::interpret::{read_target_uint, write_target_uint};
 use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::{Ty, TyCtxt};
@@ -30,7 +31,7 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll
 }
 impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     /// Generates a value of `TypeId` for `ty` in-place.
-    pub(crate) fn write_type_id(
+    fn write_type_id(
         &mut self,
         ty: Ty<'tcx>,
         dest: &PlaceTy<'tcx, M::Provenance>,
@@ -48,8 +49,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         // Here we rely on `TypeId` being a newtype around an array of pointers, so we
         // first project to its only field and then the array elements.
         let alloc_id = tcx.reserve_and_set_type_id_alloc(ty);
-        let first = self.project_field(dest, FieldIdx::ZERO)?;
-        let mut elem_iter = self.project_array_fields(&first)?;
+        let arr = self.project_field(dest, FieldIdx::ZERO)?;
+        let mut elem_iter = self.project_array_fields(&arr)?;
         while let Some((_, elem)) = elem_iter.next(self)? {
             // Decorate this part of the hash with provenance; leave the integer part unchanged.
             let hash_fragment = self.read_scalar(&elem)?.to_target_usize(&tcx)?;
@@ -61,6 +62,52 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         interp_ok(())
     }
 
+    /// Read a value of type `TypeId`, returning the type it represents.
+    pub(crate) fn read_type_id(
+        &self,
+        op: &OpTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx, Ty<'tcx>> {
+        // `TypeId` is a newtype around an array of pointers. All pointers must have the same
+        // provenance, and that provenance represents the type.
+        let ptr_size = self.pointer_size().bytes_usize();
+        let arr = self.project_field(op, FieldIdx::ZERO)?;
+
+        let mut ty_and_hash = None;
+        let mut elem_iter = self.project_array_fields(&arr)?;
+        while let Some((idx, elem)) = elem_iter.next(self)? {
+            let elem = self.read_pointer(&elem)?;
+            let (elem_ty, elem_hash) = self.get_ptr_type_id(elem)?;
+            // If this is the first element, remember the type and its hash.
+            // If this is not the first element, ensure it is consistent with the previous ones.
+            let full_hash = match ty_and_hash {
+                None => {
+                    let hash = self.tcx.type_id_hash(elem_ty).as_u128();
+                    let mut hash_bytes = [0u8; 16];
+                    write_target_uint(self.data_layout().endian, &mut hash_bytes, hash).unwrap();
+                    ty_and_hash = Some((elem_ty, hash_bytes));
+                    hash_bytes
+                }
+                Some((ty, hash_bytes)) => {
+                    if ty != elem_ty {
+                        throw_ub_format!(
+                            "invalid `TypeId` value: not all bytes carry the same type id metadata"
+                        );
+                    }
+                    hash_bytes
+                }
+            };
+            // Ensure the elem_hash matches the corresponding part of the full hash.
+            let hash_frag = &full_hash[(idx as usize) * ptr_size..][..ptr_size];
+            if read_target_uint(self.data_layout().endian, hash_frag).unwrap() != elem_hash.into() {
+                throw_ub_format!(
+                    "invalid `TypeId` value: the hash does not match the type id metadata"
+                );
+            }
+        }
+
+        interp_ok(ty_and_hash.unwrap().0)
+    }
+
     /// Returns `true` if emulation happened.
     /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own
     /// intrinsic handling.
@@ -97,47 +144,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 self.write_type_id(tp_ty, dest)?;
             }
             sym::type_id_eq => {
-                // Both operands are `TypeId`, which is a newtype around an array of pointers.
-                // Project until we have the array elements.
-                let a_fields = self.project_field(&args[0], FieldIdx::ZERO)?;
-                let b_fields = self.project_field(&args[1], FieldIdx::ZERO)?;
-
-                let mut a_fields = self.project_array_fields(&a_fields)?;
-                let mut b_fields = self.project_array_fields(&b_fields)?;
-
-                let mut provenance_a = None;
-                let mut provenance_b = None;
-                let mut provenance_matches = true;
-
-                while let Some((i, a)) = a_fields.next(self)? {
-                    let (_, b) = b_fields.next(self)?.unwrap();
-
-                    let a = self.deref_pointer(&a)?;
-                    let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;
-
-                    let b = self.deref_pointer(&b)?;
-                    let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;
-
-                    if *provenance_a.get_or_insert(a) != a {
-                        throw_ub_format!(
-                            "type_id_eq: the first TypeId argument is invalid, the provenance of chunk {i} does not match the first chunk's"
-                        )
-                    }
-                    if *provenance_b.get_or_insert(b) != b {
-                        throw_ub_format!(
-                            "type_id_eq: the second TypeId argument is invalid, the provenance of chunk {i} does not match the first chunk's"
-                        )
-                    }
-                    provenance_matches &= a == b;
-
-                    if offset_a != offset_b && provenance_matches {
-                        throw_ub_format!(
-                            "type_id_eq: one of the TypeId arguments is invalid, chunk {i} of the hash does not match the type it represents"
-                        )
-                    }
-                }
-
-                self.write_scalar(Scalar::from_bool(provenance_matches), dest)?;
+                let a_ty = self.read_type_id(&args[0])?;
+                let b_ty = self.read_type_id(&args[1])?;
+                self.write_scalar(Scalar::from_bool(a_ty == b_ty), dest)?;
             }
             sym::variant_count => {
                 let tp_ty = instance.args.type_at(0);
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 6414821e21d..e77467ab412 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -951,12 +951,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     pub fn get_ptr_type_id(
         &self,
         ptr: Pointer<Option<M::Provenance>>,
-    ) -> InterpResult<'tcx, (Ty<'tcx>, Size)> {
+    ) -> InterpResult<'tcx, (Ty<'tcx>, u64)> {
         let (alloc_id, offset, _meta) = self.ptr_get_alloc_id(ptr, 0)?;
         let GlobalAlloc::TypeId { ty } = self.tcx.global_alloc(alloc_id) else {
-            throw_ub_format!("type_id_eq: `TypeId` provenance is not a type id")
+            throw_ub_format!("invalid `TypeId` value: not all bytes carry type id metadata")
         };
-        interp_ok((ty, offset))
+        interp_ok((ty, offset.bytes()))
     }
 
     pub fn get_ptr_fn(