Auto merge of #128742 - RalfJung:miri-vtable-uniqueness, r=saethlin
miri: make vtable addresses not globally unique Miri currently gives vtables a unique global address. That's not actually matching reality though. So this PR enables Miri to generate different addresses for the same type-trait pair. To avoid generating an unbounded number of `AllocId` (and consuming unbounded amounts of memory), we use the "salt" technique that we also already use for giving constants non-unique addresses: the cache is keyed on a "salt" value n top of the actually relevant key, and Miri picks a random salt (currently in the range `0..16`) each time it needs to choose an `AllocId` for one of these globals -- that means we'll get up to 16 different addresses for each vtable. The salt scheme is integrated into the global allocation deduplication logic in `tcx`, and also used for functions and string literals. (So this also fixes the problem that casting the same function to a fn ptr over and over will consume unbounded memory.) r? `@saethlin` Fixes https://github.com/rust-lang/miri/issues/3737
This commit is contained in:
commit
591ecb88df
@ -400,6 +400,12 @@ fn unsize_into_ptr(
|
||||
}
|
||||
(ty::Dynamic(data_a, _, ty::Dyn), ty::Dynamic(data_b, _, ty::Dyn)) => {
|
||||
let val = self.read_immediate(src)?;
|
||||
// MIR building generates odd NOP casts, prevent them from causing unexpected trouble.
|
||||
// See <https://github.com/rust-lang/rust/issues/128880>.
|
||||
// FIXME: ideally we wouldn't have to do this.
|
||||
if data_a == data_b {
|
||||
return self.write_immediate(*val, dest);
|
||||
}
|
||||
// Take apart the old pointer, and find the dynamic type.
|
||||
let (old_data, old_vptr) = val.to_scalar_pair();
|
||||
let old_data = old_data.to_pointer(self)?;
|
||||
|
@ -19,7 +19,7 @@
|
||||
use super::{
|
||||
throw_unsup, throw_unsup_format, AllocBytes, AllocId, AllocKind, AllocRange, Allocation,
|
||||
ConstAllocation, CtfeProvenance, FnArg, Frame, ImmTy, InterpCx, InterpResult, MPlaceTy,
|
||||
MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance,
|
||||
MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance, CTFE_ALLOC_SALT,
|
||||
};
|
||||
|
||||
/// Data returned by [`Machine::after_stack_pop`], and consumed by
|
||||
@ -575,6 +575,14 @@ fn eval_mir_constant<F>(
|
||||
{
|
||||
eval(ecx, val, span, layout)
|
||||
}
|
||||
|
||||
/// Returns the salt to be used for a deduplicated global alloation.
|
||||
/// If the allocation is for a function, the instance is provided as well
|
||||
/// (this lets Miri ensure unique addresses for some functions).
|
||||
fn get_global_alloc_salt(
|
||||
ecx: &InterpCx<'tcx, Self>,
|
||||
instance: Option<ty::Instance<'tcx>>,
|
||||
) -> usize;
|
||||
}
|
||||
|
||||
/// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines
|
||||
@ -677,4 +685,12 @@ fn ptr_get_alloc(
|
||||
let (prov, offset) = ptr.into_parts();
|
||||
Some((prov.alloc_id(), offset, prov.immutable()))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn get_global_alloc_salt(
|
||||
_ecx: &InterpCx<$tcx, Self>,
|
||||
_instance: Option<ty::Instance<$tcx>>,
|
||||
) -> usize {
|
||||
CTFE_ALLOC_SALT
|
||||
}
|
||||
}
|
||||
|
@ -195,7 +195,10 @@ pub fn global_root_pointer(
|
||||
|
||||
pub fn fn_ptr(&mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>) -> Pointer<M::Provenance> {
|
||||
let id = match fn_val {
|
||||
FnVal::Instance(instance) => self.tcx.reserve_and_set_fn_alloc(instance),
|
||||
FnVal::Instance(instance) => {
|
||||
let salt = M::get_global_alloc_salt(self, Some(instance));
|
||||
self.tcx.reserve_and_set_fn_alloc(instance, salt)
|
||||
}
|
||||
FnVal::Other(extra) => {
|
||||
// FIXME(RalfJung): Should we have a cache here?
|
||||
let id = self.tcx.reserve_alloc_id();
|
||||
|
@ -1008,7 +1008,8 @@ pub fn allocate_str(
|
||||
// Use cache for immutable strings.
|
||||
let ptr = if mutbl.is_not() {
|
||||
// Use dedup'd allocation function.
|
||||
let id = tcx.allocate_bytes_dedup(str.as_bytes());
|
||||
let salt = M::get_global_alloc_salt(self, None);
|
||||
let id = tcx.allocate_bytes_dedup(str.as_bytes(), salt);
|
||||
|
||||
// Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation.
|
||||
M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))?
|
||||
|
@ -28,7 +28,9 @@ pub fn get_vtable_ptr(
|
||||
ensure_monomorphic_enough(*self.tcx, ty)?;
|
||||
ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;
|
||||
|
||||
let vtable_symbolic_allocation = self.tcx.reserve_and_set_vtable_alloc(ty, poly_trait_ref);
|
||||
let salt = M::get_global_alloc_salt(self, None);
|
||||
let vtable_symbolic_allocation =
|
||||
self.tcx.reserve_and_set_vtable_alloc(ty, poly_trait_ref, salt);
|
||||
let vtable_ptr = self.global_root_pointer(Pointer::from(vtable_symbolic_allocation))?;
|
||||
Ok(vtable_ptr.into())
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
use std::{fmt, io};
|
||||
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_attr::InlineAttr;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sync::Lock;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
@ -46,7 +45,7 @@
|
||||
pub use self::value::Scalar;
|
||||
use crate::mir;
|
||||
use crate::ty::codec::{TyDecoder, TyEncoder};
|
||||
use crate::ty::{self, GenericArgKind, Instance, Ty, TyCtxt};
|
||||
use crate::ty::{self, Instance, Ty, TyCtxt};
|
||||
|
||||
/// Uniquely identifies one of the following:
|
||||
/// - A constant
|
||||
@ -126,11 +125,10 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>(
|
||||
AllocDiscriminant::Alloc.encode(encoder);
|
||||
alloc.encode(encoder);
|
||||
}
|
||||
GlobalAlloc::Function { instance, unique } => {
|
||||
GlobalAlloc::Function { instance } => {
|
||||
trace!("encoding {:?} with {:#?}", alloc_id, instance);
|
||||
AllocDiscriminant::Fn.encode(encoder);
|
||||
instance.encode(encoder);
|
||||
unique.encode(encoder);
|
||||
}
|
||||
GlobalAlloc::VTable(ty, poly_trait_ref) => {
|
||||
trace!("encoding {:?} with {ty:#?}, {poly_trait_ref:#?}", alloc_id);
|
||||
@ -219,8 +217,7 @@ pub fn decode_alloc_id<'tcx, D>(&self, decoder: &mut D) -> AllocId
|
||||
}
|
||||
|
||||
// Now decode the actual data.
|
||||
let alloc_id = decoder.with_position(pos, |decoder| {
|
||||
match alloc_kind {
|
||||
let alloc_id = decoder.with_position(pos, |decoder| match alloc_kind {
|
||||
AllocDiscriminant::Alloc => {
|
||||
trace!("creating memory alloc ID");
|
||||
let alloc = <ConstAllocation<'tcx> as Decodable<_>>::decode(decoder);
|
||||
@ -231,11 +228,7 @@ pub fn decode_alloc_id<'tcx, D>(&self, decoder: &mut D) -> AllocId
|
||||
trace!("creating fn alloc ID");
|
||||
let instance = ty::Instance::decode(decoder);
|
||||
trace!("decoded fn alloc instance: {:?}", instance);
|
||||
let unique = bool::decode(decoder);
|
||||
// Here we cannot call `reserve_and_set_fn_alloc` as that would use a query, which
|
||||
// is not possible in this context. That's why the allocation stores
|
||||
// whether it is unique or not.
|
||||
decoder.interner().reserve_and_set_fn_alloc_internal(instance, unique)
|
||||
decoder.interner().reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT)
|
||||
}
|
||||
AllocDiscriminant::VTable => {
|
||||
trace!("creating vtable alloc ID");
|
||||
@ -243,7 +236,7 @@ pub fn decode_alloc_id<'tcx, D>(&self, decoder: &mut D) -> AllocId
|
||||
let poly_trait_ref =
|
||||
<Option<ty::PolyExistentialTraitRef<'_>> as Decodable<D>>::decode(decoder);
|
||||
trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}");
|
||||
decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref)
|
||||
decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref, CTFE_ALLOC_SALT)
|
||||
}
|
||||
AllocDiscriminant::Static => {
|
||||
trace!("creating extern static alloc ID");
|
||||
@ -251,7 +244,6 @@ pub fn decode_alloc_id<'tcx, D>(&self, decoder: &mut D) -> AllocId
|
||||
trace!("decoded static def-ID: {:?}", did);
|
||||
decoder.interner().reserve_and_set_static_alloc(did)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
*entry = State::Done(alloc_id);
|
||||
@ -265,12 +257,7 @@ pub fn decode_alloc_id<'tcx, D>(&self, decoder: &mut D) -> AllocId
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, TyDecodable, TyEncodable, HashStable)]
|
||||
pub enum GlobalAlloc<'tcx> {
|
||||
/// The alloc ID is used as a function pointer.
|
||||
Function {
|
||||
instance: Instance<'tcx>,
|
||||
/// Stores whether this instance is unique, i.e. all pointers to this function use the same
|
||||
/// alloc ID.
|
||||
unique: bool,
|
||||
},
|
||||
Function { instance: Instance<'tcx> },
|
||||
/// This alloc ID points to a symbolic (not-reified) vtable.
|
||||
VTable(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>),
|
||||
/// The alloc ID points to a "lazy" static variable that did not get computed (yet).
|
||||
@ -323,14 +310,17 @@ pub fn address_space(&self, cx: &impl HasDataLayout) -> AddressSpace {
|
||||
}
|
||||
}
|
||||
|
||||
pub const CTFE_ALLOC_SALT: usize = 0;
|
||||
|
||||
pub(crate) struct AllocMap<'tcx> {
|
||||
/// Maps `AllocId`s to their corresponding allocations.
|
||||
alloc_map: FxHashMap<AllocId, GlobalAlloc<'tcx>>,
|
||||
|
||||
/// Used to ensure that statics and functions only get one associated `AllocId`.
|
||||
//
|
||||
// FIXME: Should we just have two separate dedup maps for statics and functions each?
|
||||
dedup: FxHashMap<GlobalAlloc<'tcx>, AllocId>,
|
||||
/// Used to deduplicate global allocations: functions, vtables, string literals, ...
|
||||
///
|
||||
/// The `usize` is a "salt" used by Miri to make deduplication imperfect, thus better emulating
|
||||
/// the actual guarantees.
|
||||
dedup: FxHashMap<(GlobalAlloc<'tcx>, usize), AllocId>,
|
||||
|
||||
/// The `AllocId` to assign to the next requested ID.
|
||||
/// Always incremented; never gets smaller.
|
||||
@ -368,74 +358,40 @@ pub fn reserve_alloc_id(self) -> AllocId {
|
||||
|
||||
/// Reserves a new ID *if* this allocation has not been dedup-reserved before.
|
||||
/// Should not be used for mutable memory.
|
||||
fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId {
|
||||
fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>, salt: usize) -> AllocId {
|
||||
let mut alloc_map = self.alloc_map.lock();
|
||||
if let GlobalAlloc::Memory(mem) = alloc {
|
||||
if mem.inner().mutability.is_mut() {
|
||||
bug!("trying to dedup-reserve mutable memory");
|
||||
}
|
||||
}
|
||||
if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) {
|
||||
let alloc_salt = (alloc, salt);
|
||||
if let Some(&alloc_id) = alloc_map.dedup.get(&alloc_salt) {
|
||||
return alloc_id;
|
||||
}
|
||||
let id = alloc_map.reserve();
|
||||
debug!("creating alloc {alloc:?} with id {id:?}");
|
||||
alloc_map.alloc_map.insert(id, alloc.clone());
|
||||
alloc_map.dedup.insert(alloc, id);
|
||||
debug!("creating alloc {:?} with id {id:?}", alloc_salt.0);
|
||||
alloc_map.alloc_map.insert(id, alloc_salt.0.clone());
|
||||
alloc_map.dedup.insert(alloc_salt, id);
|
||||
id
|
||||
}
|
||||
|
||||
/// Generates an `AllocId` for a memory allocation. If the exact same memory has been
|
||||
/// allocated before, this will return the same `AllocId`.
|
||||
pub fn reserve_and_set_memory_dedup(self, mem: ConstAllocation<'tcx>) -> AllocId {
|
||||
self.reserve_and_set_dedup(GlobalAlloc::Memory(mem))
|
||||
pub fn reserve_and_set_memory_dedup(self, mem: ConstAllocation<'tcx>, salt: usize) -> AllocId {
|
||||
self.reserve_and_set_dedup(GlobalAlloc::Memory(mem), salt)
|
||||
}
|
||||
|
||||
/// Generates an `AllocId` for a static or return a cached one in case this function has been
|
||||
/// called on the same static before.
|
||||
pub fn reserve_and_set_static_alloc(self, static_id: DefId) -> AllocId {
|
||||
self.reserve_and_set_dedup(GlobalAlloc::Static(static_id))
|
||||
let salt = 0; // Statics have a guaranteed unique address, no salt added.
|
||||
self.reserve_and_set_dedup(GlobalAlloc::Static(static_id), salt)
|
||||
}
|
||||
|
||||
/// Generates an `AllocId` for a function. The caller must already have decided whether this
|
||||
/// function obtains a unique AllocId or gets de-duplicated via the cache.
|
||||
fn reserve_and_set_fn_alloc_internal(self, instance: Instance<'tcx>, unique: bool) -> AllocId {
|
||||
let alloc = GlobalAlloc::Function { instance, unique };
|
||||
if unique {
|
||||
// Deduplicate.
|
||||
self.reserve_and_set_dedup(alloc)
|
||||
} else {
|
||||
// Get a fresh ID.
|
||||
let mut alloc_map = self.alloc_map.lock();
|
||||
let id = alloc_map.reserve();
|
||||
alloc_map.alloc_map.insert(id, alloc);
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates an `AllocId` for a function. Depending on the function type,
|
||||
/// this might get deduplicated or assigned a new ID each time.
|
||||
pub fn reserve_and_set_fn_alloc(self, instance: Instance<'tcx>) -> AllocId {
|
||||
// Functions cannot be identified by pointers, as asm-equal functions can get deduplicated
|
||||
// by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be
|
||||
// duplicated across crates. We thus generate a new `AllocId` for every mention of a
|
||||
// function. This means that `main as fn() == main as fn()` is false, while `let x = main as
|
||||
// fn(); x == x` is true. However, as a quality-of-life feature it can be useful to identify
|
||||
// certain functions uniquely, e.g. for backtraces. So we identify whether codegen will
|
||||
// actually emit duplicate functions. It does that when they have non-lifetime generics, or
|
||||
// when they can be inlined. All other functions are given a unique address.
|
||||
// This is not a stable guarantee! The `inline` attribute is a hint and cannot be relied
|
||||
// upon for anything. But if we don't do this, backtraces look terrible.
|
||||
let is_generic = instance
|
||||
.args
|
||||
.into_iter()
|
||||
.any(|kind| !matches!(kind.unpack(), GenericArgKind::Lifetime(_)));
|
||||
let can_be_inlined = match self.codegen_fn_attrs(instance.def_id()).inline {
|
||||
InlineAttr::Never => false,
|
||||
_ => true,
|
||||
};
|
||||
let unique = !is_generic && !can_be_inlined;
|
||||
self.reserve_and_set_fn_alloc_internal(instance, unique)
|
||||
/// Generates an `AllocId` for a function. Will get deduplicated.
|
||||
pub fn reserve_and_set_fn_alloc(self, instance: Instance<'tcx>, salt: usize) -> AllocId {
|
||||
self.reserve_and_set_dedup(GlobalAlloc::Function { instance }, salt)
|
||||
}
|
||||
|
||||
/// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
|
||||
@ -443,8 +399,9 @@ pub fn reserve_and_set_vtable_alloc(
|
||||
self,
|
||||
ty: Ty<'tcx>,
|
||||
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
|
||||
salt: usize,
|
||||
) -> AllocId {
|
||||
self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, poly_trait_ref))
|
||||
self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, poly_trait_ref), salt)
|
||||
}
|
||||
|
||||
/// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical
|
||||
|
@ -1438,11 +1438,11 @@ pub fn mk_adt_def(
|
||||
|
||||
/// Allocates a read-only byte or string literal for `mir::interpret`.
|
||||
/// Returns the same `AllocId` if called again with the same bytes.
|
||||
pub fn allocate_bytes_dedup(self, bytes: &[u8]) -> interpret::AllocId {
|
||||
pub fn allocate_bytes_dedup(self, bytes: &[u8], salt: usize) -> interpret::AllocId {
|
||||
// Create an allocation that just contains these bytes.
|
||||
let alloc = interpret::Allocation::from_bytes_byte_aligned_immutable(bytes);
|
||||
let alloc = self.mk_const_alloc(alloc);
|
||||
self.reserve_and_set_memory_dedup(alloc)
|
||||
self.reserve_and_set_memory_dedup(alloc, salt)
|
||||
}
|
||||
|
||||
/// Returns a range of the start/end indices specified with the
|
||||
|
@ -3,7 +3,7 @@
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_macros::HashStable;
|
||||
|
||||
use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar};
|
||||
use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar, CTFE_ALLOC_SALT};
|
||||
use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, HashStable)]
|
||||
@ -73,6 +73,11 @@ pub(crate) fn vtable_min_entries<'tcx>(
|
||||
|
||||
/// Retrieves an allocation that represents the contents of a vtable.
|
||||
/// Since this is a query, allocations are cached and not duplicated.
|
||||
///
|
||||
/// This is an "internal" `AllocId` that should never be used as a value in the interpreted program.
|
||||
/// The interpreter should use `AllocId` that refer to a `GlobalAlloc::VTable` instead.
|
||||
/// (This is similar to statics, which also have a similar "internal" `AllocId` storing their
|
||||
/// initial contents.)
|
||||
pub(super) fn vtable_allocation_provider<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
key: (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>),
|
||||
@ -114,7 +119,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
|
||||
VtblEntry::MetadataDropInPlace => {
|
||||
if ty.needs_drop(tcx, ty::ParamEnv::reveal_all()) {
|
||||
let instance = ty::Instance::resolve_drop_in_place(tcx, ty);
|
||||
let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance);
|
||||
let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT);
|
||||
let fn_ptr = Pointer::from(fn_alloc_id);
|
||||
Scalar::from_pointer(fn_ptr, &tcx)
|
||||
} else {
|
||||
@ -127,7 +132,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
|
||||
VtblEntry::Method(instance) => {
|
||||
// Prepare the fn ptr we write into the vtable.
|
||||
let instance = instance.polymorphize(tcx);
|
||||
let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance);
|
||||
let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT);
|
||||
let fn_ptr = Pointer::from(fn_alloc_id);
|
||||
Scalar::from_pointer(fn_ptr, &tcx)
|
||||
}
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_middle::mir::interpret::{Allocation, LitToConstError, LitToConstInput, Scalar};
|
||||
use rustc_middle::mir::interpret::{
|
||||
Allocation, LitToConstError, LitToConstInput, Scalar, CTFE_ALLOC_SALT,
|
||||
};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty::{
|
||||
@ -140,7 +142,7 @@ fn lit_to_mir_constant<'tcx>(
|
||||
ConstValue::Slice { data: allocation, meta: allocation.inner().size().bytes() }
|
||||
}
|
||||
(ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
|
||||
let id = tcx.allocate_bytes_dedup(data);
|
||||
let id = tcx.allocate_bytes_dedup(data, CTFE_ALLOC_SALT);
|
||||
ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx))
|
||||
}
|
||||
(ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) =>
|
||||
|
@ -4,7 +4,7 @@
|
||||
use core::task::{LocalWaker, Waker};
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, should_panic)] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it fails
|
||||
#[cfg_attr(miri, ignore)] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it can fail
|
||||
fn test_waker_will_wake_clone() {
|
||||
struct NoopWaker;
|
||||
|
||||
@ -20,7 +20,7 @@ fn wake(self: Arc<Self>) {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, should_panic)] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it fails
|
||||
#[cfg_attr(miri, ignore)] // `will_wake` doesn't guarantee that this test will work, and indeed on Miri it can fail
|
||||
fn test_local_waker_will_wake_clone() {
|
||||
struct NoopWaker;
|
||||
|
||||
|
@ -810,11 +810,14 @@ fn ptr_metadata() {
|
||||
assert_ne!(address_1, address_2);
|
||||
// Different erased type => different vtable pointer
|
||||
assert_ne!(address_2, address_3);
|
||||
// Same erased type and same trait => same vtable pointer
|
||||
// Same erased type and same trait => same vtable pointer.
|
||||
// This is *not guaranteed*, so we skip it in Miri.
|
||||
if !cfg!(miri) {
|
||||
assert_eq!(address_3, address_4);
|
||||
assert_eq!(address_3, address_5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ptr_metadata_bounds() {
|
||||
|
@ -56,6 +56,7 @@
|
||||
extern crate tracing;
|
||||
|
||||
// The rustc crates we need
|
||||
extern crate rustc_attr;
|
||||
extern crate rustc_apfloat;
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_const_eval;
|
||||
|
@ -11,6 +11,7 @@
|
||||
use rand::Rng;
|
||||
use rand::SeedableRng;
|
||||
|
||||
use rustc_attr::InlineAttr;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
#[allow(unused)]
|
||||
use rustc_data_structures::static_assert_size;
|
||||
@ -23,6 +24,7 @@
|
||||
Instance, Ty, TyCtxt,
|
||||
},
|
||||
};
|
||||
use rustc_session::config::InliningThreshold;
|
||||
use rustc_span::def_id::{CrateNum, DefId};
|
||||
use rustc_span::{Span, SpanData, Symbol};
|
||||
use rustc_target::abi::{Align, Size};
|
||||
@ -47,10 +49,10 @@
|
||||
/// `SIGRTMAX` - `SIGRTMIN` >= 8 (which is the value of `_POSIX_RTSIG_MAX`)
|
||||
pub const SIGRTMAX: i32 = 42;
|
||||
|
||||
/// Each const has multiple addresses, but only this many. Since const allocations are never
|
||||
/// deallocated, choosing a new [`AllocId`] and thus base address for each evaluation would
|
||||
/// produce unbounded memory usage.
|
||||
const ADDRS_PER_CONST: usize = 16;
|
||||
/// Each anonymous global (constant, vtable, function pointer, ...) has multiple addresses, but only
|
||||
/// this many. Since const allocations are never deallocated, choosing a new [`AllocId`] and thus
|
||||
/// base address for each evaluation would produce unbounded memory usage.
|
||||
const ADDRS_PER_ANON_GLOBAL: usize = 32;
|
||||
|
||||
/// Extra data stored with each stack frame
|
||||
pub struct FrameExtra<'tcx> {
|
||||
@ -1372,7 +1374,7 @@ fn init_frame(
|
||||
catch_unwind: None,
|
||||
timing,
|
||||
is_user_relevant: ecx.machine.is_user_relevant(&frame),
|
||||
salt: ecx.machine.rng.borrow_mut().gen::<usize>() % ADDRS_PER_CONST,
|
||||
salt: ecx.machine.rng.borrow_mut().gen::<usize>() % ADDRS_PER_ANON_GLOBAL,
|
||||
};
|
||||
|
||||
Ok(frame.with_extra(extra))
|
||||
@ -1518,4 +1520,45 @@ fn eval_mir_constant<F>(
|
||||
Entry::Occupied(oe) => Ok(oe.get().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_global_alloc_salt(
|
||||
ecx: &InterpCx<'tcx, Self>,
|
||||
instance: Option<ty::Instance<'tcx>>,
|
||||
) -> usize {
|
||||
let unique = if let Some(instance) = instance {
|
||||
// Functions cannot be identified by pointers, as asm-equal functions can get
|
||||
// deduplicated by the linker (we set the "unnamed_addr" attribute for LLVM) and
|
||||
// functions can be duplicated across crates. We thus generate a new `AllocId` for every
|
||||
// mention of a function. This means that `main as fn() == main as fn()` is false, while
|
||||
// `let x = main as fn(); x == x` is true. However, as a quality-of-life feature it can
|
||||
// be useful to identify certain functions uniquely, e.g. for backtraces. So we identify
|
||||
// whether codegen will actually emit duplicate functions. It does that when they have
|
||||
// non-lifetime generics, or when they can be inlined. All other functions are given a
|
||||
// unique address. This is not a stable guarantee! The `inline` attribute is a hint and
|
||||
// cannot be relied upon for anything. But if we don't do this, the
|
||||
// `__rust_begin_short_backtrace`/`__rust_end_short_backtrace` logic breaks and panic
|
||||
// backtraces look terrible.
|
||||
let is_generic = instance
|
||||
.args
|
||||
.into_iter()
|
||||
.any(|kind| !matches!(kind.unpack(), ty::GenericArgKind::Lifetime(_)));
|
||||
let can_be_inlined = matches!(
|
||||
ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold,
|
||||
InliningThreshold::Always
|
||||
) || !matches!(
|
||||
ecx.tcx.codegen_fn_attrs(instance.def_id()).inline,
|
||||
InlineAttr::Never
|
||||
);
|
||||
!is_generic && !can_be_inlined
|
||||
} else {
|
||||
// Non-functions are never unique.
|
||||
false
|
||||
};
|
||||
// Always use the same salt if the allocation is unique.
|
||||
if unique {
|
||||
CTFE_ALLOC_SALT
|
||||
} else {
|
||||
ecx.machine.rng.borrow_mut().gen::<usize>() % ADDRS_PER_ANON_GLOBAL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +141,17 @@ fn foo(mut self) -> String {
|
||||
}
|
||||
*/
|
||||
|
||||
fn vtable_ptr_eq() {
|
||||
use std::{fmt, ptr};
|
||||
|
||||
// We don't always get the same vtable when casting this to a wide pointer.
|
||||
let x = &2;
|
||||
let x_wide = x as &dyn fmt::Display;
|
||||
assert!((0..256).any(|_| !ptr::eq(x as &dyn fmt::Display, x_wide)));
|
||||
}
|
||||
|
||||
fn main() {
|
||||
ref_box_dyn();
|
||||
box_box_trait();
|
||||
vtable_ptr_eq();
|
||||
}
|
||||
|
@ -82,7 +82,8 @@ fn main() {
|
||||
assert!(return_fn_ptr(i) == i);
|
||||
assert!(return_fn_ptr(i) as unsafe fn() -> i32 == i as fn() -> i32 as unsafe fn() -> i32);
|
||||
// Miri gives different addresses to different reifications of a generic function.
|
||||
assert!(return_fn_ptr(f) != f);
|
||||
// at least if we try often enough.
|
||||
assert!((0..256).any(|_| return_fn_ptr(f) != f));
|
||||
// However, if we only turn `f` into a function pointer and use that pointer,
|
||||
// it is equal to itself.
|
||||
let f2 = f as fn() -> i32;
|
||||
|
@ -75,7 +75,8 @@ fn rc_fat_ptr_eq() {
|
||||
let p = Rc::new(1) as Rc<dyn Debug>;
|
||||
let a: *const dyn Debug = &*p;
|
||||
let r = Rc::into_raw(p);
|
||||
assert!(a == r);
|
||||
// Only compare the pointer parts, as the vtable might differ.
|
||||
assert!(a as *const () == r as *const ());
|
||||
drop(unsafe { Rc::from_raw(r) });
|
||||
}
|
||||
|
||||
|
@ -436,17 +436,27 @@ LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_,
|
||||
╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼
|
||||
}
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/raw-bytes.rs:196:62
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/raw-bytes.rs:196:1
|
||||
|
|
||||
LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 8, align: 4) {
|
||||
╾ALLOC_ID╼ 00 00 00 00 │ ╾──╼....
|
||||
}
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/raw-bytes.rs:199:65
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/raw-bytes.rs:199:1
|
||||
|
|
||||
LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using ALLOC32 as vtable pointer but it does not point to a vtable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC27<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 8, align: 4) {
|
||||
╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/raw-bytes.rs:204:1
|
||||
|
@ -436,17 +436,27 @@ LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_,
|
||||
╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼
|
||||
}
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/raw-bytes.rs:196:62
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/raw-bytes.rs:196:1
|
||||
|
|
||||
LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 16, align: 8) {
|
||||
╾ALLOC_ID╼ 00 00 00 00 00 00 00 00 │ ╾──────╼........
|
||||
}
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/raw-bytes.rs:199:65
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/raw-bytes.rs:199:1
|
||||
|
|
||||
LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using ALLOC32 as vtable pointer but it does not point to a vtable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC27<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 16, align: 8) {
|
||||
╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/raw-bytes.rs:204:1
|
||||
|
@ -194,10 +194,10 @@ impl Trait for bool {}
|
||||
//~| expected a boolean
|
||||
|
||||
const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) };
|
||||
//~^ ERROR evaluation of constant value failed
|
||||
//~^ ERROR it is undefined behavior to use this value
|
||||
//~| null pointer
|
||||
const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
|
||||
//~^ ERROR evaluation of constant value failed
|
||||
//~^ ERROR it is undefined behavior to use this value
|
||||
//~| vtable
|
||||
|
||||
// Uninhabited types
|
||||
|
@ -1,20 +1,8 @@
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/ub-incorrect-vtable.rs:19:14
|
||||
|
|
||||
LL | unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using ALLOC8 as vtable pointer but it does not point to a vtable
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/ub-incorrect-vtable.rs:24:14
|
||||
|
|
||||
LL | unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using ALLOC9 as vtable pointer but it does not point to a vtable
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-incorrect-vtable.rs:33:1
|
||||
--> $DIR/ub-incorrect-vtable.rs:18:1
|
||||
|
|
||||
LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC1<imm>, but expected a vtable pointer
|
||||
LL | const INVALID_VTABLE_ALIGNMENT: &dyn Trait =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC1<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 8, align: 4) {
|
||||
@ -22,10 +10,10 @@ LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-incorrect-vtable.rs:38:1
|
||||
--> $DIR/ub-incorrect-vtable.rs:23:1
|
||||
|
|
||||
LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC3<imm>, but expected a vtable pointer
|
||||
LL | const INVALID_VTABLE_SIZE: &dyn Trait =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC3<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 8, align: 4) {
|
||||
@ -33,16 +21,38 @@ LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-incorrect-vtable.rs:44:1
|
||||
--> $DIR/ub-incorrect-vtable.rs:33:1
|
||||
|
|
||||
LL | const INVALID_VTABLE_UB: W<&dyn Trait> =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC5<imm>, but expected a vtable pointer
|
||||
LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC5<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 8, align: 4) {
|
||||
╾ALLOC4<imm>╼ ╾ALLOC5<imm>╼ │ ╾──╼╾──╼
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-incorrect-vtable.rs:38:1
|
||||
|
|
||||
LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC7<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 8, align: 4) {
|
||||
╾ALLOC6<imm>╼ ╾ALLOC7<imm>╼ │ ╾──╼╾──╼
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-incorrect-vtable.rs:44:1
|
||||
|
|
||||
LL | const INVALID_VTABLE_UB: W<&dyn Trait> =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC9<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 8, align: 4) {
|
||||
╾ALLOC8<imm>╼ ╾ALLOC9<imm>╼ │ ╾──╼╾──╼
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-incorrect-vtable.rs:91:1
|
||||
|
|
||||
@ -51,7 +61,7 @@ LL | const G: Wide = unsafe { Transmute { t: FOO }.u };
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 8, align: 4) {
|
||||
╾ALLOC6<imm>╼ ╾ALLOC7╼ │ ╾──╼╾──╼
|
||||
╾ALLOC10<imm>╼ ╾ALLOC11╼ │ ╾──╼╾──╼
|
||||
}
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
@ -1,20 +1,8 @@
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/ub-incorrect-vtable.rs:19:14
|
||||
|
|
||||
LL | unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using ALLOC8 as vtable pointer but it does not point to a vtable
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/ub-incorrect-vtable.rs:24:14
|
||||
|
|
||||
LL | unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using ALLOC9 as vtable pointer but it does not point to a vtable
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-incorrect-vtable.rs:33:1
|
||||
--> $DIR/ub-incorrect-vtable.rs:18:1
|
||||
|
|
||||
LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC1<imm>, but expected a vtable pointer
|
||||
LL | const INVALID_VTABLE_ALIGNMENT: &dyn Trait =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC1<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 16, align: 8) {
|
||||
@ -22,10 +10,10 @@ LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-incorrect-vtable.rs:38:1
|
||||
--> $DIR/ub-incorrect-vtable.rs:23:1
|
||||
|
|
||||
LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC3<imm>, but expected a vtable pointer
|
||||
LL | const INVALID_VTABLE_SIZE: &dyn Trait =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC3<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 16, align: 8) {
|
||||
@ -33,16 +21,38 @@ LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-incorrect-vtable.rs:44:1
|
||||
--> $DIR/ub-incorrect-vtable.rs:33:1
|
||||
|
|
||||
LL | const INVALID_VTABLE_UB: W<&dyn Trait> =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC5<imm>, but expected a vtable pointer
|
||||
LL | const INVALID_VTABLE_ALIGNMENT_UB: W<&dyn Trait> =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC5<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 16, align: 8) {
|
||||
╾ALLOC4<imm>╼ ╾ALLOC5<imm>╼ │ ╾──────╼╾──────╼
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-incorrect-vtable.rs:38:1
|
||||
|
|
||||
LL | const INVALID_VTABLE_SIZE_UB: W<&dyn Trait> =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC7<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 16, align: 8) {
|
||||
╾ALLOC6<imm>╼ ╾ALLOC7<imm>╼ │ ╾──────╼╾──────╼
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-incorrect-vtable.rs:44:1
|
||||
|
|
||||
LL | const INVALID_VTABLE_UB: W<&dyn Trait> =
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC9<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 16, align: 8) {
|
||||
╾ALLOC8<imm>╼ ╾ALLOC9<imm>╼ │ ╾──────╼╾──────╼
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-incorrect-vtable.rs:91:1
|
||||
|
|
||||
@ -51,7 +61,7 @@ LL | const G: Wide = unsafe { Transmute { t: FOO }.u };
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 16, align: 8) {
|
||||
╾ALLOC6<imm>╼ ╾ALLOC7╼ │ ╾──────╼╾──────╼
|
||||
╾ALLOC10<imm>╼ ╾ALLOC11╼ │ ╾──────╼╾──────╼
|
||||
}
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
@ -17,12 +17,12 @@ trait Trait {}
|
||||
|
||||
const INVALID_VTABLE_ALIGNMENT: &dyn Trait =
|
||||
unsafe { std::mem::transmute((&92u8, &[0usize, 1usize, 1000usize])) };
|
||||
//~^ ERROR evaluation of constant value failed
|
||||
//~^^ ERROR it is undefined behavior to use this value
|
||||
//~| vtable
|
||||
|
||||
const INVALID_VTABLE_SIZE: &dyn Trait =
|
||||
unsafe { std::mem::transmute((&92u8, &[1usize, usize::MAX, 1usize])) };
|
||||
//~^ ERROR evaluation of constant value failed
|
||||
//~^^ ERROR it is undefined behavior to use this value
|
||||
//~| vtable
|
||||
|
||||
#[repr(transparent)]
|
||||
|
@ -123,13 +123,13 @@ impl Trait for bool {}
|
||||
//~^ ERROR it is undefined behavior to use this value
|
||||
//~| vtable
|
||||
const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
|
||||
//~^ ERROR evaluation of constant value failed
|
||||
//~^ ERROR it is undefined behavior to use this value
|
||||
//~| vtable
|
||||
const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) };
|
||||
//~^ ERROR evaluation of constant value failed
|
||||
//~^ ERROR it is undefined behavior to use this value
|
||||
//~| vtable
|
||||
const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) };
|
||||
//~^ ERROR evaluation of constant value failed
|
||||
//~^ ERROR it is undefined behavior to use this value
|
||||
//~| vtable
|
||||
const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) };
|
||||
//~^ ERROR it is undefined behavior to use this value
|
||||
@ -142,10 +142,10 @@ impl Trait for bool {}
|
||||
|
||||
// # raw trait object
|
||||
const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) };
|
||||
//~^ ERROR evaluation of constant value failed
|
||||
//~^ ERROR it is undefined behavior to use this value
|
||||
//~| null pointer
|
||||
const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
|
||||
//~^ ERROR evaluation of constant value failed
|
||||
//~^ ERROR it is undefined behavior to use this value
|
||||
//~| vtable
|
||||
const RAW_TRAIT_OBJ_CONTENT_INVALID: *const dyn Trait = unsafe { mem::transmute::<_, &bool>(&3u8) } as *const dyn Trait; // ok because raw
|
||||
// Officially blessed way to get the vtable
|
||||
@ -154,12 +154,12 @@ impl Trait for bool {}
|
||||
|
||||
static mut RAW_TRAIT_OBJ_VTABLE_NULL_THROUGH_REF: *const dyn Trait = unsafe {
|
||||
mem::transmute::<_, &dyn Trait>((&92u8, 0usize))
|
||||
//~^ ERROR could not evaluate static initializer
|
||||
//~^^ ERROR it is undefined behavior to use this value
|
||||
//~| null pointer
|
||||
};
|
||||
static mut RAW_TRAIT_OBJ_VTABLE_INVALID_THROUGH_REF: *const dyn Trait = unsafe {
|
||||
mem::transmute::<_, &dyn Trait>((&92u8, &3u64))
|
||||
//~^ ERROR could not evaluate static initializer
|
||||
//~^^ ERROR it is undefined behavior to use this value
|
||||
//~| vtable
|
||||
};
|
||||
|
||||
|
@ -218,29 +218,44 @@ LL | const TRAIT_OBJ_INT_VTABLE: W<&dyn Trait> = unsafe { mem::transmute(W((&92u
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/ub-wide-ptr.rs:125:57
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-wide-ptr.rs:125:1
|
||||
|
|
||||
LL | const TRAIT_OBJ_UNALIGNED_VTABLE: &dyn Trait = unsafe { mem::transmute((&92u8, &[0u8; 128])) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using ALLOC20 as vtable pointer but it does not point to a vtable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC17<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/ub-wide-ptr.rs:128:57
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-wide-ptr.rs:128:1
|
||||
|
|
||||
LL | const TRAIT_OBJ_BAD_DROP_FN_NULL: &dyn Trait = unsafe { mem::transmute((&92u8, &[0usize; 8])) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using ALLOC21 as vtable pointer but it does not point to a vtable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC19<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/ub-wide-ptr.rs:131:56
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-wide-ptr.rs:131:1
|
||||
|
|
||||
LL | const TRAIT_OBJ_BAD_DROP_FN_INT: &dyn Trait = unsafe { mem::transmute((&92u8, &[1usize; 8])) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using ALLOC22 as vtable pointer but it does not point to a vtable
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC21<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-wide-ptr.rs:134:1
|
||||
|
|
||||
LL | const TRAIT_OBJ_BAD_DROP_FN_NOT_FN_PTR: W<&dyn Trait> = unsafe { mem::transmute(W((&92u8, &[&42u8; 8]))) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC17<imm>, but expected a vtable pointer
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .0: encountered ALLOC23<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
@ -258,29 +273,49 @@ LL | const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = unsafe { mem::transmute::<_,
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/ub-wide-ptr.rs:144:62
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-wide-ptr.rs:144:1
|
||||
|
|
||||
LL | const RAW_TRAIT_OBJ_VTABLE_NULL: *const dyn Trait = unsafe { mem::transmute((&92u8, 0usize)) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/ub-wide-ptr.rs:147:65
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-wide-ptr.rs:147:1
|
||||
|
|
||||
LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transmute((&92u8, &3u64)) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using ALLOC23 as vtable pointer but it does not point to a vtable
|
||||
|
||||
error[E0080]: could not evaluate static initializer
|
||||
--> $DIR/ub-wide-ptr.rs:156:5
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC28<imm>, but expected a vtable pointer
|
||||
|
|
||||
LL | mem::transmute::<_, &dyn Trait>((&92u8, 0usize))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: expected a pointer to some allocation, but got a null pointer
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: could not evaluate static initializer
|
||||
--> $DIR/ub-wide-ptr.rs:161:5
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-wide-ptr.rs:155:1
|
||||
|
|
||||
LL | mem::transmute::<_, &dyn Trait>((&92u8, &3u64))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using ALLOC24 as vtable pointer but it does not point to a vtable
|
||||
LL | static mut RAW_TRAIT_OBJ_VTABLE_NULL_THROUGH_REF: *const dyn Trait = unsafe {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-wide-ptr.rs:160:1
|
||||
|
|
||||
LL | static mut RAW_TRAIT_OBJ_VTABLE_INVALID_THROUGH_REF: *const dyn Trait = unsafe {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered ALLOC31<imm>, but expected a vtable pointer
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
HEX_DUMP
|
||||
}
|
||||
|
||||
error: aborting due to 29 previous errors
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user