fix handling of unsized types in validation; validate str to be UTF-8
This commit is contained in:
parent
89cfd08b47
commit
c898e1911d
12 changed files with 401 additions and 317 deletions
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))?,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue