fix handling of unsized types in validation; validate str to be UTF-8

This commit is contained in:
Ralf Jung 2018-08-25 14:36:24 +02:00
parent 89cfd08b47
commit c898e1911d
12 changed files with 401 additions and 317 deletions

View file

@ -29,7 +29,7 @@ use rustc::mir::interpret::{
Scalar, AllocId, Allocation, ConstValue, AllocType,
};
use interpret::{self,
Place, PlaceExtra, PlaceTy, MemPlace, OpTy, Operand, Value,
Place, PlaceTy, MemPlace, OpTy, Operand, Value,
EvalContext, StackPopCleanup, MemoryKind, Memory,
};
@ -103,7 +103,7 @@ pub fn op_to_const<'tcx>(
let val = match normalized_op {
Err(MemPlace { ptr, align, extra }) => {
// extract alloc-offset pair
assert_eq!(extra, PlaceExtra::None);
assert!(extra.is_none());
let ptr = ptr.to_ptr()?;
let alloc = ecx.memory.get(ptr.alloc_id)?;
assert!(alloc.align.abi() >= align.abi());

View file

@ -34,7 +34,7 @@ use rustc::mir::interpret::{
use syntax::source_map::{self, Span};
use super::{
Value, Operand, MemPlace, MPlaceTy, Place, PlaceExtra,
Value, Operand, MemPlace, MPlaceTy, Place,
Memory, Machine
};
@ -462,89 +462,93 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M
}
/// Return the actual dynamic size and alignment of the place at the given type.
/// Note that the value does not matter if the type is sized. For unsized types,
/// the value has to be a fat pointer, and we only care about the "extra" data in it.
/// Only the "extra" (metadata) part of the place matters.
pub(super) fn size_and_align_of(
&self,
metadata: Option<Scalar>,
layout: TyLayout<'tcx>,
) -> EvalResult<'tcx, (Size, Align)> {
let metadata = match metadata {
None => {
assert!(!layout.is_unsized());
return Ok(layout.size_and_align())
}
Some(metadata) => {
assert!(layout.is_unsized());
metadata
}
};
match layout.ty.sty {
ty::Adt(..) | ty::Tuple(..) => {
// First get the size of all statically known fields.
// Don't use type_of::sizing_type_of because that expects t to be sized,
// and it also rounds up to alignment, which we want to avoid,
// as the unsized field's alignment could be smaller.
assert!(!layout.ty.is_simd());
debug!("DST layout: {:?}", layout);
let sized_size = layout.fields.offset(layout.fields.count() - 1);
let sized_align = layout.align;
debug!(
"DST {} statically sized prefix size: {:?} align: {:?}",
layout.ty,
sized_size,
sized_align
);
// Recurse to get the size of the dynamically sized field (must be
// the last field).
let field = layout.field(self, layout.fields.count() - 1)?;
let (unsized_size, unsized_align) = self.size_and_align_of(Some(metadata), field)?;
// FIXME (#26403, #27023): We should be adding padding
// to `sized_size` (to accommodate the `unsized_align`
// required of the unsized field that follows) before
// summing it with `sized_size`. (Note that since #26403
// is unfixed, we do not yet add the necessary padding
// here. But this is where the add would go.)
// Return the sum of sizes and max of aligns.
let size = sized_size + unsized_size;
// Choose max of two known alignments (combined value must
// be aligned according to more restrictive of the two).
let align = sized_align.max(unsized_align);
// Issue #27023: must add any necessary padding to `size`
// (to make it a multiple of `align`) before returning it.
//
// Namely, the returned size should be, in C notation:
//
// `size + ((size & (align-1)) ? align : 0)`
//
// emulated via the semi-standard fast bit trick:
//
// `(size + (align-1)) & -align`
Ok((size.abi_align(align), align))
}
ty::Dynamic(..) => {
let vtable = metadata.to_ptr()?;
// the second entry in the vtable is the dynamic size of the object.
self.read_size_and_align_from_vtable(vtable)
}
ty::Slice(_) | ty::Str => {
let len = metadata.to_usize(self)?;
let (elem_size, align) = layout.field(self, 0)?.size_and_align();
Ok((elem_size * len, align))
}
_ => bug!("size_and_align_of::<{:?}> not supported", layout.ty),
}
}
#[inline]
pub fn size_and_align_of_mplace(
&self,
mplace: MPlaceTy<'tcx>,
mplace: MPlaceTy<'tcx>
) -> EvalResult<'tcx, (Size, Align)> {
if let PlaceExtra::None = mplace.extra {
assert!(!mplace.layout.is_unsized());
Ok(mplace.layout.size_and_align())
} else {
let layout = mplace.layout;
assert!(layout.is_unsized());
match layout.ty.sty {
ty::Adt(..) | ty::Tuple(..) => {
// First get the size of all statically known fields.
// Don't use type_of::sizing_type_of because that expects t to be sized,
// and it also rounds up to alignment, which we want to avoid,
// as the unsized field's alignment could be smaller.
assert!(!layout.ty.is_simd());
debug!("DST layout: {:?}", layout);
let sized_size = layout.fields.offset(layout.fields.count() - 1);
let sized_align = layout.align;
debug!(
"DST {} statically sized prefix size: {:?} align: {:?}",
layout.ty,
sized_size,
sized_align
);
// Recurse to get the size of the dynamically sized field (must be
// the last field).
let field = self.mplace_field(mplace, layout.fields.count() as u64 - 1)?;
let (unsized_size, unsized_align) = self.size_and_align_of_mplace(field)?;
// FIXME (#26403, #27023): We should be adding padding
// to `sized_size` (to accommodate the `unsized_align`
// required of the unsized field that follows) before
// summing it with `sized_size`. (Note that since #26403
// is unfixed, we do not yet add the necessary padding
// here. But this is where the add would go.)
// Return the sum of sizes and max of aligns.
let size = sized_size + unsized_size;
// Choose max of two known alignments (combined value must
// be aligned according to more restrictive of the two).
let align = sized_align.max(unsized_align);
// Issue #27023: must add any necessary padding to `size`
// (to make it a multiple of `align`) before returning it.
//
// Namely, the returned size should be, in C notation:
//
// `size + ((size & (align-1)) ? align : 0)`
//
// emulated via the semi-standard fast bit trick:
//
// `(size + (align-1)) & -align`
Ok((size.abi_align(align), align))
}
ty::Dynamic(..) => {
let vtable = match mplace.extra {
PlaceExtra::Vtable(vtable) => vtable,
_ => bug!("Expected vtable"),
};
// the second entry in the vtable is the dynamic size of the object.
self.read_size_and_align_from_vtable(vtable)
}
ty::Slice(_) | ty::Str => {
let len = match mplace.extra {
PlaceExtra::Length(len) => len,
_ => bug!("Expected length"),
};
let (elem_size, align) = layout.field(self, 0)?.size_and_align();
Ok((elem_size * len, align))
}
_ => bug!("size_of_val::<{:?}> not supported", layout.ty),
}
}
self.size_and_align_of(mplace.extra, mplace.layout)
}
pub fn push_stack_frame(

View file

@ -142,8 +142,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
self.mplace_field(place, 3)?,
);
let msg = Symbol::intern(self.read_str(msg.into())?);
let file = Symbol::intern(self.read_str(file.into())?);
let msg_place = self.ref_to_mplace(self.read_value(msg.into())?)?;
let msg = Symbol::intern(self.read_str(msg_place)?);
let file_place = self.ref_to_mplace(self.read_value(file.into())?)?;
let file = Symbol::intern(self.read_str(file_place)?);
let line = self.read_scalar(line.into())?.to_u32()?;
let col = self.read_scalar(col.into())?.to_u32()?;
return Err(EvalErrorKind::Panic { msg, file, line, col }.into());
@ -159,8 +161,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
self.mplace_field(place, 2)?,
);
let msg = Symbol::intern(self.read_str(msg)?);
let file = Symbol::intern(self.read_str(file.into())?);
let msg_place = self.ref_to_mplace(self.read_value(msg.into())?)?;
let msg = Symbol::intern(self.read_str(msg_place)?);
let file_place = self.ref_to_mplace(self.read_value(file.into())?)?;
let file = Symbol::intern(self.read_str(file_place)?);
let line = self.read_scalar(line.into())?.to_u32()?;
let col = self.read_scalar(col.into())?.to_u32()?;
return Err(EvalErrorKind::Panic { msg, file, line, col }.into());

View file

@ -27,7 +27,7 @@ pub use self::eval_context::{
EvalContext, Frame, StackPopCleanup, LocalValue,
};
pub use self::place::{Place, PlaceExtra, PlaceTy, MemPlace, MPlaceTy};
pub use self::place::{Place, PlaceTy, MemPlace, MPlaceTy};
pub use self::memory::{Memory, MemoryKind};

View file

@ -21,7 +21,7 @@ use rustc_data_structures::indexed_vec::Idx;
use rustc::mir::interpret::{
GlobalId, ConstValue, Scalar, EvalResult, Pointer, ScalarMaybeUndef, EvalErrorKind
};
use super::{EvalContext, Machine, MemPlace, MPlaceTy, PlaceExtra, MemoryKind};
use super::{EvalContext, Machine, MemPlace, MPlaceTy, MemoryKind};
/// A `Value` represents a single immediate self-contained Rust value.
///
@ -65,6 +65,14 @@ impl<'tcx> Value {
self.to_scalar_or_undef().not_undef()
}
#[inline]
pub fn to_scalar_pair(self) -> EvalResult<'tcx, (Scalar, Scalar)> {
match self {
Value::Scalar(..) => bug!("Got a thin pointer where a scalar pair was expected"),
Value::ScalarPair(a, b) => Ok((a.not_undef()?, b.not_undef()?))
}
}
/// Convert the value into a pointer (or a pointer-sized integer).
/// Throws away the second half of a ScalarPair!
#[inline]
@ -74,24 +82,6 @@ impl<'tcx> Value {
Value::ScalarPair(ptr, _) => ptr.not_undef(),
}
}
pub fn to_scalar_dyn_trait(self) -> EvalResult<'tcx, (Scalar, Pointer)> {
match self {
Value::ScalarPair(ptr, vtable) =>
Ok((ptr.not_undef()?, vtable.to_ptr()?)),
_ => bug!("expected ptr and vtable, got {:?}", self),
}
}
pub fn to_scalar_slice(self, cx: impl HasDataLayout) -> EvalResult<'tcx, (Scalar, u64)> {
match self {
Value::ScalarPair(ptr, val) => {
let len = val.to_bits(cx.data_layout().pointer_size)?;
Ok((ptr.not_undef()?, len as u64))
}
_ => bug!("expected ptr and length, got {:?}", self),
}
}
}
// ScalarPair needs a type to interpret, so we often have a value and a type together
@ -242,7 +232,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
&self,
mplace: MPlaceTy<'tcx>,
) -> EvalResult<'tcx, Option<Value>> {
if mplace.extra != PlaceExtra::None {
debug_assert_eq!(mplace.extra.is_some(), mplace.layout.is_unsized());
if mplace.extra.is_some() {
// Dont touch unsized
return Ok(None);
}
let (ptr, ptr_align) = mplace.to_scalar_ptr_align();
@ -315,20 +307,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
}
}
// operand must be a &str or compatible layout
// Turn the MPlace into a string (must already be dereferenced!)
pub fn read_str(
&self,
op: OpTy<'tcx>,
mplace: MPlaceTy<'tcx>,
) -> EvalResult<'tcx, &str> {
let val = self.read_value(op)?;
if let Value::ScalarPair(ptr, len) = *val {
let len = len.not_undef()?.to_bits(self.memory.pointer_size())?;
let bytes = self.memory.read_bytes(ptr.not_undef()?, Size::from_bytes(len as u64))?;
let str = ::std::str::from_utf8(bytes).map_err(|err| EvalErrorKind::ValidationFailure(err.to_string()))?;
Ok(str)
} else {
bug!("read_str: not a str")
}
let len = mplace.len(self)?;
let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len as u64))?;
let str = ::std::str::from_utf8(bytes)
.map_err(|err| EvalErrorKind::ValidationFailure(err.to_string()))?;
Ok(str)
}
pub fn uninit_operand(&mut self, layout: TyLayout<'tcx>) -> EvalResult<'tcx, Operand> {

View file

@ -31,7 +31,8 @@ pub struct MemPlace {
/// However, it may never be undef.
pub ptr: Scalar,
pub align: Align,
pub extra: PlaceExtra,
/// Metadata for unsized places. Interpretation is up to the type.
pub extra: Option<Scalar>,
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
@ -47,14 +48,6 @@ pub enum Place {
},
}
// Extra information for fat pointers / places
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum PlaceExtra {
None,
Length(u64),
Vtable(Pointer),
}
#[derive(Copy, Clone, Debug)]
pub struct PlaceTy<'tcx> {
place: Place,
@ -100,7 +93,7 @@ impl MemPlace {
MemPlace {
ptr,
align,
extra: PlaceExtra::None,
extra: None,
}
}
@ -111,7 +104,7 @@ impl MemPlace {
#[inline(always)]
pub fn to_scalar_ptr_align(self) -> (Scalar, Align) {
assert_eq!(self.extra, PlaceExtra::None);
assert_eq!(self.extra, None);
(self.ptr, self.align)
}
@ -126,13 +119,12 @@ impl MemPlace {
/// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
/// This is the inverse of `ref_to_mplace`.
pub fn to_ref(self, cx: impl HasDataLayout) -> Value {
pub fn to_ref(self) -> Value {
// We ignore the alignment of the place here -- special handling for packed structs ends
// at the `&` operator.
match self.extra {
PlaceExtra::None => Value::Scalar(self.ptr.into()),
PlaceExtra::Length(len) => Value::new_slice(self.ptr.into(), len, cx),
PlaceExtra::Vtable(vtable) => Value::new_dyn_trait(self.ptr.into(), vtable),
None => Value::Scalar(self.ptr.into()),
Some(extra) => Value::ScalarPair(self.ptr.into(), extra.into()),
}
}
}
@ -144,16 +136,28 @@ impl<'tcx> MPlaceTy<'tcx> {
}
#[inline]
pub(super) fn len(self) -> u64 {
// Sanity check
let ty_len = match self.layout.fields {
layout::FieldPlacement::Array { count, .. } => count,
_ => bug!("Length for non-array layout {:?} requested", self.layout),
};
if let PlaceExtra::Length(len) = self.extra {
len
} else {
ty_len
pub(super) fn len(self, cx: impl HasDataLayout) -> EvalResult<'tcx, u64> {
match self.layout.ty.sty {
ty::Array(..) => {
// Sized, get length from layout.
debug_assert!(self.extra.is_none());
match self.layout.fields {
layout::FieldPlacement::Array { count, .. } => Ok(count),
_ => bug!("Length for non-array layout {:?} requested", self.layout),
}
}
ty::Slice(..) | ty::Str => {
self.extra.unwrap().to_usize(cx)
}
_ => bug!("len not supported on type {:?}", self.layout.ty),
}
}
#[inline]
pub(super) fn vtable(self) -> EvalResult<'tcx, Pointer> {
match self.layout.ty.sty {
ty::Dynamic(..) => self.extra.unwrap().to_ptr(),
_ => bug!("vtable not supported on type {:?}", self.layout.ty),
}
}
}
@ -231,33 +235,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
let pointee_type = val.layout.ty.builtin_deref(true).unwrap().ty;
let layout = self.layout_of(pointee_type)?;
let mplace = match self.tcx.struct_tail(pointee_type).sty {
// Matching on the type is okay here, because we used `struct_tail` to get to
// the "core" of what makes this unsized.
ty::Dynamic(..) => {
let (ptr, vtable) = val.to_scalar_dyn_trait()?;
MemPlace {
ptr,
align: layout.align,
extra: PlaceExtra::Vtable(vtable),
}
}
ty::Str | ty::Slice(_) => {
let (ptr, len) = val.to_scalar_slice(self)?;
MemPlace {
ptr,
align: layout.align,
extra: PlaceExtra::Length(len),
}
}
_ => {
assert!(!layout.is_unsized(), "Unhandled unsized type {:?}", pointee_type);
MemPlace {
ptr: val.to_scalar()?,
align: layout.align,
extra: PlaceExtra::None,
}
}
let mplace = if layout.is_unsized() {
let (ptr, extra) = val.to_scalar_pair()?;
MemPlace { ptr, align: layout.align, extra: Some(extra) }
} else {
MemPlace { ptr: val.to_scalar()?, align: layout.align, extra: None }
};
Ok(MPlaceTy { mplace, layout })
}
@ -276,9 +258,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
layout::FieldPlacement::Arbitrary { ref offsets, .. } =>
offsets[usize::try_from(field).unwrap()],
layout::FieldPlacement::Array { stride, .. } => {
let len = base.len();
assert!(field < len,
"Tried to access element {} of array/slice with length {}", field, len);
let len = base.len(self)?;
assert!(field < len, "Tried to access element {} of array/slice with length {}",
field, len);
stride * field
}
layout::FieldPlacement::Union(count) => {
@ -292,28 +274,20 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
// above). In that case, all fields are equal.
let field_layout = base.layout.field(self, usize::try_from(field).unwrap_or(0))?;
// Adjust offset
let offset = match base.extra {
PlaceExtra::Vtable(vtable) => {
let (_, align) = self.read_size_and_align_from_vtable(vtable)?;
// FIXME: Is this right? Should we always do this, or only when actually
// accessing the field to which the vtable applies?
offset.abi_align(align)
}
_ => {
// No adjustment needed
offset
}
// Offset may need adjustment for unsized fields
let (extra, offset) = if field_layout.is_unsized() {
// re-use parent metadata to determine dynamic field layout
let (_, align) = self.size_and_align_of(base.extra, field_layout)?;
(base.extra, offset.abi_align(align))
} else {
// base.extra could be present; we might be accessing a sized field of an unsized
// struct.
(None, offset)
};
let ptr = base.ptr.ptr_offset(offset, self)?;
let align = base.align.min(field_layout.align);
let extra = if !field_layout.is_unsized() {
PlaceExtra::None
} else {
assert!(base.extra != PlaceExtra::None, "Expected fat ptr");
base.extra
};
let align = base.align.min(field_layout.align); // only use static information
Ok(MPlaceTy { mplace: MemPlace { ptr, align, extra }, layout: field_layout })
}
@ -324,7 +298,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
&self,
base: MPlaceTy<'tcx>,
) -> EvalResult<'tcx, impl Iterator<Item=EvalResult<'tcx, MPlaceTy<'tcx>>> + 'a> {
let len = base.len();
let len = base.len(self)?; // also asserts that we have a type where this makes sense
let stride = match base.layout.fields {
layout::FieldPlacement::Array { stride, .. } => stride,
_ => bug!("mplace_array_fields: expected an array layout"),
@ -334,7 +308,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
Ok((0..len).map(move |i| {
let ptr = base.ptr.ptr_offset(i * stride, dl)?;
Ok(MPlaceTy {
mplace: MemPlace { ptr, align: base.align, extra: PlaceExtra::None },
mplace: MemPlace { ptr, align: base.align, extra: None },
layout
})
}))
@ -346,7 +320,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
from: u64,
to: u64,
) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
let len = base.len();
let len = base.len(self)?; // also asserts that we have a type where this makes sense
assert!(from <= len - to);
// Not using layout method because that works with usize, and does not work with slices
@ -364,9 +338,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
// It is not nice to match on the type, but that seems to be the only way to
// implement this.
ty::Array(inner, _) =>
(PlaceExtra::None, self.tcx.mk_array(inner, inner_len)),
ty::Slice(..) =>
(PlaceExtra::Length(inner_len), base.layout.ty),
(None, self.tcx.mk_array(inner, inner_len)),
ty::Slice(..) => {
let len = Scalar::Bits {
bits: inner_len.into(),
size: self.memory.pointer_size().bytes() as u8
};
(Some(len), base.layout.ty)
}
_ =>
bug!("cannot subslice non-array type: `{:?}`", base.layout.ty),
};
@ -384,7 +363,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
variant: usize,
) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
// Downcasts only change the layout
assert_eq!(base.extra, PlaceExtra::None);
assert_eq!(base.extra, None);
Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..base })
}
@ -413,7 +392,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
min_length,
from_end,
} => {
let n = base.len();
let n = base.len(self)?;
assert!(n >= min_length as u64);
let index = if from_end {
@ -773,43 +752,25 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
Ok(OpTy { op, layout: place.layout })
}
/// Turn a place that is a dyn trait (i.e., PlaceExtra::Vtable and the appropriate layout)
/// or a slice into the specific fixed-size place and layout that is given by the vtable/len.
/// This "unpacks" the existential quantifier, so to speak.
pub fn unpack_unsized_mplace(
&self,
mplace: MPlaceTy<'tcx>
) -> EvalResult<'tcx, MPlaceTy<'tcx>> {
trace!("Unpacking {:?} ({:?})", *mplace, mplace.layout.ty);
let layout = match mplace.extra {
PlaceExtra::Vtable(vtable) => {
// the drop function signature
let drop_instance = self.read_drop_type_from_vtable(vtable)?;
trace!("Found drop fn: {:?}", drop_instance);
let fn_sig = drop_instance.ty(*self.tcx).fn_sig(*self.tcx);
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig);
// the drop function takes *mut T where T is the type being dropped, so get that
let ty = fn_sig.inputs()[0].builtin_deref(true).unwrap().ty;
let layout = self.layout_of(ty)?;
// Sanity checks
let (size, align) = self.read_size_and_align_from_vtable(vtable)?;
assert_eq!(size, layout.size);
assert_eq!(align.abi(), layout.align.abi()); // only ABI alignment is preserved
// FIXME: More checks for the vtable? We could make sure it is exactly
// the one one would expect for this type.
// Done!
layout
},
PlaceExtra::Length(len) => {
let ty = self.tcx.mk_array(mplace.layout.field(self, 0)?.ty, len);
self.layout_of(ty)?
}
PlaceExtra::None => bug!("Expected a fat pointer"),
};
trace!("Unpacked type: {:?}", layout.ty);
Ok(MPlaceTy {
mplace: MemPlace { extra: PlaceExtra::None, ..*mplace },
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
/// Also return some more information so drop doesn't have to run the same code twice.
pub(super) fn unpack_dyn_trait(&self, mplace: MPlaceTy<'tcx>)
-> EvalResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx>)> {
let vtable = mplace.vtable()?; // also sanity checks the type
let (instance, ty) = self.read_drop_type_from_vtable(vtable)?;
let layout = self.layout_of(ty)?;
// More sanity checks
let (size, align) = self.read_size_and_align_from_vtable(vtable)?;
assert_eq!(size, layout.size);
assert_eq!(align.abi(), layout.align.abi()); // only ABI alignment is preserved
// FIXME: More checks for the vtable? We could make sure it is exactly
// the one one would expect for this type.
let mplace = MPlaceTy {
mplace: MemPlace { extra: None, ..*mplace },
layout
})
};
Ok((instance, mplace))
}
}

View file

@ -246,7 +246,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
Repeat(ref operand, _) => {
let op = self.eval_operand(operand, None)?;
let dest = self.force_allocation(dest)?;
let length = dest.len();
let length = dest.len(&self)?;
if length > 0 {
// write the first
@ -268,7 +268,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
// FIXME(CTFE): don't allow computing the length of arrays in const eval
let src = self.eval_place(place)?;
let mplace = self.force_allocation(src)?;
let len = mplace.len();
let len = mplace.len(&self)?;
let size = self.memory.pointer_size().bytes() as u8;
self.write_scalar(
Scalar::Bits {
@ -281,7 +281,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
Ref(_, _, ref place) => {
let src = self.eval_place(place)?;
let val = self.force_allocation(src)?.to_ref(&self);
let val = self.force_allocation(src)?.to_ref();
self.write_value(val, dest)?;
}

View file

@ -16,7 +16,7 @@ use rustc_target::spec::abi::Abi;
use rustc::mir::interpret::{EvalResult, Scalar};
use super::{
EvalContext, Machine, Value, OpTy, Place, PlaceTy, PlaceExtra, ValTy, Operand, StackPopCleanup
EvalContext, Machine, Value, OpTy, Place, PlaceTy, ValTy, Operand, StackPopCleanup
};
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
@ -419,7 +419,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
ty::InstanceDef::Virtual(_, idx) => {
let ptr_size = self.memory.pointer_size();
let ptr_align = self.tcx.data_layout.pointer_align;
let (ptr, vtable) = self.read_value(args[0])?.to_scalar_dyn_trait()?;
let ptr = self.ref_to_mplace(self.read_value(args[0])?)?;
let vtable = ptr.vtable()?;
let fn_ptr = self.memory.read_ptr_sized(
vtable.offset(ptr_size * (idx as u64 + 3), &self)?,
ptr_align
@ -433,7 +434,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
let pointee = args[0].layout.ty.builtin_deref(true).unwrap().ty;
let fake_fat_ptr_ty = self.tcx.mk_mut_ptr(pointee);
args[0].layout = self.layout_of(fake_fat_ptr_ty)?.field(&self, 0)?;
args[0].op = Operand::Immediate(Value::Scalar(ptr.into())); // strip vtable
args[0].op = Operand::Immediate(Value::Scalar(ptr.ptr.into())); // strip vtable
trace!("Patched self operand to {:#?}", args[0]);
// recurse with concrete function
self.eval_fn_call(instance, &args, dest, ret, span, sig)
@ -457,19 +458,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
let (instance, place) = match place.layout.ty.sty {
ty::Dynamic(..) => {
// Dropping a trait object.
let vtable = match place.extra {
PlaceExtra::Vtable(vtable) => vtable,
_ => bug!("Expected vtable when dropping {:#?}", place),
};
let place = self.unpack_unsized_mplace(place)?;
let instance = self.read_drop_type_from_vtable(vtable)?;
(instance, place)
self.unpack_dyn_trait(place)?
}
_ => (instance, place),
};
let arg = OpTy {
op: Operand::Immediate(place.to_ref(&self)),
op: Operand::Immediate(place.to_ref()),
layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
};

View file

@ -76,14 +76,21 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
Ok(vtable)
}
/// Return the drop fn instance as well as the actual dynamic type
pub fn read_drop_type_from_vtable(
&self,
vtable: Pointer,
) -> EvalResult<'tcx, ty::Instance<'tcx>> {
) -> EvalResult<'tcx, (ty::Instance<'tcx>, ty::Ty<'tcx>)> {
// we don't care about the pointee type, we just want a pointer
let pointer_align = self.tcx.data_layout.pointer_align;
let drop_fn = self.memory.read_ptr_sized(vtable, pointer_align)?.to_ptr()?;
self.memory.get_fn(drop_fn)
let drop_instance = self.memory.get_fn(drop_fn)?;
trace!("Found drop fn: {:?}", drop_instance);
let fn_sig = drop_instance.ty(*self.tcx).fn_sig(*self.tcx);
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig);
// the drop function takes *mut T where T is the type being dropped, so get that
let ty = fn_sig.inputs()[0].builtin_deref(true).unwrap().ty;
Ok((drop_instance, ty))
}
pub fn read_size_and_align_from_vtable(

View file

@ -187,7 +187,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
}
}
/// This function checks the data at `op`. The operand must be sized.
/// This function checks the data at `op`.
/// It will error if the bits at the destination do not match the ones described by the layout.
/// The `path` may be pushed to, but the part that is present when the function
/// starts must not be changed!
@ -208,14 +208,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
layout::Variants::Tagged { .. } => {
let variant = match self.read_discriminant(dest) {
Ok(res) => res.1,
Err(err) => match err.kind {
EvalErrorKind::InvalidDiscriminant |
EvalErrorKind::ReadPointerAsBytes =>
return validation_failure!(
"invalid enum discriminant", path
),
_ => return Err(err),
}
Err(_) =>
return validation_failure!(
"invalid enum discriminant", path
),
};
let inner_dest = self.operand_downcast(dest, variant)?;
// Put the variant projection onto the path, as a field
@ -227,6 +223,23 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
(variant, inner_dest)
},
layout::Variants::Single { index } => {
// Pre-processing for trait objects: Treat them at their real type.
// (We do not do this for slices and strings: For slices it is not needed,
// `mplace_array_fields` does the right thing, and for strings there is no
// real type that would show the actual length.)
let dest = match dest.layout.ty.sty {
ty::Dynamic(..) => {
let dest = dest.to_mem_place(); // immediate trait objects are not a thing
match self.unpack_dyn_trait(dest) {
Ok(res) => res.1.into(),
Err(_) =>
return validation_failure!(
"invalid vtable in fat pointer", path
),
}
}
_ => dest
};
(index, dest)
}
};
@ -249,7 +262,20 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
// expectation.
layout::Abi::Scalar(ref scalar_layout) => {
let size = scalar_layout.value.size(self);
let value = self.read_value(dest)?;
let value = match self.read_value(dest) {
Ok(val) => val,
Err(err) => match err.kind {
EvalErrorKind::PointerOutOfBounds { .. } |
EvalErrorKind::ReadUndefBytes =>
return validation_failure!(
"uninitialized or out-of-bounds memory", path
),
_ =>
return validation_failure!(
"unrepresentable data", path
),
}
};
let scalar = value.to_scalar_or_undef();
self.validate_scalar(scalar, size, scalar_layout, &path, dest.layout.ty)?;
if scalar_layout.value == Primitive::Pointer {
@ -283,44 +309,88 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
// The fields don't need to correspond to any bit pattern of the union's fields.
// See https://github.com/rust-lang/rust/issues/32836#issuecomment-406875389
},
layout::FieldPlacement::Array { .. } => {
// FIXME: For a TyStr, check that this is valid UTF-8.
// Skips for ZSTs; we could have an empty array as an immediate
if !dest.layout.is_zst() {
let dest = dest.to_mem_place(); // arrays cannot be immediate
for (i, field) in self.mplace_array_fields(dest)?.enumerate() {
let field = field?;
path.push(PathElem::ArrayElem(i));
self.validate_operand(field.into(), path, seen, todo)?;
path.truncate(path_len);
layout::FieldPlacement::Array { .. } if !dest.layout.is_zst() => {
let dest = dest.to_mem_place(); // non-ZST array/slice/str cannot be immediate
// Special handling for strings to verify UTF-8
match dest.layout.ty.sty {
ty::Str => {
match self.read_str(dest) {
Ok(_) => {},
Err(err) => match err.kind {
EvalErrorKind::PointerOutOfBounds { .. } |
EvalErrorKind::ReadUndefBytes =>
// The error here looks slightly different than it does
// for slices, because we do not report the index into the
// str at which we are OOB.
return validation_failure!(
"uninitialized or out-of-bounds memory", path
),
_ =>
return validation_failure!(
"non-UTF-8 data in str", path
),
}
}
}
ty::Array(..) | ty::Slice(..) => {
// This handles the unsized case correctly as well
for (i, field) in self.mplace_array_fields(dest)?.enumerate() {
let field = field?;
path.push(PathElem::ArrayElem(i));
self.validate_operand(field.into(), path, seen, todo)?;
path.truncate(path_len);
}
}
_ => bug!("Array layout for non-array type {:?}", dest.layout.ty),
}
},
layout::FieldPlacement::Array { .. } => {
// An empty array. Nothing to do.
}
layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
// Fat pointers need special treatment.
// Fat pointers are treated like pointers, not aggregates.
if dest.layout.ty.builtin_deref(true).is_some() {
// This is a fat pointer.
let ptr = match self.ref_to_mplace(self.read_value(dest.into())?) {
let ptr = match self.read_value(dest.into())
.and_then(|val| self.ref_to_mplace(val))
{
Ok(ptr) => ptr,
Err(err) => match err.kind {
EvalErrorKind::ReadPointerAsBytes =>
return validation_failure!(
"fat pointer length is not a valid integer", path
),
EvalErrorKind::ReadBytesAsPointer =>
return validation_failure!(
"fat pointer vtable is not a valid pointer", path
),
_ => return Err(err),
}
Err(_) =>
return validation_failure!(
"undefined metadata in fat pointer", path
),
};
let unpacked_ptr = self.unpack_unsized_mplace(ptr)?.into();
// check metadata
match self.tcx.struct_tail(ptr.layout.ty).sty {
ty::Dynamic(..) => {
match ptr.extra.unwrap().to_ptr() {
Ok(_) => {},
Err(_) =>
return validation_failure!(
"non-pointer vtable in fat pointer", path
),
}
}
ty::Slice(..) | ty::Str => {
match ptr.extra.unwrap().to_usize(self) {
Ok(_) => {},
Err(_) =>
return validation_failure!(
"non-integer slice length in fat pointer", path
),
}
}
_ =>
bug!("Unexpected unsized type tail: {:?}",
self.tcx.struct_tail(ptr.layout.ty)
),
}
// for safe ptrs, recursively check it
if !dest.layout.ty.is_unsafe_ptr() {
if seen.insert(unpacked_ptr) {
trace!("Recursing below fat ptr {:?} (unpacked: {:?})",
ptr, unpacked_ptr);
todo.push((unpacked_ptr, path_clone_and_deref(path)));
let ptr = ptr.into();
if seen.insert(ptr) {
trace!("Recursing below fat ptr {:?}", ptr);
todo.push((ptr, path_clone_and_deref(path)));
}
}
} else {

View file

@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(unused)]
// normalize-stderr-test "alignment \d+" -> "alignment N"
// normalize-stderr-test "offset \d+" -> "offset N"
// normalize-stderr-test "allocation \d+" -> "allocation N"
@ -37,7 +39,8 @@ union SliceTransmute {
bad: BadSliceRepr,
slice: &'static [u8],
str: &'static str,
my_str: &'static Str,
my_str: &'static MyStr,
my_slice: &'static MySliceBool,
}
#[repr(C)]
@ -71,7 +74,12 @@ union DynTransmute {
trait Trait {}
impl Trait for bool {}
struct Str(str);
// custom unsized type
struct MyStr(str);
// custom unsized type with sized fields
struct MySlice<T: ?Sized>(bool, T);
type MySliceBool = MySlice<[bool]>;
// OK
const A: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 1 } }.str};
@ -81,8 +89,8 @@ const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 }
// bad str
const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str};
//~^ ERROR this constant likely exhibits undefined behavior
// bad str in Str
const C2: &Str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str};
// bad str in user-defined unsized type
const C2: &MyStr = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str};
//~^ ERROR this constant likely exhibits undefined behavior
// OK
@ -107,10 +115,25 @@ const F: &Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3
// bad data *inside* the trait object
const G: &Trait = &unsafe { BoolTransmute { val: 3 }.bl };
//~^ ERROR this constant likely exhibits undefined behavior
// bad data *inside* the slice
const H: &[bool] = &[unsafe { BoolTransmute { val: 3 }.bl }];
//~^ ERROR this constant likely exhibits undefined behavior
// good MySliceBool
const I1: &MySliceBool = &MySlice(true, [false]);
// bad: sized field is not okay
const I2: &MySliceBool = &MySlice(unsafe { BoolTransmute { val: 3 }.bl }, [false]);
//~^ ERROR this constant likely exhibits undefined behavior
// bad: unsized part is not okay
const I3: &MySliceBool = &MySlice(true, [unsafe { BoolTransmute { val: 3 }.bl }]);
//~^ ERROR this constant likely exhibits undefined behavior
// invalid UTF-8
const J1: &str = unsafe { SliceTransmute { slice: &[0xFF] }.str };
//~^ ERROR this constant likely exhibits undefined behavior
// invalid UTF-8 in user-defined str-like
const J2: &MyStr = unsafe { SliceTransmute { slice: &[0xFF] }.my_str };
//~^ ERROR this constant likely exhibits undefined behavior
fn main() {
}

View file

@ -1,69 +1,69 @@
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:79:1
--> $DIR/union-ub-fat-ptr.rs:87:1
|
LL | const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset N, outside bounds of allocation N which has size N
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or out-of-bounds memory at .<deref>
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:82:1
--> $DIR/union-ub-fat-ptr.rs:90:1
|
LL | const C: &str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.str};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in fat 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 rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:85:1
--> $DIR/union-ub-fat-ptr.rs:93:1
|
LL | const C2: &Str = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer
LL | const C2: &MyStr = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.my_str};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in fat 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 rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:91:1
--> $DIR/union-ub-fat-ptr.rs:99:1
|
LL | const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access at offset N, outside bounds of allocation N which has size N
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or out-of-bounds memory at .<deref>[1]
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:94:1
--> $DIR/union-ub-fat-ptr.rs:102:1
|
LL | const C3: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { ptr: &42, len: &3 } }.slice};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer length is not a valid integer
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-integer slice length in fat 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 rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:98:1
--> $DIR/union-ub-fat-ptr.rs:106:1
|
LL | const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ tried to access memory with alignment N, but alignment N is required
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid vtable in fat pointer at .<deref>
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:101:1
--> $DIR/union-ub-fat-ptr.rs:109:1
|
LL | const E: &Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a memory access tried to interpret some bytes as a pointer
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid vtable in fat pointer at .<deref>
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:104:1
--> $DIR/union-ub-fat-ptr.rs:112:1
|
LL | const F: &Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust};
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered fat pointer vtable is not a valid pointer
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-pointer vtable in fat 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 rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:108:1
--> $DIR/union-ub-fat-ptr.rs:116:1
|
LL | const G: &Trait = &unsafe { BoolTransmute { val: 3 }.bl };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>, but expected something in the range 0..=1
@ -71,13 +71,45 @@ LL | const G: &Trait = &unsafe { BoolTransmute { val: 3 }.bl };
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:112:1
--> $DIR/union-ub-fat-ptr.rs:119:1
|
LL | const H: &[bool] = &[unsafe { BoolTransmute { val: 3 }.bl }];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>[0], but expected something in the range 0..=1
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
error: aborting due to 10 previous errors
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:125:1
|
LL | const I2: &MySliceBool = &MySlice(unsafe { BoolTransmute { val: 3 }.bl }, [false]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>.0, but expected something in the range 0..=1
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:128:1
|
LL | const I3: &MySliceBool = &MySlice(true, [unsafe { BoolTransmute { val: 3 }.bl }]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered 3 at .<deref>.1[0], but expected something in the range 0..=1
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:132:1
|
LL | const J1: &str = unsafe { SliceTransmute { slice: &[0xFF] }.str };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-UTF-8 data in str at .<deref>
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: this constant likely exhibits undefined behavior
--> $DIR/union-ub-fat-ptr.rs:135:1
|
LL | const J2: &MyStr = unsafe { SliceTransmute { slice: &[0xFF] }.my_str };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-UTF-8 data in str at .<deref>.0
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
error: aborting due to 14 previous errors
For more information about this error, try `rustc --explain E0080`.