diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index f6937dc145d5b..b79d7441acac7 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -82,12 +82,6 @@ const_eval_double_storage_live = const_eval_dyn_call_not_a_method = `dyn` call trying to call something that is not a method -const_eval_dyn_call_vtable_mismatch = - `dyn` call on a pointer whose vtable does not match its type - -const_eval_dyn_star_call_vtable_mismatch = - `dyn*` call on a pointer whose vtable does not match its type - const_eval_error = {$error_kind -> [static] could not evaluate static initializer [const] evaluation of constant value failed @@ -192,6 +186,8 @@ const_eval_invalid_uninit_bytes_unknown = const_eval_invalid_vtable_pointer = using {$pointer} as vtable pointer but it does not point to a vtable +const_eval_invalid_vtable_trait = + using vtable for trait `{$vtable_trait}` but trait `{$expected_trait}` was expected const_eval_live_drop = destructor of `{$dropped_ty}` cannot be evaluated at compile-time @@ -401,9 +397,6 @@ const_eval_unterminated_c_string = const_eval_unwind_past_top = unwinding past the topmost frame of the stack -const_eval_upcast_mismatch = - upcast on a pointer whose vtable does not match its type - ## The `front_matter`s here refer to either `const_eval_front_matter_invalid_value` or `const_eval_front_matter_invalid_value_with_path`. ## (We'd love to sort this differently to make that more clear but tidy won't let us...) const_eval_validation_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant @@ -450,6 +443,7 @@ const_eval_validation_invalid_fn_ptr = {$front_matter}: encountered {$value}, bu const_eval_validation_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object const_eval_validation_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object const_eval_validation_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer +const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wide pointer vtable: expected `{$ref_trait}`, but encountered `{$vtable_trait}` const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` const_eval_validation_null_box = {$front_matter}: encountered a null box diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index a60cedd6500d5..90d4f1168e4fd 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -498,6 +498,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { InvalidTag(_) => const_eval_invalid_tag, InvalidFunctionPointer(_) => const_eval_invalid_function_pointer, InvalidVTablePointer(_) => const_eval_invalid_vtable_pointer, + InvalidVTableTrait { .. } => const_eval_invalid_vtable_trait, InvalidStr(_) => const_eval_invalid_str, InvalidUninitBytes(None) => const_eval_invalid_uninit_bytes_unknown, InvalidUninitBytes(Some(_)) => const_eval_invalid_uninit_bytes, @@ -537,6 +538,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { | DeadLocal | UninhabitedEnumVariantWritten(_) | UninhabitedEnumVariantRead(_) => {} + BoundsCheckFailed { len, index } => { diag.arg("len", len); diag.arg("index", index); @@ -544,6 +546,13 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => { diag.arg("pointer", ptr); } + InvalidVTableTrait { expected_trait, vtable_trait } => { + diag.arg("expected_trait", expected_trait.to_string()); + diag.arg( + "vtable_trait", + vtable_trait.map(|t| t.to_string()).unwrap_or_else(|| format!("")), + ); + } PointerUseAfterFree(alloc_id, msg) => { diag.arg("alloc_id", alloc_id) .arg("bad_pointer_message", bad_pointer_message(msg, dcx)); @@ -634,6 +643,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { UninhabitedEnumVariant => const_eval_validation_uninhabited_enum_variant, Uninit { .. } => const_eval_validation_uninit, InvalidVTablePtr { .. } => const_eval_validation_invalid_vtable_ptr, + InvalidMetaWrongTrait { .. } => const_eval_validation_invalid_vtable_trait, InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Box } => { const_eval_validation_invalid_box_slice_meta } @@ -773,6 +783,13 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { DanglingPtrNoProvenance { pointer, .. } => { err.arg("pointer", pointer); } + InvalidMetaWrongTrait { expected_trait: ref_trait, vtable_trait } => { + err.arg("ref_trait", ref_trait.to_string()); + err.arg( + "vtable_trait", + vtable_trait.map(|t| t.to_string()).unwrap_or_else(|| format!("")), + ); + } NullPtr { .. } | PtrToStatic { .. } | ConstRefToMutable diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index 9447d18fe8c93..76e59ea90559f 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -393,6 +393,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = self.read_immediate(src)?; if data_a.principal() == data_b.principal() { // A NOP cast that doesn't actually change anything, should be allowed even with mismatching vtables. + // (But currently mismatching vtables violate the validity invariant so UB is triggered anyway.) return self.write_immediate(*val, dest); } let (old_data, old_vptr) = val.to_scalar_pair(); @@ -400,7 +401,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let old_vptr = old_vptr.to_pointer(self)?; let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?; if old_trait != data_a.principal() { - throw_ub_custom!(fluent::const_eval_upcast_mismatch); + throw_ub!(InvalidVTableTrait { + expected_trait: data_a, + vtable_trait: old_trait, + }); } let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?; self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 8364a5a8d186a..e5241f1ba19e1 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -1020,16 +1020,20 @@ where pub(super) fn unpack_dyn_trait( &self, mplace: &MPlaceTy<'tcx, M::Provenance>, + expected_trait: &'tcx ty::List>, ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, Pointer>)> { 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(vtable)?; - let layout = self.layout_of(ty)?; + let (ty, vtable_trait) = self.get_ptr_vtable(vtable)?; + if expected_trait.principal() != vtable_trait { + throw_ub!(InvalidVTableTrait { expected_trait, vtable_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 = MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace }, layout }; Ok((mplace, vtable)) @@ -1040,6 +1044,7 @@ where pub(super) fn unpack_dyn_star>( &self, val: &P, + expected_trait: &'tcx ty::List>, ) -> InterpResult<'tcx, (P, Pointer>)> { assert!( matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)), @@ -1048,10 +1053,12 @@ where 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(vtable)?; + let (ty, vtable_trait) = self.get_ptr_vtable(vtable)?; + if expected_trait.principal() != vtable_trait { + throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); + } + // `data` is already the right thing but has the wrong type. So we transmute it. let layout = self.layout_of(ty)?; - // `data` is already the right thing but has the wrong type. So we transmute it, by - // projecting with offset 0. let data = data.transmute(layout, self)?; Ok((data, vtable)) } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index c0e27e86d500a..0c3b01d449bd8 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -802,11 +802,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let (vptr, dyn_ty, adjusted_receiver) = if let ty::Dynamic(data, _, ty::DynStar) = receiver_place.layout.ty.kind() { - let (recv, vptr) = self.unpack_dyn_star(&receiver_place)?; - let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; - if dyn_trait != data.principal() { - throw_ub_custom!(fluent::const_eval_dyn_star_call_vtable_mismatch); - } + let (recv, vptr) = self.unpack_dyn_star(&receiver_place, data)?; + let (dyn_ty, _dyn_trait) = self.get_ptr_vtable(vptr)?; (vptr, dyn_ty, recv.ptr()) } else { @@ -828,7 +825,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let vptr = receiver_place.meta().unwrap_meta().to_pointer(self)?; let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; if dyn_trait != data.principal() { - throw_ub_custom!(fluent::const_eval_dyn_call_vtable_mismatch); + throw_ub!(InvalidVTableTrait { + expected_trait: data, + vtable_trait: dyn_trait, + }); } // It might be surprising that we use a pointer as the receiver even if this @@ -938,13 +938,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let place = self.force_allocation(place)?; let place = match place.layout.ty.kind() { - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(data, _, ty::Dyn) => { // Dropping a trait object. Need to find actual drop fn. - self.unpack_dyn_trait(&place)?.0 + self.unpack_dyn_trait(&place, data)?.0 } - ty::Dynamic(_, _, ty::DynStar) => { + ty::Dynamic(data, _, ty::DynStar) => { // Dropping a `dyn*`. Need to find actual drop fn. - self.unpack_dyn_star(&place)?.0 + self.unpack_dyn_star(&place, data)?.0 } _ => { debug_assert_eq!( diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index b8a1733e45ad6..14566719ccd72 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -339,16 +339,22 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' ) -> InterpResult<'tcx> { let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); match tail.kind() { - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(data, _, ty::Dyn) => { let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; // Make sure it is a genuine vtable pointer. - let (_ty, _trait) = try_validation!( + let (_dyn_ty, dyn_trait) = try_validation!( self.ecx.get_ptr_vtable(vtable), self.path, Ub(DanglingIntPointer(..) | InvalidVTablePointer(..)) => InvalidVTablePtr { value: format!("{vtable}") } ); - // FIXME: check if the type/trait match what ty::Dynamic says? + // Make sure it is for the right trait. + if dyn_trait != data.principal() { + throw_validation_failure!( + self.path, + InvalidMetaWrongTrait { expected_trait: data, vtable_trait: dyn_trait } + ); + } } ty::Slice(..) | ty::Str => { let _len = meta.unwrap_meta().to_target_usize(self.ecx)?; @@ -933,7 +939,16 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } } _ => { - self.walk_value(op)?; // default handler + // default handler + try_validation!( + self.walk_value(op), + self.path, + // It's not great to catch errors here, since we can't give a very good path, + // but it's better than ICEing. + Ub(InvalidVTableTrait { expected_trait, vtable_trait }) => { + InvalidMetaWrongTrait { expected_trait, vtable_trait: *vtable_trait } + }, + ); } } diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 0e824f3f592d4..84557b8e2d600 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -88,22 +88,22 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { // Special treatment for special types, where the (static) layout is not sufficient. match *ty.kind() { // If it is a trait object, switch to the real type that was used to create it. - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(data, _, ty::Dyn) => { // Dyn types. This is unsized, and the actual dynamic type of the data is given by the // vtable stored in the place metadata. // unsized values are never immediate, so we can assert_mem_place let op = v.to_op(self.ecx())?; let dest = op.assert_mem_place(); - let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0; + let inner_mplace = self.ecx().unpack_dyn_trait(&dest, data)?.0; trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout); // recurse with the inner type return self.visit_field(v, 0, &inner_mplace.into()); } - ty::Dynamic(_, _, ty::DynStar) => { + ty::Dynamic(data, _, ty::DynStar) => { // DynStar types. Very different from a dyn type (but strangely part of the // same variant in `TyKind`): These are pairs where the 2nd component is the // vtable, and the first component is the data (which must be ptr-sized). - let data = self.ecx().unpack_dyn_star(v)?.0; + let data = self.ecx().unpack_dyn_star(v, data)?.0; return self.visit_field(v, 0, &data); } // Slices do not need special handling here: they have `Array` field diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 65ce1cd8f50c6..a3d16d4f097a8 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -2,7 +2,7 @@ use super::{AllocId, AllocRange, ConstAllocation, Pointer, Scalar}; use crate::error; use crate::mir::{ConstAlloc, ConstValue}; -use crate::ty::{layout, tls, Ty, TyCtxt, ValTree}; +use crate::ty::{self, layout, tls, Ty, TyCtxt, ValTree}; use rustc_ast_ir::Mutability; use rustc_data_structures::sync::Lock; @@ -344,6 +344,11 @@ pub enum UndefinedBehaviorInfo<'tcx> { InvalidFunctionPointer(Pointer), /// Using a pointer-not-to-a-vtable as vtable pointer. InvalidVTablePointer(Pointer), + /// Using a vtable for the wrong trait. + InvalidVTableTrait { + expected_trait: &'tcx ty::List>, + vtable_trait: Option>, + }, /// Using a string that is not valid UTF-8, InvalidStr(std::str::Utf8Error), /// Using uninitialized data where it is not allowed. @@ -414,34 +419,86 @@ impl From for ExpectedKind { #[derive(Debug)] pub enum ValidationErrorKind<'tcx> { - PointerAsInt { expected: ExpectedKind }, + PointerAsInt { + expected: ExpectedKind, + }, PartialPointer, - PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> }, - PtrToStatic { ptr_kind: PointerKind }, + PtrToUninhabited { + ptr_kind: PointerKind, + ty: Ty<'tcx>, + }, + PtrToStatic { + ptr_kind: PointerKind, + }, ConstRefToMutable, ConstRefToExtern, MutableRefToImmutable, UnsafeCellInImmutable, NullFnPtr, NeverVal, - NullablePtrOutOfRange { range: WrappingRange, max_value: u128 }, - PtrOutOfRange { range: WrappingRange, max_value: u128 }, - OutOfRange { value: String, range: WrappingRange, max_value: u128 }, - UninhabitedVal { ty: Ty<'tcx> }, - InvalidEnumTag { value: String }, + NullablePtrOutOfRange { + range: WrappingRange, + max_value: u128, + }, + PtrOutOfRange { + range: WrappingRange, + max_value: u128, + }, + OutOfRange { + value: String, + range: WrappingRange, + max_value: u128, + }, + UninhabitedVal { + ty: Ty<'tcx>, + }, + InvalidEnumTag { + value: String, + }, UninhabitedEnumVariant, - Uninit { expected: ExpectedKind }, - InvalidVTablePtr { value: String }, - InvalidMetaSliceTooLarge { ptr_kind: PointerKind }, - InvalidMetaTooLarge { ptr_kind: PointerKind }, - UnalignedPtr { ptr_kind: PointerKind, required_bytes: u64, found_bytes: u64 }, - NullPtr { ptr_kind: PointerKind }, - DanglingPtrNoProvenance { ptr_kind: PointerKind, pointer: String }, - DanglingPtrOutOfBounds { ptr_kind: PointerKind }, - DanglingPtrUseAfterFree { ptr_kind: PointerKind }, - InvalidBool { value: String }, - InvalidChar { value: String }, - InvalidFnPtr { value: String }, + Uninit { + expected: ExpectedKind, + }, + InvalidVTablePtr { + value: String, + }, + InvalidMetaWrongTrait { + expected_trait: &'tcx ty::List>, + vtable_trait: Option>, + }, + InvalidMetaSliceTooLarge { + ptr_kind: PointerKind, + }, + InvalidMetaTooLarge { + ptr_kind: PointerKind, + }, + UnalignedPtr { + ptr_kind: PointerKind, + required_bytes: u64, + found_bytes: u64, + }, + NullPtr { + ptr_kind: PointerKind, + }, + DanglingPtrNoProvenance { + ptr_kind: PointerKind, + pointer: String, + }, + DanglingPtrOutOfBounds { + ptr_kind: PointerKind, + }, + DanglingPtrUseAfterFree { + ptr_kind: PointerKind, + }, + InvalidBool { + value: String, + }, + InvalidChar { + value: String, + }, + InvalidFnPtr { + value: String, + }, } /// Error information for when the program did something that might (or might not) be correct 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 13f913454dcdf..f71df9a1c909d 100644 --- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs +++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.rs @@ -1,3 +1,6 @@ +// Validation stops this too early. +//@compile-flags: -Zmiri-disable-validation + trait T1 { #[allow(dead_code)] fn method1(self: Box); @@ -13,5 +16,5 @@ impl T1 for i32 { fn main() { let r = Box::new(0) as Box; let r2: Box = unsafe { std::mem::transmute(r) }; - r2.method2(); //~ERROR: call on a pointer whose vtable does not match its type + r2.method2(); //~ERROR: using vtable for trait `T1` but trait `T2` was expected } diff --git a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr index 365186bcc4b41..019a55bcdcb1a 100644 --- a/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr +++ b/src/tools/miri/tests/fail/dyn-call-trait-mismatch.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `dyn` call on a pointer whose vtable does not match its type +error: Undefined Behavior: using vtable for trait `T1` but trait `T2` was expected --> $DIR/dyn-call-trait-mismatch.rs:LL:CC | LL | r2.method2(); - | ^^^^^^^^^^^^ `dyn` call on a pointer whose vtable does not match its type + | ^^^^^^^^^^^^ using vtable for trait `T1` but trait `T2` was expected | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.rs b/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.rs new file mode 100644 index 0000000000000..dff5a21c4c777 --- /dev/null +++ b/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.rs @@ -0,0 +1,15 @@ +// This upcast is currently forbidden because it involves an invalid value. +// However, if in the future we relax the validity requirements for raw pointer vtables, +// we could consider allowing this again -- the cast itself isn't doing anything wrong, +// only the transmutes needed to set up the testcase are wrong. + +use std::fmt; + +fn main() { + // vtable_mismatch_nop_cast + let ptr: &dyn fmt::Display = &0; + let ptr: *const (dyn fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) }; //~ERROR: wrong trait + // Even though the vtable is for the wrong trait, this cast doesn't actually change the needed + // vtable so it should still be allowed -- if we ever allow the line above. + let _ptr2 = ptr as *const dyn fmt::Debug; +} diff --git a/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.stderr b/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.stderr new file mode 100644 index 0000000000000..4165d5ea15d97 --- /dev/null +++ b/src/tools/miri/tests/fail/dyn-upcast-nop-wrong-trait.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug + std::marker::Send + std::marker::Sync`, but encountered `std::fmt::Display` + --> $DIR/dyn-upcast-nop-wrong-trait.rs:LL:CC + | +LL | let ptr: *const (dyn fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug + std::marker::Send + std::marker::Sync`, but encountered `std::fmt::Display` + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/dyn-upcast-nop-wrong-trait.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs index 982f33b0a3102..1d6b6777032de 100644 --- a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs +++ b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.rs @@ -1,3 +1,6 @@ +// Validation stops this too early. +//@compile-flags: -Zmiri-disable-validation + #![feature(trait_upcasting)] #![allow(incomplete_features)] @@ -57,7 +60,7 @@ impl Baz for i32 { fn main() { let baz: &dyn Baz = &1; - let baz_fake: &dyn Bar = unsafe { std::mem::transmute(baz) }; - let _err = baz_fake as &dyn Foo; - //~^ERROR: upcast on a pointer whose vtable does not match its type + let baz_fake: *const dyn Bar = unsafe { std::mem::transmute(baz) }; + let _err = baz_fake as *const dyn Foo; + //~^ERROR: using vtable for trait `Baz` but trait `Bar` was expected } diff --git a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr index 8bac908b864bc..6a2415cf57e29 100644 --- a/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr +++ b/src/tools/miri/tests/fail/dyn-upcast-trait-mismatch.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: upcast on a pointer whose vtable does not match its type +error: Undefined Behavior: using vtable for trait `Baz` but trait `Bar` was expected --> $DIR/dyn-upcast-trait-mismatch.rs:LL:CC | -LL | let _err = baz_fake as &dyn Foo; - | ^^^^^^^^ upcast on a pointer whose vtable does not match its type +LL | let _err = baz_fake as *const dyn Foo; + | ^^^^^^^^ using vtable for trait `Baz` but trait `Bar` was expected | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.rs b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.rs new file mode 100644 index 0000000000000..9b1cefc4b1d3d --- /dev/null +++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.rs @@ -0,0 +1,12 @@ +use std::mem; + +// Make sure we notice the mismatch also if the difference is "only" in the generic +// parameters of the trait. + +trait Trait {} +impl Trait for T {} + +fn main() { + let x: &dyn Trait = &0; + let _y: *const dyn Trait = unsafe { mem::transmute(x) }; //~ERROR: wrong trait +} diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.stderr b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.stderr new file mode 100644 index 0000000000000..1219f9f88cf8c --- /dev/null +++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait-generic.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `Trait`, but encountered `Trait` + --> $DIR/wrong-dyn-trait-generic.rs:LL:CC + | +LL | let _y: *const dyn Trait = unsafe { mem::transmute(x) }; + | ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `Trait`, but encountered `Trait` + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/wrong-dyn-trait-generic.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait.rs b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.rs new file mode 100644 index 0000000000000..d6049196f2662 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.rs @@ -0,0 +1,6 @@ +use std::{fmt, mem}; + +fn main() { + let x: &dyn Send = &0; + let _y: *const dyn fmt::Debug = unsafe { mem::transmute(x) }; //~ERROR: wrong trait +} diff --git a/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr new file mode 100644 index 0000000000000..e3503323b31a7 --- /dev/null +++ b/src/tools/miri/tests/fail/validity/wrong-dyn-trait.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `` + --> $DIR/wrong-dyn-trait.rs:LL:CC + | +LL | let _y: *const dyn fmt::Debug = unsafe { mem::transmute(x) }; + | ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `` + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/wrong-dyn-trait.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs b/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs index 2491bda0917d3..adbf5df62cc5d 100644 --- a/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs +++ b/src/tools/miri/tests/pass/cast-rfc0401-vtable-kinds.rs @@ -25,7 +25,7 @@ impl Foo for u32 { impl Bar for () {} unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo + 'a)) -> u32 { - let foo_e: *const dyn Foo = t as *const _; + let foo_e: *const dyn Foo = t as *const _; let r_1 = foo_e as *mut dyn Foo; (&*r_1).foo(0) diff --git a/src/tools/miri/tests/pass/dyn-upcast.rs b/src/tools/miri/tests/pass/dyn-upcast.rs index 529b9c471d426..ddc4bdcf082a8 100644 --- a/src/tools/miri/tests/pass/dyn-upcast.rs +++ b/src/tools/miri/tests/pass/dyn-upcast.rs @@ -1,24 +1,26 @@ #![feature(trait_upcasting)] #![allow(incomplete_features)] +use std::fmt; + fn main() { basic(); diamond(); struct_(); replace_vptr(); - vtable_mismatch_nop_cast(); + vtable_nop_cast(); } -fn vtable_mismatch_nop_cast() { - let ptr: &dyn std::fmt::Display = &0; - // Even though the vtable is for the wrong trait, this cast doesn't actually change the needed - // vtable so it should still be allowed. - let ptr: *const (dyn std::fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) }; - let _ptr2 = ptr as *const dyn std::fmt::Debug; +fn vtable_nop_cast() { + let ptr: &dyn fmt::Debug = &0; + // We transmute things around, but the principal trait does not change, so this is allowed. + let ptr: *const (dyn fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) }; + // This cast is a NOP and should be allowed. + let _ptr2 = ptr as *const dyn fmt::Debug; } fn basic() { - trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + trait Foo: PartialEq + fmt::Debug + Send + Sync { fn a(&self) -> i32 { 10 } @@ -67,7 +69,7 @@ fn basic() { } let baz: &dyn Baz = &1; - let _: &dyn std::fmt::Debug = baz; + let _: &dyn fmt::Debug = baz; assert_eq!(*baz, 1); assert_eq!(baz.a(), 100); assert_eq!(baz.b(), 200); @@ -77,7 +79,7 @@ fn basic() { assert_eq!(baz.w(), 21); let bar: &dyn Bar = baz; - let _: &dyn std::fmt::Debug = bar; + let _: &dyn fmt::Debug = bar; assert_eq!(*bar, 1); assert_eq!(bar.a(), 100); assert_eq!(bar.b(), 200); @@ -86,14 +88,14 @@ fn basic() { assert_eq!(bar.w(), 21); let foo: &dyn Foo = baz; - let _: &dyn std::fmt::Debug = foo; + let _: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); assert_eq!(foo.z(), 11); assert_eq!(foo.y(), 12); let foo: &dyn Foo = bar; - let _: &dyn std::fmt::Debug = foo; + let _: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); assert_eq!(foo.z(), 11); @@ -101,7 +103,7 @@ fn basic() { } fn diamond() { - trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + trait Foo: PartialEq + fmt::Debug + Send + Sync { fn a(&self) -> i32 { 10 } @@ -166,7 +168,7 @@ fn diamond() { } let baz: &dyn Baz = &1; - let _: &dyn std::fmt::Debug = baz; + let _: &dyn fmt::Debug = baz; assert_eq!(*baz, 1); assert_eq!(baz.a(), 100); assert_eq!(baz.b(), 200); @@ -178,7 +180,7 @@ fn diamond() { assert_eq!(baz.v(), 31); let bar1: &dyn Bar1 = baz; - let _: &dyn std::fmt::Debug = bar1; + let _: &dyn fmt::Debug = bar1; assert_eq!(*bar1, 1); assert_eq!(bar1.a(), 100); assert_eq!(bar1.b(), 200); @@ -187,7 +189,7 @@ fn diamond() { assert_eq!(bar1.w(), 21); let bar2: &dyn Bar2 = baz; - let _: &dyn std::fmt::Debug = bar2; + let _: &dyn fmt::Debug = bar2; assert_eq!(*bar2, 1); assert_eq!(bar2.a(), 100); assert_eq!(bar2.c(), 300); @@ -196,17 +198,17 @@ fn diamond() { assert_eq!(bar2.v(), 31); let foo: &dyn Foo = baz; - let _: &dyn std::fmt::Debug = foo; + let _: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); let foo: &dyn Foo = bar1; - let _: &dyn std::fmt::Debug = foo; + let _: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); let foo: &dyn Foo = bar2; - let _: &dyn std::fmt::Debug = foo; + let _: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); } @@ -215,7 +217,7 @@ fn struct_() { use std::rc::Rc; use std::sync::Arc; - trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + trait Foo: PartialEq + fmt::Debug + Send + Sync { fn a(&self) -> i32 { 10 } diff --git a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs index 410e15d024fab..0d8f92f013fba 100644 --- a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs +++ b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs @@ -17,7 +17,7 @@ impl Foo for u32 { fn foo(&self, _: u32) -> u32 { self+43 } } impl Bar for () {} unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo+'a)) -> u32 { - let foo_e : *const dyn Foo = t as *const _; + let foo_e : *const dyn Foo = t as *const _; let r_1 = foo_e as *mut dyn Foo; (&*r_1).foo(0)