Skip to content

Give all bytes of TypeId provenance #143736

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 37 additions & 31 deletions compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,21 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
)?;
self.copy_op_allow_transmute(&op, dest)?;

// Give the first pointer-size bytes provenance that knows about the type id.
// Give the each pointer-sized chunk provenance that knows about the type id.
// Here we rely on `TypeId` being a newtype around an array of pointers, so we
// first project to its only field and then the first array element.
// 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 first = self.project_index(&first, 0)?;
let offset = self.read_scalar(&first)?.to_target_usize(&tcx)?;
let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(offset));
let ptr = self.global_root_pointer(ptr)?;
let val = Scalar::from_pointer(ptr, &tcx);
self.write_scalar(val, &first)
let mut elem_iter = self.project_array_fields(&first)?;
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)?;
let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(hash_fragment));
let ptr = self.global_root_pointer(ptr)?;
let val = Scalar::from_pointer(ptr, &tcx);
self.write_scalar(val, &elem)?;
}
interp_ok(())
}

/// Returns `true` if emulation happened.
Expand Down Expand Up @@ -101,34 +105,36 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let mut a_fields = self.project_array_fields(&a_fields)?;
let mut b_fields = self.project_array_fields(&b_fields)?;

let (_idx, a) = a_fields
.next(self)?
.expect("we know the layout of TypeId has at least 2 array elements");
let a = self.deref_pointer(&a)?;
let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;

let (_idx, b) = b_fields
.next(self)?
.expect("we know the layout of TypeId has at least 2 array elements");
let b = self.deref_pointer(&b)?;
let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;
let mut provenance_a = None;
let mut provenance_b = None;
let mut provenance_matches = true;

let provenance_matches = a == b;
while let Some((i, a)) = a_fields.next(self)? {
let (_, b) = b_fields.next(self)?.unwrap();

let mut eq_id = offset_a == offset_b;
let a = self.deref_pointer(&a)?;
let (a, offset_a) = self.get_ptr_type_id(a.ptr())?;

while let Some((_, a)) = a_fields.next(self)? {
let (_, b) = b_fields.next(self)?.unwrap();
let b = self.deref_pointer(&b)?;
let (b, offset_b) = self.get_ptr_type_id(b.ptr())?;

let a = self.read_target_usize(&a)?;
let b = self.read_target_usize(&b)?;
eq_id &= a == b;
}
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 !eq_id && provenance_matches {
throw_ub_format!(
"type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents"
)
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)?;
Expand Down
5 changes: 4 additions & 1 deletion tests/ui/consts/const_transmute_type_id3.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! Test that all bytes of a TypeId must have the
//! TypeId marker provenance.

#![feature(const_type_id, const_trait_impl, const_cmp)]

use std::any::TypeId;
Expand All @@ -10,7 +13,7 @@ const _: () = {
std::ptr::write(ptr.offset(1), 999);
}
assert!(a == b);
//~^ ERROR: one of the TypeId arguments is invalid, the hash does not match the type it represents
//~^ ERROR: pointer must point to some allocation
};

fn main() {}
4 changes: 2 additions & 2 deletions tests/ui/consts/const_transmute_type_id3.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0080]: type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents
--> $DIR/const_transmute_type_id3.rs:12:13
error[E0080]: pointer not dereferenceable: pointer must point to some allocation, but got 0x3e7[noalloc] which is a dangling pointer (it has no provenance)
--> $DIR/const_transmute_type_id3.rs:15:13
|
LL | assert!(a == b);
| ^^^^^^ evaluation of `_` failed inside this call
Expand Down
21 changes: 21 additions & 0 deletions tests/ui/consts/const_transmute_type_id5.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//! Test that we require an equal TypeId to have the same integer
//! part, even if the provenance matches.

#![feature(const_type_id, const_trait_impl, const_cmp)]

use std::any::TypeId;

const _: () = {
let a = TypeId::of::<()>();
let mut b = TypeId::of::<()>();
unsafe {
let ptr = &mut b as *mut TypeId as *mut *const ();
// Copy the ptr at index 0 to index 1
let val = std::ptr::read(ptr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let val = std::ptr::read(ptr);
// Copy the ptr at index 0 to index 1.
let val = std::ptr::read(ptr);

std::ptr::write(ptr.offset(1), val);
}
assert!(a == b);
//~^ ERROR: type_id_eq: one of the TypeId arguments is invalid, chunk 1 of the hash does not match the type it represents
};

fn main() {}
15 changes: 15 additions & 0 deletions tests/ui/consts/const_transmute_type_id5.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error[E0080]: type_id_eq: one of the TypeId arguments is invalid, chunk 1 of the hash does not match the type it represents
--> $DIR/const_transmute_type_id5.rs:17:13
|
LL | assert!(a == b);
| ^^^^^^ evaluation of `_` failed inside this call
|
note: inside `<TypeId as PartialEq>::eq`
--> $SRC_DIR/core/src/any.rs:LL:COL
note: inside `<TypeId as PartialEq>::eq::compiletime`
--> $SRC_DIR/core/src/any.rs:LL:COL
= note: this error originates in the macro `$crate::intrinsics::const_eval_select` which comes from the expansion of the macro `crate::intrinsics::const_eval_select` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0080`.
Loading