From af4d6c74efb31e34f75dfe5faf6064a0ff9cbd7c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jun 2024 17:24:36 +0200 Subject: interpret: refactor dyn trait handling We can check that the vtable is for the right trait very early, and then just pass the type around. --- compiler/rustc_const_eval/src/interpret/traits.rs | 66 ++++++++++++++++------- 1 file changed, 48 insertions(+), 18 deletions(-) (limited to 'compiler/rustc_const_eval/src/interpret/traits.rs') diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 244a6ba48a4..1df9f5f7b2a 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,11 +1,11 @@ use rustc_middle::mir::interpret::{InterpResult, Pointer}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty}; use rustc_target::abi::{Align, Size}; use tracing::trace; use super::util::ensure_monomorphic_enough; -use super::{InterpCx, Machine}; +use super::{InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable}; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for @@ -33,28 +33,58 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(vtable_ptr.into()) } - /// Returns a high-level representation of the entries of the given vtable. - pub fn get_vtable_entries( - &self, - vtable: Pointer>, - ) -> InterpResult<'tcx, &'tcx [ty::VtblEntry<'tcx>]> { - let (ty, poly_trait_ref) = self.get_ptr_vtable(vtable)?; - Ok(if let Some(poly_trait_ref) = poly_trait_ref { - let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty); - let trait_ref = self.tcx.erase_regions(trait_ref); - self.tcx.vtable_entries(trait_ref) - } else { - TyCtxt::COMMON_VTABLE_ENTRIES - }) - } - pub fn get_vtable_size_and_align( &self, vtable: Pointer>, ) -> InterpResult<'tcx, (Size, Align)> { - let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?; + let ty = self.get_ptr_vtable_ty(vtable, None)?; let layout = self.layout_of(ty)?; assert!(layout.is_sized(), "there are no vtables for unsized types"); Ok((layout.size, layout.align.abi)) } + + /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. + pub(super) fn unpack_dyn_trait( + &self, + mplace: &MPlaceTy<'tcx, M::Provenance>, + expected_trait: &'tcx ty::List>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + assert!( + matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)), + "`unpack_dyn_trait` only makes sense on `dyn*` types" + ); + let vtable = mplace.meta().unwrap_meta().to_pointer(self)?; + let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?; + // This is a kind of transmute, from a place with unsized type and metadata to + // a place with sized type and no metadata. + let layout = self.layout_of(ty)?; + let mplace = mplace.offset_with_meta( + Size::ZERO, + OffsetMode::Wrapping, + MemPlaceMeta::None, + layout, + self, + )?; + Ok(mplace) + } + + /// Turn a `dyn* Trait` type into an value with the actual dynamic type. + pub(super) fn unpack_dyn_star>( + &self, + val: &P, + expected_trait: &'tcx ty::List>, + ) -> InterpResult<'tcx, P> { + assert!( + matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)), + "`unpack_dyn_star` only makes sense on `dyn*` types" + ); + let data = self.project_field(val, 0)?; + let vtable = self.project_field(val, 1)?; + let vtable = self.read_pointer(&vtable.to_op(self)?)?; + let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?; + // `data` is already the right thing but has the wrong type. So we transmute it. + let layout = self.layout_of(ty)?; + let data = data.transmute(layout, self)?; + Ok(data) + } } -- cgit 1.4.1-3-g733a5 From d041b7cf30bb5b5236cd9148cde7a3017ed28679 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 11 Jun 2024 07:47:58 +0200 Subject: check for correct trait in size_and_align_of --- compiler/rustc_const_eval/src/interpret/eval_context.rs | 4 ++-- compiler/rustc_const_eval/src/interpret/intrinsics.rs | 6 ++++-- compiler/rustc_const_eval/src/interpret/traits.rs | 3 ++- src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) (limited to 'compiler/rustc_const_eval/src/interpret/traits.rs') diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 7c2100fcbe3..e28cc05cc2a 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -765,10 +765,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } Ok(Some((full_size, full_align))) } - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(expected_trait, _, ty::Dyn) => { let vtable = metadata.unwrap_meta().to_pointer(self)?; // Read size and align from vtable (already checks size). - Ok(Some(self.get_vtable_size_and_align(vtable)?)) + Ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?)) } ty::Slice(_) | ty::Str => { diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 18b76443cd9..497ae500144 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -432,12 +432,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::vtable_size => { let ptr = self.read_pointer(&args[0])?; - let (size, _align) = self.get_vtable_size_and_align(ptr)?; + // `None` because we don't know which trait to expect here; any vtable is okay. + let (size, _align) = self.get_vtable_size_and_align(ptr, None)?; self.write_scalar(Scalar::from_target_usize(size.bytes(), self), dest)?; } sym::vtable_align => { let ptr = self.read_pointer(&args[0])?; - let (_size, align) = self.get_vtable_size_and_align(ptr)?; + // `None` because we don't know which trait to expect here; any vtable is okay. + let (_size, align) = self.get_vtable_size_and_align(ptr, None)?; self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 1df9f5f7b2a..44e7244a513 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -36,8 +36,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { pub fn get_vtable_size_and_align( &self, vtable: Pointer>, + expected_trait: Option<&'tcx ty::List>>, ) -> InterpResult<'tcx, (Size, Align)> { - let ty = self.get_ptr_vtable_ty(vtable, None)?; + let ty = self.get_ptr_vtable_ty(vtable, expected_trait)?; let layout = self.layout_of(ty)?; assert!(layout.is_sized(), "there are no vtables for unsized types"); Ok((layout.size, layout.align.abi)) diff --git a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs index f71df9a1c90..982d57b7372 100644 --- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs +++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs @@ -1,5 +1,5 @@ -// Validation stops this too early. -//@compile-flags: -Zmiri-disable-validation +// Validation and SB stop this too early. +//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows trait T1 { #[allow(dead_code)] -- cgit 1.4.1-3-g733a5 From 3757136d8e0d8ddca294453e5a5ce70cfa3417e9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Jun 2024 17:26:36 +0200 Subject: interpret: dyn trait metadata check: equate traits in a proper way --- compiler/rustc_const_eval/src/interpret/memory.rs | 4 +-- compiler/rustc_const_eval/src/interpret/traits.rs | 36 ++++++++++++++++++- ...sue-miri-3541-dyn-vtable-trait-normalization.rs | 40 ++++++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs (limited to 'compiler/rustc_const_eval/src/interpret/traits.rs') diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 84c6dad1cd3..e2e39399f3a 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -884,9 +884,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset))) }; if let Some(expected_trait) = expected_trait { - if vtable_trait != expected_trait.principal() { - throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); - } + self.check_vtable_for_type(vtable_trait, expected_trait)?; } Ok(ty) } diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 44e7244a513..bd2c6519421 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,11 +1,14 @@ +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::ObligationCause; use rustc_middle::mir::interpret::{InterpResult, Pointer}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Ty}; use rustc_target::abi::{Align, Size}; +use rustc_trait_selection::traits::ObligationCtxt; use tracing::trace; use super::util::ensure_monomorphic_enough; -use super::{InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable}; +use super::{throw_ub, InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable}; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for @@ -44,6 +47,37 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok((layout.size, layout.align.abi)) } + /// Check that the given vtable trait is valid for a pointer/reference/place with the given + /// expected trait type. + pub(super) fn check_vtable_for_type( + &self, + vtable_trait: Option>, + expected_trait: &'tcx ty::List>, + ) -> InterpResult<'tcx> { + // Fast path: if they are equal, it's all fine. + if expected_trait.principal() == vtable_trait { + return Ok(()); + } + if let (Some(expected_trait), Some(vtable_trait)) = + (expected_trait.principal(), vtable_trait) + { + // Slow path: spin up an inference context to check if these traits are sufficiently equal. + let infcx = self.tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + let cause = ObligationCause::dummy_with_span(self.cur_span()); + // equate the two trait refs after normalization + let expected_trait = ocx.normalize(&cause, self.param_env, expected_trait); + let vtable_trait = ocx.normalize(&cause, self.param_env, vtable_trait); + if ocx.eq(&cause, self.param_env, expected_trait, vtable_trait).is_ok() { + if ocx.select_all_or_error().is_empty() { + // All good. + return Ok(()); + } + } + } + throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); + } + /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. pub(super) fn unpack_dyn_trait( &self, diff --git a/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs b/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs new file mode 100644 index 00000000000..c46031de2d8 --- /dev/null +++ b/src/tools/miri/tests/pass/issues/issue-miri-3541-dyn-vtable-trait-normalization.rs @@ -0,0 +1,40 @@ +#![feature(ptr_metadata)] +// This test is the result of minimizing the `emplacable` crate to reproduce +// . + +use std::{ops::FnMut, ptr::Pointee}; + +pub type EmplacerFn<'a, T> = dyn for<'b> FnMut(::Metadata) + 'a; + +#[repr(transparent)] +pub struct Emplacer<'a, T>(EmplacerFn<'a, T>) +where + T: ?Sized; + +impl<'a, T> Emplacer<'a, T> +where + T: ?Sized, +{ + pub unsafe fn from_fn<'b>(emplacer_fn: &'b mut EmplacerFn<'a, T>) -> &'b mut Self { + // This used to trigger: + // constructing invalid value: wrong trait in wide pointer vtable: expected + // `std::ops::FnMut(<[std::boxed::Box] as std::ptr::Pointee>::Metadata)`, but encountered + // `std::ops::FnMut<(usize,)>`. + unsafe { &mut *((emplacer_fn as *mut EmplacerFn<'a, T>) as *mut Self) } + } +} + +pub fn box_new_with() +where + T: ?Sized, +{ + let emplacer_closure = &mut |_meta| { + unreachable!(); + }; + + unsafe { Emplacer::::from_fn(emplacer_closure) }; +} + +fn main() { + box_new_with::<[Box]>(); +} -- cgit 1.4.1-3-g733a5