place evaluation: require the original pointer to be aligned if an access happens
This commit is contained in:
parent
ea9a24e32e
commit
f3f9b795bd
22 changed files with 266 additions and 219 deletions
|
@ -681,6 +681,7 @@ impl fmt::Display for AlignFromBytesError {
|
||||||
|
|
||||||
impl Align {
|
impl Align {
|
||||||
pub const ONE: Align = Align { pow2: 0 };
|
pub const ONE: Align = Align { pow2: 0 };
|
||||||
|
// LLVM has a maximal supported alignment of 2^29, we inherit that.
|
||||||
pub const MAX: Align = Align { pow2: 29 };
|
pub const MAX: Align = Align { pow2: 29 };
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
use either::{Left, Right};
|
use either::{Left, Right};
|
||||||
|
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
|
@ -73,9 +75,9 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
||||||
None => InternKind::Constant,
|
None => InternKind::Constant,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ecx.machine.check_alignment = CheckAlignment::No; // interning doesn't need to respect alignment
|
let check_alignment = mem::replace(&mut ecx.machine.check_alignment, CheckAlignment::No); // interning doesn't need to respect alignment
|
||||||
intern_const_alloc_recursive(ecx, intern_kind, &ret)?;
|
intern_const_alloc_recursive(ecx, intern_kind, &ret)?;
|
||||||
// we leave alignment checks off, since this `ecx` will not be used for further evaluation anyway
|
ecx.machine.check_alignment = check_alignment;
|
||||||
|
|
||||||
debug!("eval_body_using_ecx done: {:?}", ret);
|
debug!("eval_body_using_ecx done: {:?}", ret);
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
|
|
|
@ -5,8 +5,9 @@ use rustc_errors::{
|
||||||
use rustc_hir::ConstContext;
|
use rustc_hir::ConstContext;
|
||||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||||
use rustc_middle::mir::interpret::{
|
use rustc_middle::mir::interpret::{
|
||||||
CheckInAllocMsg, ExpectedKind, InterpError, InvalidMetaKind, InvalidProgramInfo, PointerKind,
|
CheckInAllocMsg, ExpectedKind, InterpError, InvalidMetaKind, InvalidProgramInfo, Misalignment,
|
||||||
ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
|
PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo,
|
||||||
|
ValidationErrorInfo,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
@ -567,7 +568,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
||||||
|
|
||||||
builder.set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
|
builder.set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
|
||||||
}
|
}
|
||||||
AlignmentCheckFailed { required, has } => {
|
AlignmentCheckFailed(Misalignment { required, has }) => {
|
||||||
builder.set_arg("required", required.bytes());
|
builder.set_arg("required", required.bytes());
|
||||||
builder.set_arg("has", has.bytes());
|
builder.set_arg("has", has.bytes());
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ use crate::fluent_generated as fluent;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg,
|
alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg,
|
||||||
GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance,
|
GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer,
|
||||||
Scalar,
|
PointerArithmetic, Provenance, Scalar,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
|
@ -372,7 +372,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
self.check_and_deref_ptr(
|
self.check_and_deref_ptr(
|
||||||
ptr,
|
ptr,
|
||||||
size,
|
size,
|
||||||
M::enforce_alignment(self).then_some(align),
|
align,
|
||||||
CheckInAllocMsg::MemoryAccessTest,
|
CheckInAllocMsg::MemoryAccessTest,
|
||||||
|alloc_id, offset, prov| {
|
|alloc_id, offset, prov| {
|
||||||
let (size, align) = self
|
let (size, align) = self
|
||||||
|
@ -382,9 +382,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the given pointer points to live memory of given `size` and `align`
|
/// Check if the given pointer points to live memory of given `size` and `align`.
|
||||||
/// (ignoring `M::enforce_alignment`). The caller can control the error message for the
|
/// The caller can control the error message for the out-of-bounds case.
|
||||||
/// out-of-bounds case.
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn check_ptr_access_align(
|
pub fn check_ptr_access_align(
|
||||||
&self,
|
&self,
|
||||||
|
@ -393,7 +392,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
align: Align,
|
align: Align,
|
||||||
msg: CheckInAllocMsg,
|
msg: CheckInAllocMsg,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
self.check_and_deref_ptr(ptr, size, Some(align), msg, |alloc_id, _, _| {
|
self.check_and_deref_ptr(ptr, size, align, msg, |alloc_id, _, _| {
|
||||||
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
|
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
|
||||||
Ok((size, align, ()))
|
Ok((size, align, ()))
|
||||||
})?;
|
})?;
|
||||||
|
@ -402,15 +401,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
|
|
||||||
/// Low-level helper function to check if a ptr is in-bounds and potentially return a reference
|
/// Low-level helper function to check if a ptr is in-bounds and potentially return a reference
|
||||||
/// to the allocation it points to. Supports both shared and mutable references, as the actual
|
/// to the allocation it points to. Supports both shared and mutable references, as the actual
|
||||||
/// checking is offloaded to a helper closure. `align` defines whether and which alignment check
|
/// checking is offloaded to a helper closure.
|
||||||
/// is done.
|
|
||||||
///
|
///
|
||||||
/// If this returns `None`, the size is 0; it can however return `Some` even for size 0.
|
/// If this returns `None`, the size is 0; it can however return `Some` even for size 0.
|
||||||
fn check_and_deref_ptr<T>(
|
fn check_and_deref_ptr<T>(
|
||||||
&self,
|
&self,
|
||||||
ptr: Pointer<Option<M::Provenance>>,
|
ptr: Pointer<Option<M::Provenance>>,
|
||||||
size: Size,
|
size: Size,
|
||||||
align: Option<Align>,
|
align: Align,
|
||||||
msg: CheckInAllocMsg,
|
msg: CheckInAllocMsg,
|
||||||
alloc_size: impl FnOnce(
|
alloc_size: impl FnOnce(
|
||||||
AllocId,
|
AllocId,
|
||||||
|
@ -426,9 +424,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
throw_ub!(DanglingIntPointer(addr, msg));
|
throw_ub!(DanglingIntPointer(addr, msg));
|
||||||
}
|
}
|
||||||
// Must be aligned.
|
// Must be aligned.
|
||||||
if let Some(align) = align {
|
self.check_misalign(Self::offset_misalignment(addr, align))?;
|
||||||
self.check_offset_align(addr, align)?;
|
|
||||||
}
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Ok((alloc_id, offset, prov)) => {
|
Ok((alloc_id, offset, prov)) => {
|
||||||
|
@ -450,18 +446,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
}
|
}
|
||||||
// Test align. Check this last; if both bounds and alignment are violated
|
// Test align. Check this last; if both bounds and alignment are violated
|
||||||
// we want the error to be about the bounds.
|
// we want the error to be about the bounds.
|
||||||
if let Some(align) = align {
|
self.check_misalign(self.alloc_misalignment(ptr, offset, align, alloc_align))?;
|
||||||
if M::use_addr_for_alignment_check(self) {
|
|
||||||
// `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
|
|
||||||
self.check_offset_align(ptr.addr().bytes(), align)?;
|
|
||||||
} else {
|
|
||||||
// Check allocation alignment and offset alignment.
|
|
||||||
if alloc_align.bytes() < align.bytes() {
|
|
||||||
throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align });
|
|
||||||
}
|
|
||||||
self.check_offset_align(offset.bytes(), align)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can still be zero-sized in this branch, in which case we have to
|
// We can still be zero-sized in this branch, in which case we have to
|
||||||
// return `None`.
|
// return `None`.
|
||||||
|
@ -470,16 +455,59 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_offset_align(&self, offset: u64, align: Align) -> InterpResult<'tcx> {
|
#[inline(always)]
|
||||||
|
pub(super) fn check_misalign(&self, misaligned: Option<Misalignment>) -> InterpResult<'tcx> {
|
||||||
|
if M::enforce_alignment(self) {
|
||||||
|
if let Some(misaligned) = misaligned {
|
||||||
|
throw_ub!(AlignmentCheckFailed(misaligned))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn offset_misalignment(offset: u64, align: Align) -> Option<Misalignment> {
|
||||||
if offset % align.bytes() == 0 {
|
if offset % align.bytes() == 0 {
|
||||||
Ok(())
|
None
|
||||||
} else {
|
} else {
|
||||||
// The biggest power of two through which `offset` is divisible.
|
// The biggest power of two through which `offset` is divisible.
|
||||||
let offset_pow2 = 1 << offset.trailing_zeros();
|
let offset_pow2 = 1 << offset.trailing_zeros();
|
||||||
throw_ub!(AlignmentCheckFailed {
|
Some(Misalignment { has: Align::from_bytes(offset_pow2).unwrap(), required: align })
|
||||||
has: Align::from_bytes(offset_pow2).unwrap(),
|
}
|
||||||
required: align
|
}
|
||||||
});
|
|
||||||
|
#[must_use]
|
||||||
|
fn alloc_misalignment(
|
||||||
|
&self,
|
||||||
|
ptr: Pointer<Option<M::Provenance>>,
|
||||||
|
offset: Size,
|
||||||
|
align: Align,
|
||||||
|
alloc_align: Align,
|
||||||
|
) -> Option<Misalignment> {
|
||||||
|
if M::use_addr_for_alignment_check(self) {
|
||||||
|
// `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
|
||||||
|
Self::offset_misalignment(ptr.addr().bytes(), align)
|
||||||
|
} else {
|
||||||
|
// Check allocation alignment and offset alignment.
|
||||||
|
if alloc_align.bytes() < align.bytes() {
|
||||||
|
Some(Misalignment { has: alloc_align, required: align })
|
||||||
|
} else {
|
||||||
|
Self::offset_misalignment(offset.bytes(), align)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn is_ptr_misaligned(
|
||||||
|
&self,
|
||||||
|
ptr: Pointer<Option<M::Provenance>>,
|
||||||
|
align: Align,
|
||||||
|
) -> Option<Misalignment> {
|
||||||
|
match self.ptr_try_get_alloc_id(ptr) {
|
||||||
|
Err(addr) => Self::offset_misalignment(addr, align),
|
||||||
|
Ok((alloc_id, offset, _prov)) => {
|
||||||
|
let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id);
|
||||||
|
self.alloc_misalignment(ptr, offset, align, alloc_align)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -597,7 +625,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
let ptr_and_alloc = self.check_and_deref_ptr(
|
let ptr_and_alloc = self.check_and_deref_ptr(
|
||||||
ptr,
|
ptr,
|
||||||
size,
|
size,
|
||||||
M::enforce_alignment(self).then_some(align),
|
align,
|
||||||
CheckInAllocMsg::MemoryAccessTest,
|
CheckInAllocMsg::MemoryAccessTest,
|
||||||
|alloc_id, offset, prov| {
|
|alloc_id, offset, prov| {
|
||||||
let alloc = self.get_alloc_raw(alloc_id)?;
|
let alloc = self.get_alloc_raw(alloc_id)?;
|
||||||
|
|
|
@ -10,7 +10,7 @@ use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||||
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
|
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
|
||||||
use rustc_middle::ty::{ConstInt, Ty, TyCtxt};
|
use rustc_middle::ty::{ConstInt, Ty, TyCtxt};
|
||||||
use rustc_middle::{mir, ty};
|
use rustc_middle::{mir, ty};
|
||||||
use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
|
use rustc_target::abi::{self, Abi, HasDataLayout, Size};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, Frame, InterpCx, InterpResult,
|
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, Frame, InterpCx, InterpResult,
|
||||||
|
@ -44,12 +44,16 @@ impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Prov: Provenance> Immediate<Prov> {
|
impl<Prov: Provenance> Immediate<Prov> {
|
||||||
pub fn from_pointer(ptr: Pointer<Prov>, cx: &impl HasDataLayout) -> Self {
|
pub fn new_pointer_with_meta(
|
||||||
Immediate::Scalar(Scalar::from_pointer(ptr, cx))
|
ptr: Pointer<Option<Prov>>,
|
||||||
}
|
meta: MemPlaceMeta<Prov>,
|
||||||
|
cx: &impl HasDataLayout,
|
||||||
pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self {
|
) -> Self {
|
||||||
Immediate::Scalar(Scalar::from_maybe_pointer(ptr, cx))
|
let ptr = Scalar::from_maybe_pointer(ptr, cx);
|
||||||
|
match meta {
|
||||||
|
MemPlaceMeta::None => Immediate::from(ptr),
|
||||||
|
MemPlaceMeta::Meta(meta) => Immediate::ScalarPair(ptr, meta),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_slice(ptr: Pointer<Option<Prov>>, len: u64, cx: &impl HasDataLayout) -> Self {
|
pub fn new_slice(ptr: Pointer<Option<Prov>>, len: u64, cx: &impl HasDataLayout) -> Self {
|
||||||
|
@ -328,14 +332,6 @@ pub(super) enum Operand<Prov: Provenance = AllocId> {
|
||||||
pub struct OpTy<'tcx, Prov: Provenance = AllocId> {
|
pub struct OpTy<'tcx, Prov: Provenance = AllocId> {
|
||||||
op: Operand<Prov>, // Keep this private; it helps enforce invariants.
|
op: Operand<Prov>, // Keep this private; it helps enforce invariants.
|
||||||
pub layout: TyAndLayout<'tcx>,
|
pub layout: TyAndLayout<'tcx>,
|
||||||
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
|
|
||||||
/// it needs to have a different alignment than the field type would usually have.
|
|
||||||
/// So we represent this here with a separate field that "overwrites" `layout.align`.
|
|
||||||
/// This means `layout.align` should never be used for an `OpTy`!
|
|
||||||
/// `None` means "alignment does not matter since this is a by-value operand"
|
|
||||||
/// (`Operand::Immediate`); this field is only relevant for `Operand::Indirect`.
|
|
||||||
/// Also CTFE ignores alignment anyway, so this is for Miri only.
|
|
||||||
pub align: Option<Align>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Prov: Provenance> std::fmt::Debug for OpTy<'_, Prov> {
|
impl<Prov: Provenance> std::fmt::Debug for OpTy<'_, Prov> {
|
||||||
|
@ -351,18 +347,14 @@ impl<Prov: Provenance> std::fmt::Debug for OpTy<'_, Prov> {
|
||||||
impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(val: ImmTy<'tcx, Prov>) -> Self {
|
fn from(val: ImmTy<'tcx, Prov>) -> Self {
|
||||||
OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None }
|
OpTy { op: Operand::Immediate(val.imm), layout: val.layout }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
|
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
|
||||||
OpTy {
|
OpTy { op: Operand::Indirect(*mplace.mplace()), layout: mplace.layout }
|
||||||
op: Operand::Indirect(*mplace.mplace()),
|
|
||||||
layout: mplace.layout,
|
|
||||||
align: Some(mplace.align),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -635,7 +627,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
throw_inval!(ConstPropNonsense);
|
throw_inval!(ConstPropNonsense);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(OpTy { op, layout, align: Some(layout.align.abi) })
|
Ok(OpTy { op, layout })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Every place can be read from, so we can turn them into an operand.
|
/// Every place can be read from, so we can turn them into an operand.
|
||||||
|
@ -650,16 +642,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
Right((frame, local, offset)) => {
|
Right((frame, local, offset)) => {
|
||||||
debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`.
|
debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`.
|
||||||
let base = self.local_to_op(&self.stack()[frame], local, None)?;
|
let base = self.local_to_op(&self.stack()[frame], local, None)?;
|
||||||
let mut field = match offset {
|
Ok(match offset {
|
||||||
Some(offset) => base.offset(offset, place.layout, self)?,
|
Some(offset) => base.offset(offset, place.layout, self)?,
|
||||||
None => {
|
None => {
|
||||||
// In the common case this hasn't been projected.
|
// In the common case this hasn't been projected.
|
||||||
debug_assert_eq!(place.layout, base.layout);
|
debug_assert_eq!(place.layout, base.layout);
|
||||||
base
|
base
|
||||||
}
|
}
|
||||||
};
|
})
|
||||||
field.align = Some(place.align);
|
|
||||||
Ok(field)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -747,27 +737,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
|
let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
|
||||||
let op = match val_val {
|
let imm = match val_val {
|
||||||
mir::ConstValue::Indirect { alloc_id, offset } => {
|
mir::ConstValue::Indirect { alloc_id, offset } => {
|
||||||
// We rely on mutability being set correctly in that allocation to prevent writes
|
// We rely on mutability being set correctly in that allocation to prevent writes
|
||||||
// where none should happen.
|
// where none should happen.
|
||||||
let ptr = self.global_base_pointer(Pointer::new(alloc_id, offset))?;
|
let ptr = self.global_base_pointer(Pointer::new(alloc_id, offset))?;
|
||||||
Operand::Indirect(MemPlace::from_ptr(ptr.into()))
|
return Ok(self.ptr_to_mplace(ptr.into(), layout).into());
|
||||||
}
|
}
|
||||||
mir::ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()),
|
mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(),
|
||||||
mir::ConstValue::ZeroSized => Operand::Immediate(Immediate::Uninit),
|
mir::ConstValue::ZeroSized => Immediate::Uninit,
|
||||||
mir::ConstValue::Slice { data, meta } => {
|
mir::ConstValue::Slice { data, meta } => {
|
||||||
// We rely on mutability being set correctly in `data` to prevent writes
|
// We rely on mutability being set correctly in `data` to prevent writes
|
||||||
// where none should happen.
|
// where none should happen.
|
||||||
let ptr = Pointer::new(self.tcx.reserve_and_set_memory_alloc(data), Size::ZERO);
|
let ptr = Pointer::new(self.tcx.reserve_and_set_memory_alloc(data), Size::ZERO);
|
||||||
Operand::Immediate(Immediate::new_slice(
|
Immediate::new_slice(self.global_base_pointer(ptr)?.into(), meta, self)
|
||||||
self.global_base_pointer(ptr)?.into(),
|
|
||||||
meta,
|
|
||||||
self,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(OpTy { op, layout, align: Some(layout.align.abi) })
|
Ok(OpTy { op: Operand::Immediate(imm), layout })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -780,6 +766,6 @@ mod size_asserts {
|
||||||
static_assert_size!(Immediate, 48);
|
static_assert_size!(Immediate, 48);
|
||||||
static_assert_size!(ImmTy<'_>, 64);
|
static_assert_size!(ImmTy<'_>, 64);
|
||||||
static_assert_size!(Operand, 56);
|
static_assert_size!(Operand, 56);
|
||||||
static_assert_size!(OpTy<'_>, 80);
|
static_assert_size!(OpTy<'_>, 72);
|
||||||
// tidy-alphabetical-end
|
// tidy-alphabetical-end
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use rustc_target::abi::{Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_VARIANT
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, ImmTy, Immediate,
|
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, ImmTy, Immediate,
|
||||||
InterpCx, InterpResult, Machine, MemoryKind, OffsetMode, OpTy, Operand, Pointer,
|
InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer,
|
||||||
PointerArithmetic, Projectable, Provenance, Readable, Scalar,
|
PointerArithmetic, Projectable, Provenance, Readable, Scalar,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,19 +57,11 @@ pub(super) struct MemPlace<Prov: Provenance = AllocId> {
|
||||||
/// Must not be present for sized types, but can be missing for unsized types
|
/// Must not be present for sized types, but can be missing for unsized types
|
||||||
/// (e.g., `extern type`).
|
/// (e.g., `extern type`).
|
||||||
pub meta: MemPlaceMeta<Prov>,
|
pub meta: MemPlaceMeta<Prov>,
|
||||||
|
/// Stores whether this place was created based on a sufficiently aligned pointer.
|
||||||
|
misaligned: Option<Misalignment>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Prov: Provenance> MemPlace<Prov> {
|
impl<Prov: Provenance> MemPlace<Prov> {
|
||||||
#[inline(always)]
|
|
||||||
pub fn from_ptr(ptr: Pointer<Option<Prov>>) -> Self {
|
|
||||||
MemPlace { ptr, meta: MemPlaceMeta::None }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn from_ptr_with_meta(ptr: Pointer<Option<Prov>>, meta: MemPlaceMeta<Prov>) -> Self {
|
|
||||||
MemPlace { ptr, meta }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adjust the provenance of the main pointer (metadata is unaffected).
|
/// Adjust the provenance of the main pointer (metadata is unaffected).
|
||||||
pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self {
|
pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self {
|
||||||
MemPlace { ptr: self.ptr.map_provenance(f), ..self }
|
MemPlace { ptr: self.ptr.map_provenance(f), ..self }
|
||||||
|
@ -78,12 +70,7 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
||||||
/// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
|
/// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Prov> {
|
pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Prov> {
|
||||||
match self.meta {
|
Immediate::new_pointer_with_meta(self.ptr, self.meta, cx)
|
||||||
MemPlaceMeta::None => Immediate::from(Scalar::from_maybe_pointer(self.ptr, cx)),
|
|
||||||
MemPlaceMeta::Meta(meta) => {
|
|
||||||
Immediate::ScalarPair(Scalar::from_maybe_pointer(self.ptr, cx), meta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -108,7 +95,7 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
||||||
}
|
}
|
||||||
OffsetMode::Wrapping => self.ptr.wrapping_offset(offset, ecx),
|
OffsetMode::Wrapping => self.ptr.wrapping_offset(offset, ecx),
|
||||||
};
|
};
|
||||||
Ok(MemPlace { ptr, meta })
|
Ok(MemPlace { ptr, meta, misaligned: self.misaligned })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,11 +104,6 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
||||||
pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
|
pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||||
mplace: MemPlace<Prov>,
|
mplace: MemPlace<Prov>,
|
||||||
pub layout: TyAndLayout<'tcx>,
|
pub layout: TyAndLayout<'tcx>,
|
||||||
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
|
|
||||||
/// it needs to have a different alignment than the field type would usually have.
|
|
||||||
/// So we represent this here with a separate field that "overwrites" `layout.align`.
|
|
||||||
/// This means `layout.align` should never be used for a `MPlaceTy`!
|
|
||||||
pub align: Align,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Prov: Provenance> std::fmt::Debug for MPlaceTy<'_, Prov> {
|
impl<Prov: Provenance> std::fmt::Debug for MPlaceTy<'_, Prov> {
|
||||||
|
@ -143,25 +125,7 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
|
||||||
assert!(layout.is_zst());
|
assert!(layout.is_zst());
|
||||||
let align = layout.align.abi;
|
let align = layout.align.abi;
|
||||||
let ptr = Pointer::from_addr_invalid(align.bytes()); // no provenance, absolute address
|
let ptr = Pointer::from_addr_invalid(align.bytes()); // no provenance, absolute address
|
||||||
MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align }
|
MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None, misaligned: None }, layout }
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn from_aligned_ptr(ptr: Pointer<Option<Prov>>, layout: TyAndLayout<'tcx>) -> Self {
|
|
||||||
MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn from_aligned_ptr_with_meta(
|
|
||||||
ptr: Pointer<Option<Prov>>,
|
|
||||||
layout: TyAndLayout<'tcx>,
|
|
||||||
meta: MemPlaceMeta<Prov>,
|
|
||||||
) -> Self {
|
|
||||||
MPlaceTy {
|
|
||||||
mplace: MemPlace::from_ptr_with_meta(ptr, meta),
|
|
||||||
layout,
|
|
||||||
align: layout.align.abi,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adjust the provenance of the main pointer (metadata is unaffected).
|
/// Adjust the provenance of the main pointer (metadata is unaffected).
|
||||||
|
@ -204,11 +168,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||||
layout: TyAndLayout<'tcx>,
|
layout: TyAndLayout<'tcx>,
|
||||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
) -> InterpResult<'tcx, Self> {
|
) -> InterpResult<'tcx, Self> {
|
||||||
Ok(MPlaceTy {
|
Ok(MPlaceTy { mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?, layout })
|
||||||
mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?,
|
|
||||||
align: self.align.restrict_for_offset(offset),
|
|
||||||
layout,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||||
|
@ -239,11 +199,6 @@ pub(super) enum Place<Prov: Provenance = AllocId> {
|
||||||
pub struct PlaceTy<'tcx, Prov: Provenance = AllocId> {
|
pub struct PlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||||
place: Place<Prov>, // Keep this private; it helps enforce invariants.
|
place: Place<Prov>, // Keep this private; it helps enforce invariants.
|
||||||
pub layout: TyAndLayout<'tcx>,
|
pub layout: TyAndLayout<'tcx>,
|
||||||
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
|
|
||||||
/// it needs to have a different alignment than the field type would usually have.
|
|
||||||
/// So we represent this here with a separate field that "overwrites" `layout.align`.
|
|
||||||
/// This means `layout.align` should never be used for a `PlaceTy`!
|
|
||||||
pub align: Align,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Prov: Provenance> std::fmt::Debug for PlaceTy<'_, Prov> {
|
impl<Prov: Provenance> std::fmt::Debug for PlaceTy<'_, Prov> {
|
||||||
|
@ -259,7 +214,7 @@ impl<Prov: Provenance> std::fmt::Debug for PlaceTy<'_, Prov> {
|
||||||
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
|
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
|
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
|
||||||
PlaceTy { place: Place::Ptr(mplace.mplace), layout: mplace.layout, align: mplace.align }
|
PlaceTy { place: Place::Ptr(mplace.mplace), layout: mplace.layout }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +230,7 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
|
||||||
&self,
|
&self,
|
||||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>)> {
|
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>)> {
|
||||||
match self.place {
|
match self.place {
|
||||||
Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout, align: self.align }),
|
Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout }),
|
||||||
Place::Local { frame, local, offset } => Right((frame, local, offset)),
|
Place::Local { frame, local, offset } => Right((frame, local, offset)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -332,11 +287,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
||||||
.offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?,
|
.offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?,
|
||||||
);
|
);
|
||||||
|
|
||||||
PlaceTy {
|
PlaceTy { place: Place::Local { frame, local, offset: Some(new_offset) }, layout }
|
||||||
place: Place::Local { frame, local, offset: Some(new_offset) },
|
|
||||||
align: self.align.restrict_for_offset(offset),
|
|
||||||
layout,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -354,9 +305,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
pub fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||||
match self.op() {
|
match self.op() {
|
||||||
Operand::Indirect(mplace) => {
|
Operand::Indirect(mplace) => Left(MPlaceTy { mplace: *mplace, layout: self.layout }),
|
||||||
Left(MPlaceTy { mplace: *mplace, layout: self.layout, align: self.align.unwrap() })
|
|
||||||
}
|
|
||||||
Operand::Immediate(imm) => Right(ImmTy::from_immediate(*imm, self.layout)),
|
Operand::Immediate(imm) => Right(ImmTy::from_immediate(*imm, self.layout)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,7 +326,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
||||||
pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
|
pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
|
||||||
fn as_mplace_or_local(
|
fn as_mplace_or_local(
|
||||||
&self,
|
&self,
|
||||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)>;
|
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, TyAndLayout<'tcx>)>;
|
||||||
|
|
||||||
fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -389,10 +338,9 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_mplace_or_local(
|
fn as_mplace_or_local(
|
||||||
&self,
|
&self,
|
||||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)>
|
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, TyAndLayout<'tcx>)> {
|
||||||
{
|
|
||||||
self.as_mplace_or_local()
|
self.as_mplace_or_local()
|
||||||
.map_right(|(frame, local, offset)| (frame, local, offset, self.align, self.layout))
|
.map_right(|(frame, local, offset)| (frame, local, offset, self.layout))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -408,8 +356,7 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_mplace_or_local(
|
fn as_mplace_or_local(
|
||||||
&self,
|
&self,
|
||||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, Align, TyAndLayout<'tcx>)>
|
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, TyAndLayout<'tcx>)> {
|
||||||
{
|
|
||||||
Left(self.clone())
|
Left(self.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,6 +375,25 @@ where
|
||||||
Prov: Provenance,
|
Prov: Provenance,
|
||||||
M: Machine<'mir, 'tcx, Provenance = Prov>,
|
M: Machine<'mir, 'tcx, Provenance = Prov>,
|
||||||
{
|
{
|
||||||
|
pub fn ptr_with_meta_to_mplace(
|
||||||
|
&self,
|
||||||
|
ptr: Pointer<Option<M::Provenance>>,
|
||||||
|
meta: MemPlaceMeta<M::Provenance>,
|
||||||
|
layout: TyAndLayout<'tcx>,
|
||||||
|
) -> MPlaceTy<'tcx, M::Provenance> {
|
||||||
|
let misaligned = self.is_ptr_misaligned(ptr, layout.align.abi);
|
||||||
|
MPlaceTy { mplace: MemPlace { ptr, meta, misaligned }, layout }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ptr_to_mplace(
|
||||||
|
&self,
|
||||||
|
ptr: Pointer<Option<M::Provenance>>,
|
||||||
|
layout: TyAndLayout<'tcx>,
|
||||||
|
) -> MPlaceTy<'tcx, M::Provenance> {
|
||||||
|
assert!(layout.is_sized());
|
||||||
|
self.ptr_with_meta_to_mplace(ptr, MemPlaceMeta::None, layout)
|
||||||
|
}
|
||||||
|
|
||||||
/// Take a value, which represents a (thin or wide) reference, and make it a place.
|
/// Take a value, which represents a (thin or wide) reference, and make it a place.
|
||||||
/// Alignment is just based on the type. This is the inverse of `mplace_to_ref()`.
|
/// Alignment is just based on the type. This is the inverse of `mplace_to_ref()`.
|
||||||
///
|
///
|
||||||
|
@ -449,7 +415,8 @@ where
|
||||||
|
|
||||||
// `ref_to_mplace` is called on raw pointers even if they don't actually get dereferenced;
|
// `ref_to_mplace` is called on raw pointers even if they don't actually get dereferenced;
|
||||||
// we hence can't call `size_and_align_of` since that asserts more validity than we want.
|
// we hence can't call `size_and_align_of` since that asserts more validity than we want.
|
||||||
Ok(MPlaceTy::from_aligned_ptr_with_meta(ptr.to_pointer(self)?, layout, meta))
|
let ptr = ptr.to_pointer(self)?;
|
||||||
|
Ok(self.ptr_with_meta_to_mplace(ptr, meta, layout))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn a mplace into a (thin or wide) mutable raw pointer, pointing to the same space.
|
/// Turn a mplace into a (thin or wide) mutable raw pointer, pointing to the same space.
|
||||||
|
@ -491,8 +458,11 @@ where
|
||||||
let (size, _align) = self
|
let (size, _align) = self
|
||||||
.size_and_align_of_mplace(&mplace)?
|
.size_and_align_of_mplace(&mplace)?
|
||||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||||
// Due to packed places, only `mplace.align` matters.
|
// We check alignment separately, and *after* checking everything else.
|
||||||
self.get_ptr_alloc(mplace.ptr(), size, mplace.align)
|
// If an access is both OOB and misaligned, we want to see the bounds error.
|
||||||
|
let a = self.get_ptr_alloc(mplace.ptr(), size, Align::ONE)?;
|
||||||
|
self.check_misalign(mplace.mplace.misaligned)?;
|
||||||
|
Ok(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -504,8 +474,13 @@ where
|
||||||
let (size, _align) = self
|
let (size, _align) = self
|
||||||
.size_and_align_of_mplace(&mplace)?
|
.size_and_align_of_mplace(&mplace)?
|
||||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||||
// Due to packed places, only `mplace.align` matters.
|
// We check alignment separately, and raise that error *after* checking everything else.
|
||||||
self.get_ptr_alloc_mut(mplace.ptr(), size, mplace.align)
|
// If an access is both OOB and misaligned, we want to see the bounds error.
|
||||||
|
// However we have to call `check_misalign` first to make the borrow checker happy.
|
||||||
|
let misalign_err = self.check_misalign(mplace.mplace.misaligned);
|
||||||
|
let a = self.get_ptr_alloc_mut(mplace.ptr(), size, Align::ONE)?;
|
||||||
|
misalign_err?;
|
||||||
|
Ok(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
|
/// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
|
||||||
|
@ -520,8 +495,8 @@ where
|
||||||
let (len, e_ty) = mplace.layout.ty.simd_size_and_type(*self.tcx);
|
let (len, e_ty) = mplace.layout.ty.simd_size_and_type(*self.tcx);
|
||||||
let array = Ty::new_array(self.tcx.tcx, e_ty, len);
|
let array = Ty::new_array(self.tcx.tcx, e_ty, len);
|
||||||
let layout = self.layout_of(array)?;
|
let layout = self.layout_of(array)?;
|
||||||
assert_eq!(layout.size, mplace.layout.size);
|
let mplace = mplace.transmute(layout, self)?;
|
||||||
Ok((MPlaceTy { layout, ..*mplace }, len))
|
Ok((mplace, len))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
|
/// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
|
||||||
|
@ -557,7 +532,7 @@ where
|
||||||
Operand::Indirect(mplace) => Place::Ptr(*mplace),
|
Operand::Indirect(mplace) => Place::Ptr(*mplace),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(PlaceTy { place, layout, align: layout.align.abi })
|
Ok(PlaceTy { place, layout })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes a place. You should only use this if you intend to write into this
|
/// Computes a place. You should only use this if you intend to write into this
|
||||||
|
@ -647,7 +622,7 @@ where
|
||||||
// See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
|
// See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
|
||||||
// but not factored as a separate function.
|
// but not factored as a separate function.
|
||||||
let mplace = match dest.as_mplace_or_local() {
|
let mplace = match dest.as_mplace_or_local() {
|
||||||
Right((frame, local, offset, align, layout)) => {
|
Right((frame, local, offset, layout)) => {
|
||||||
if offset.is_some() {
|
if offset.is_some() {
|
||||||
// This has been projected to a part of this local. We could have complicated
|
// This has been projected to a part of this local. We could have complicated
|
||||||
// logic to still keep this local as an `Operand`... but it's much easier to
|
// logic to still keep this local as an `Operand`... but it's much easier to
|
||||||
|
@ -688,7 +663,7 @@ where
|
||||||
}
|
}
|
||||||
Operand::Indirect(mplace) => {
|
Operand::Indirect(mplace) => {
|
||||||
// The local is in memory, go on below.
|
// The local is in memory, go on below.
|
||||||
MPlaceTy { mplace: *mplace, align, layout }
|
MPlaceTy { mplace: *mplace, layout }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -697,7 +672,7 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is already in memory, write there.
|
// This is already in memory, write there.
|
||||||
self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.align, mplace.mplace)
|
self.write_immediate_to_mplace_no_validate(src, mplace.layout, mplace.mplace)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write an immediate to memory.
|
/// Write an immediate to memory.
|
||||||
|
@ -707,7 +682,6 @@ where
|
||||||
&mut self,
|
&mut self,
|
||||||
value: Immediate<M::Provenance>,
|
value: Immediate<M::Provenance>,
|
||||||
layout: TyAndLayout<'tcx>,
|
layout: TyAndLayout<'tcx>,
|
||||||
align: Align,
|
|
||||||
dest: MemPlace<M::Provenance>,
|
dest: MemPlace<M::Provenance>,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
// Note that it is really important that the type here is the right one, and matches the
|
// Note that it is really important that the type here is the right one, and matches the
|
||||||
|
@ -716,9 +690,7 @@ where
|
||||||
// wrong type.
|
// wrong type.
|
||||||
|
|
||||||
let tcx = *self.tcx;
|
let tcx = *self.tcx;
|
||||||
let Some(mut alloc) =
|
let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout })? else {
|
||||||
self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout, align })?
|
|
||||||
else {
|
|
||||||
// zero-sized access
|
// zero-sized access
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
@ -736,9 +708,6 @@ where
|
||||||
alloc.write_scalar(alloc_range(Size::ZERO, size), scalar)
|
alloc.write_scalar(alloc_range(Size::ZERO, size), scalar)
|
||||||
}
|
}
|
||||||
Immediate::ScalarPair(a_val, b_val) => {
|
Immediate::ScalarPair(a_val, b_val) => {
|
||||||
// We checked `ptr_align` above, so all fields will have the alignment they need.
|
|
||||||
// We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
|
|
||||||
// which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
|
|
||||||
let Abi::ScalarPair(a, b) = layout.abi else {
|
let Abi::ScalarPair(a, b) = layout.abi else {
|
||||||
span_bug!(
|
span_bug!(
|
||||||
self.cur_span(),
|
self.cur_span(),
|
||||||
|
@ -767,7 +736,7 @@ where
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
let mplace = match dest.as_mplace_or_local() {
|
let mplace = match dest.as_mplace_or_local() {
|
||||||
Left(mplace) => mplace,
|
Left(mplace) => mplace,
|
||||||
Right((frame, local, offset, align, layout)) => {
|
Right((frame, local, offset, layout)) => {
|
||||||
if offset.is_some() {
|
if offset.is_some() {
|
||||||
// This has been projected to a part of this local. We could have complicated
|
// This has been projected to a part of this local. We could have complicated
|
||||||
// logic to still keep this local as an `Operand`... but it's much easier to
|
// logic to still keep this local as an `Operand`... but it's much easier to
|
||||||
|
@ -783,7 +752,7 @@ where
|
||||||
}
|
}
|
||||||
Operand::Indirect(mplace) => {
|
Operand::Indirect(mplace) => {
|
||||||
// The local is in memory, go on below.
|
// The local is in memory, go on below.
|
||||||
MPlaceTy { mplace: *mplace, layout, align }
|
MPlaceTy { mplace: *mplace, layout }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -876,7 +845,6 @@ where
|
||||||
self.write_immediate_to_mplace_no_validate(
|
self.write_immediate_to_mplace_no_validate(
|
||||||
*src_val,
|
*src_val,
|
||||||
src.layout(),
|
src.layout(),
|
||||||
dest_mem.align,
|
|
||||||
dest_mem.mplace,
|
dest_mem.mplace,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -903,14 +871,19 @@ where
|
||||||
// type does not have Scalar/ScalarPair layout.
|
// type does not have Scalar/ScalarPair layout.
|
||||||
// (Or as the `Assign` docs put it, assignments "not producing primitives" must be
|
// (Or as the `Assign` docs put it, assignments "not producing primitives" must be
|
||||||
// non-overlapping.)
|
// non-overlapping.)
|
||||||
|
// We check alignment separately, and *after* checking everything else.
|
||||||
|
// If an access is both OOB and misaligned, we want to see the bounds error.
|
||||||
self.mem_copy(
|
self.mem_copy(
|
||||||
src.ptr(),
|
src.ptr(),
|
||||||
src.align,
|
Align::ONE,
|
||||||
dest.ptr(),
|
dest.ptr(),
|
||||||
dest.align,
|
Align::ONE,
|
||||||
dest_size,
|
dest_size,
|
||||||
/*nonoverlapping*/ true,
|
/*nonoverlapping*/ true,
|
||||||
)
|
)?;
|
||||||
|
self.check_misalign(src.mplace.misaligned)?;
|
||||||
|
self.check_misalign(dest.mplace.misaligned)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensures that a place is in memory, and returns where it is.
|
/// Ensures that a place is in memory, and returns where it is.
|
||||||
|
@ -944,7 +917,6 @@ where
|
||||||
self.write_immediate_to_mplace_no_validate(
|
self.write_immediate_to_mplace_no_validate(
|
||||||
local_val,
|
local_val,
|
||||||
local_layout,
|
local_layout,
|
||||||
local_layout.align.abi,
|
|
||||||
mplace.mplace,
|
mplace.mplace,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -974,7 +946,7 @@ where
|
||||||
Place::Ptr(mplace) => mplace,
|
Place::Ptr(mplace) => mplace,
|
||||||
};
|
};
|
||||||
// Return with the original layout and align, so that the caller can go on
|
// Return with the original layout and align, so that the caller can go on
|
||||||
Ok(MPlaceTy { mplace, layout: place.layout, align: place.align })
|
Ok(MPlaceTy { mplace, layout: place.layout })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate_dyn(
|
pub fn allocate_dyn(
|
||||||
|
@ -987,7 +959,7 @@ where
|
||||||
span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known")
|
span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known")
|
||||||
};
|
};
|
||||||
let ptr = self.allocate_ptr(size, align, kind)?;
|
let ptr = self.allocate_ptr(size, align, kind)?;
|
||||||
Ok(MPlaceTy::from_aligned_ptr_with_meta(ptr.into(), layout, meta))
|
Ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate(
|
pub fn allocate(
|
||||||
|
@ -999,7 +971,7 @@ where
|
||||||
self.allocate_dyn(layout, kind, MemPlaceMeta::None)
|
self.allocate_dyn(layout, kind, MemPlaceMeta::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a wide MPlace of type `&'static [mut] str` to a new 1-aligned allocation.
|
/// Returns a wide MPlace of type `str` to a new 1-aligned allocation.
|
||||||
pub fn allocate_str(
|
pub fn allocate_str(
|
||||||
&mut self,
|
&mut self,
|
||||||
str: &str,
|
str: &str,
|
||||||
|
@ -1008,15 +980,8 @@ where
|
||||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||||
let ptr = self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)?;
|
let ptr = self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)?;
|
||||||
let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self);
|
let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self);
|
||||||
let mplace = MemPlace { ptr: ptr.into(), meta: MemPlaceMeta::Meta(meta) };
|
let layout = self.layout_of(self.tcx.types.str_).unwrap();
|
||||||
|
Ok(self.ptr_with_meta_to_mplace(ptr.into(), MemPlaceMeta::Meta(meta), layout))
|
||||||
let ty = Ty::new_ref(
|
|
||||||
self.tcx.tcx,
|
|
||||||
self.tcx.lifetimes.re_static,
|
|
||||||
ty::TypeAndMut { ty: self.tcx.types.str_, mutbl },
|
|
||||||
);
|
|
||||||
let layout = self.layout_of(ty).unwrap();
|
|
||||||
Ok(MPlaceTy { mplace, layout, align: layout.align.abi })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the aggregate to the destination.
|
/// Writes the aggregate to the destination.
|
||||||
|
@ -1055,7 +1020,7 @@ where
|
||||||
let _ = self.tcx.global_alloc(raw.alloc_id);
|
let _ = self.tcx.global_alloc(raw.alloc_id);
|
||||||
let ptr = self.global_base_pointer(Pointer::from(raw.alloc_id))?;
|
let ptr = self.global_base_pointer(Pointer::from(raw.alloc_id))?;
|
||||||
let layout = self.layout_of(raw.ty)?;
|
let layout = self.layout_of(raw.ty)?;
|
||||||
Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout))
|
Ok(self.ptr_to_mplace(ptr.into(), layout))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
|
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
|
||||||
|
@ -1071,12 +1036,10 @@ where
|
||||||
let vtable = mplace.meta().unwrap_meta().to_pointer(self)?;
|
let vtable = mplace.meta().unwrap_meta().to_pointer(self)?;
|
||||||
let (ty, _) = self.get_ptr_vtable(vtable)?;
|
let (ty, _) = self.get_ptr_vtable(vtable)?;
|
||||||
let layout = self.layout_of(ty)?;
|
let layout = self.layout_of(ty)?;
|
||||||
|
// This is a kind of transmute, from a place with unsized type and metadata to
|
||||||
let mplace = MPlaceTy {
|
// a place with sized type and no metadata.
|
||||||
mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace },
|
let mplace =
|
||||||
layout,
|
MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace }, layout };
|
||||||
align: layout.align.abi,
|
|
||||||
};
|
|
||||||
Ok((mplace, vtable))
|
Ok((mplace, vtable))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1108,10 +1071,10 @@ mod size_asserts {
|
||||||
use super::*;
|
use super::*;
|
||||||
use rustc_data_structures::static_assert_size;
|
use rustc_data_structures::static_assert_size;
|
||||||
// tidy-alphabetical-start
|
// tidy-alphabetical-start
|
||||||
static_assert_size!(MemPlace, 40);
|
static_assert_size!(MemPlace, 48);
|
||||||
static_assert_size!(MemPlaceMeta, 24);
|
static_assert_size!(MemPlaceMeta, 24);
|
||||||
static_assert_size!(MPlaceTy<'_>, 64);
|
static_assert_size!(MPlaceTy<'_>, 64);
|
||||||
static_assert_size!(Place, 40);
|
static_assert_size!(Place, 48);
|
||||||
static_assert_size!(PlaceTy<'_>, 64);
|
static_assert_size!(PlaceTy<'_>, 64);
|
||||||
// tidy-alphabetical-end
|
// tidy-alphabetical-end
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,6 +267,7 @@ where
|
||||||
let len = base.len(self)?;
|
let len = base.len(self)?;
|
||||||
let field_layout = base.layout().field(self, 0);
|
let field_layout = base.layout().field(self, 0);
|
||||||
// Ensure that all the offsets are in-bounds once, up-front.
|
// Ensure that all the offsets are in-bounds once, up-front.
|
||||||
|
debug!("project_array_fields: {base:?} {len}");
|
||||||
base.offset(len * stride, self.layout_of(self.tcx.types.unit).unwrap(), self)?;
|
base.offset(len * stride, self.layout_of(self.tcx.types.unit).unwrap(), self)?;
|
||||||
// Create the iterator.
|
// Create the iterator.
|
||||||
Ok(ArrayIterator { base, range: 0..len, stride, field_layout, _phantom: PhantomData })
|
Ok(ArrayIterator { base, range: 0..len, stride, field_layout, _phantom: PhantomData })
|
||||||
|
|
|
@ -7,6 +7,7 @@ use either::Either;
|
||||||
use rustc_middle::mir;
|
use rustc_middle::mir;
|
||||||
use rustc_middle::mir::interpret::{InterpResult, Scalar};
|
use rustc_middle::mir::interpret::{InterpResult, Scalar};
|
||||||
use rustc_middle::ty::layout::LayoutOf;
|
use rustc_middle::ty::layout::LayoutOf;
|
||||||
|
use rustc_target::abi::Align;
|
||||||
|
|
||||||
use super::{ImmTy, InterpCx, Machine, Projectable};
|
use super::{ImmTy, InterpCx, Machine, Projectable};
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
@ -206,15 +207,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
let elem_size = first.layout.size;
|
let elem_size = first.layout.size;
|
||||||
let first_ptr = first.ptr();
|
let first_ptr = first.ptr();
|
||||||
let rest_ptr = first_ptr.offset(elem_size, self)?;
|
let rest_ptr = first_ptr.offset(elem_size, self)?;
|
||||||
// For the alignment of `rest_ptr`, we crucially do *not* use `first.align` as
|
// No alignment requirement since `copy_op` above already checked it.
|
||||||
// that place might be more aligned than its type mandates (a `u8` array could
|
|
||||||
// be 4-aligned if it sits at the right spot in a struct). We have to also factor
|
|
||||||
// in element size.
|
|
||||||
self.mem_copy_repeatedly(
|
self.mem_copy_repeatedly(
|
||||||
first_ptr,
|
first_ptr,
|
||||||
dest.align,
|
Align::ONE,
|
||||||
rest_ptr,
|
rest_ptr,
|
||||||
dest.align.restrict_for_offset(elem_size),
|
Align::ONE,
|
||||||
elem_size,
|
elem_size,
|
||||||
length - 1,
|
length - 1,
|
||||||
/*nonoverlapping:*/ true,
|
/*nonoverlapping:*/ true,
|
||||||
|
|
|
@ -13,14 +13,14 @@ use rustc_ast::Mutability;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_middle::mir::interpret::{
|
use rustc_middle::mir::interpret::{
|
||||||
ExpectedKind, InterpError, InvalidMetaKind, PointerKind, ValidationErrorInfo,
|
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, ValidationErrorInfo,
|
||||||
ValidationErrorKind, ValidationErrorKind::*,
|
ValidationErrorKind, ValidationErrorKind::*,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||||
use rustc_span::symbol::{sym, Symbol};
|
use rustc_span::symbol::{sym, Symbol};
|
||||||
use rustc_target::abi::{
|
use rustc_target::abi::{
|
||||||
Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
|
Abi, Align, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
@ -385,7 +385,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||||
CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
|
CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
|
||||||
),
|
),
|
||||||
self.path,
|
self.path,
|
||||||
Ub(AlignmentCheckFailed { required, has }) => UnalignedPtr {
|
Ub(AlignmentCheckFailed(Misalignment { required, has })) => UnalignedPtr {
|
||||||
ptr_kind,
|
ptr_kind,
|
||||||
required_bytes: required.bytes(),
|
required_bytes: required.bytes(),
|
||||||
found_bytes: has.bytes()
|
found_bytes: has.bytes()
|
||||||
|
@ -781,14 +781,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
||||||
// Optimization: we just check the entire range at once.
|
// Optimization: we just check the entire range at once.
|
||||||
// NOTE: Keep this in sync with the handling of integer and float
|
// NOTE: Keep this in sync with the handling of integer and float
|
||||||
// types above, in `visit_primitive`.
|
// types above, in `visit_primitive`.
|
||||||
// In run-time mode, we accept pointers in here. This is actually more
|
// No need for an alignment check here, this is not an actual memory access.
|
||||||
// permissive than a per-element check would be, e.g., we accept
|
let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size, Align::ONE)?.expect("we already excluded size 0");
|
||||||
// a &[u8] that contains a pointer even though bytewise checking would
|
|
||||||
// reject it. However, that's good: We don't inherently want
|
|
||||||
// to reject those pointers, we just do not have the machinery to
|
|
||||||
// talk about parts of a pointer.
|
|
||||||
// We also accept uninit, for consistency with the slow path.
|
|
||||||
let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size, mplace.align)?.expect("we already excluded size 0");
|
|
||||||
|
|
||||||
match alloc.get_bytes_strip_provenance() {
|
match alloc.get_bytes_strip_provenance() {
|
||||||
// In the happy case, we needn't check anything else.
|
// In the happy case, we needn't check anything else.
|
||||||
|
|
|
@ -261,6 +261,13 @@ pub struct ScalarSizeMismatch {
|
||||||
pub data_size: u64,
|
pub data_size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Information about a misaligned pointer.
|
||||||
|
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||||
|
pub struct Misalignment {
|
||||||
|
pub has: Align,
|
||||||
|
pub required: Align,
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impl_into_diagnostic_arg_through_debug {
|
macro_rules! impl_into_diagnostic_arg_through_debug {
|
||||||
($($ty:ty),*$(,)?) => {$(
|
($($ty:ty),*$(,)?) => {$(
|
||||||
impl IntoDiagnosticArg for $ty {
|
impl IntoDiagnosticArg for $ty {
|
||||||
|
@ -322,7 +329,7 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
||||||
/// Using an integer as a pointer in the wrong way.
|
/// Using an integer as a pointer in the wrong way.
|
||||||
DanglingIntPointer(u64, CheckInAllocMsg),
|
DanglingIntPointer(u64, CheckInAllocMsg),
|
||||||
/// Used a pointer with bad alignment.
|
/// Used a pointer with bad alignment.
|
||||||
AlignmentCheckFailed { required: Align, has: Align },
|
AlignmentCheckFailed(Misalignment),
|
||||||
/// Writing to read-only memory.
|
/// Writing to read-only memory.
|
||||||
WriteToReadOnly(AllocId),
|
WriteToReadOnly(AllocId),
|
||||||
/// Trying to access the data behind a function pointer.
|
/// Trying to access the data behind a function pointer.
|
||||||
|
|
|
@ -144,7 +144,7 @@ use crate::ty::{self, Instance, Ty, TyCtxt};
|
||||||
pub use self::error::{
|
pub use self::error::{
|
||||||
struct_error, BadBytesAccess, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult,
|
struct_error, BadBytesAccess, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult,
|
||||||
EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo,
|
EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo,
|
||||||
InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, PointerKind,
|
InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, Misalignment, PointerKind,
|
||||||
ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo,
|
ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo,
|
||||||
UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
|
UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
|
||||||
};
|
};
|
||||||
|
|
|
@ -697,10 +697,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
|
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
|
||||||
let this = self.eval_context_ref();
|
let this = self.eval_context_ref();
|
||||||
let ptr = this.read_pointer(op)?;
|
let ptr = this.read_pointer(op)?;
|
||||||
|
Ok(this.ptr_to_mplace(ptr, layout))
|
||||||
let mplace = MPlaceTy::from_aligned_ptr(ptr, layout);
|
|
||||||
|
|
||||||
Ok(mplace)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the MPlaceTy given the offset and layout of an access on an operand
|
/// Calculates the MPlaceTy given the offset and layout of an access on an operand
|
||||||
|
|
|
@ -1370,7 +1370,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
("d_reclen", size.into()),
|
("d_reclen", size.into()),
|
||||||
("d_type", file_type.into()),
|
("d_type", file_type.into()),
|
||||||
],
|
],
|
||||||
&MPlaceTy::from_aligned_ptr(entry, dirent64_layout),
|
&this.ptr_to_mplace(entry, dirent64_layout),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let name_ptr = entry.offset(Size::from_bytes(d_name_offset), this)?;
|
let name_ptr = entry.offset(Size::from_bytes(d_name_offset), this)?;
|
||||||
|
|
|
@ -34,7 +34,7 @@ pub fn futex<'tcx>(
|
||||||
|
|
||||||
let thread = this.get_active_thread();
|
let thread = this.get_active_thread();
|
||||||
// This is a vararg function so we have to bring our own type for this pointer.
|
// This is a vararg function so we have to bring our own type for this pointer.
|
||||||
let addr = MPlaceTy::from_aligned_ptr(addr, this.machine.layouts.i32);
|
let addr = this.ptr_to_mplace(addr, this.machine.layouts.i32);
|
||||||
let addr_usize = addr.ptr().addr().bytes();
|
let addr_usize = addr.ptr().addr().bytes();
|
||||||
|
|
||||||
let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG");
|
let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG");
|
||||||
|
|
|
@ -322,8 +322,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
|
|
||||||
let layout = this.machine.layouts.uint(size).unwrap();
|
let layout = this.machine.layouts.uint(size).unwrap();
|
||||||
let futex_val = this
|
let futex_val = this
|
||||||
.read_scalar_atomic(&MPlaceTy::from_aligned_ptr(ptr, layout), AtomicReadOrd::Relaxed)?;
|
.read_scalar_atomic(&this.ptr_to_mplace(ptr, layout), AtomicReadOrd::Relaxed)?;
|
||||||
let compare_val = this.read_scalar(&MPlaceTy::from_aligned_ptr(compare, layout))?;
|
let compare_val = this.read_scalar(&this.ptr_to_mplace(compare, layout))?;
|
||||||
|
|
||||||
if futex_val == compare_val {
|
if futex_val == compare_val {
|
||||||
// If the values are the same, we have to block.
|
// If the values are the same, we have to block.
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
/// This tests that when a field sits at a well-aligned offset, accessing the field
|
||||||
|
/// requires high alignment even if the field type has lower alignment requirements.
|
||||||
|
|
||||||
|
#[repr(C, align(16))]
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct Aligned {
|
||||||
|
_pad: [u8; 11],
|
||||||
|
packed: Packed,
|
||||||
|
}
|
||||||
|
#[repr(packed)]
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct Packed {
|
||||||
|
_pad: [u8; 5],
|
||||||
|
x: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn foo(x: *const Aligned) -> u8 {
|
||||||
|
unsafe { (*x).packed.x } //~ERROR: accessing memory with alignment 1, but alignment 16 is required
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
unsafe {
|
||||||
|
let mem = [Aligned::default(); 16];
|
||||||
|
let odd_ptr = std::ptr::addr_of!(mem).cast::<u8>().add(1);
|
||||||
|
// `odd_ptr` is now not aligned enough for `Aligned`.
|
||||||
|
// If accessing the nested field `packed.x` can exploit that it is at offset 16
|
||||||
|
// in a 16-aligned struct, this has to be UB.
|
||||||
|
foo(odd_ptr.cast());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||||
|
--> $DIR/field_requires_parent_struct_alignment2.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | unsafe { (*x).packed.x }
|
||||||
|
| ^^^^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||||
|
|
|
||||||
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||||
|
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||||
|
= note: BACKTRACE:
|
||||||
|
= note: inside `foo` at $DIR/field_requires_parent_struct_alignment2.rs:LL:CC
|
||||||
|
note: inside `main`
|
||||||
|
--> $DIR/field_requires_parent_struct_alignment2.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | foo(odd_ptr.cast());
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// failure-status: 101
|
// failure-status: 101
|
||||||
// normalize-stderr-test "note: .*\n\n" -> ""
|
// normalize-stderr-test "note: .*\n\n" -> ""
|
||||||
// normalize-stderr-test "thread 'rustc' panicked.*\n" -> ""
|
// normalize-stderr-test "thread 'rustc' panicked.*\n" -> ""
|
||||||
|
// normalize-stderr-test "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: "
|
||||||
// rustc-env:RUST_BACKTRACE=0
|
// rustc-env:RUST_BACKTRACE=0
|
||||||
|
|
||||||
// This test used to cause an ICE in rustc_mir::interpret::step::eval_rvalue_into_place
|
// This test used to cause an ICE in rustc_mir::interpret::step::eval_rvalue_into_place
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
error: internal compiler error: compiler/rustc_const_eval/src/interpret/step.rs:274:21: SizeOf MIR operator called for unsized type dyn Debug
|
error: internal compiler error: compiler/rustc_const_eval/src/interpret/step.rs:LL:CC: SizeOf MIR operator called for unsized type dyn Debug
|
||||||
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||||
|
|
||||||
Box<dyn Any>
|
Box<dyn Any>
|
||||||
query stack during panic:
|
query stack during panic:
|
||||||
#0 [eval_to_allocation_raw] const-evaluating + checking `<impl at $DIR/issue-80742.rs:25:1: 27:32>::{constant#0}`
|
#0 [eval_to_allocation_raw] const-evaluating + checking `<impl at $DIR/issue-80742.rs:26:1: 28:32>::{constant#0}`
|
||||||
#1 [eval_to_valtree] evaluating type-level constant
|
#1 [eval_to_valtree] evaluating type-level constant
|
||||||
end of query stack
|
end of query stack
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
|
@ -3,5 +3,6 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
static C: u64 = unsafe {*(0xdeadbeef as *const u64)};
|
static C: u64 = unsafe {*(0xdeadbeef as *const u64)};
|
||||||
//~^ ERROR could not evaluate static initializer
|
//~^ ERROR could not evaluate static initializer
|
||||||
|
//~| dangling pointer
|
||||||
println!("{}", C);
|
println!("{}", C);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,17 @@ const MISALIGNED_COPY: () = unsafe {
|
||||||
// The actual error points into the implementation of `copy_to_nonoverlapping`.
|
// The actual error points into the implementation of `copy_to_nonoverlapping`.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MISALIGNED_FIELD: () = unsafe {
|
||||||
|
#[repr(align(16))]
|
||||||
|
struct Aligned(f32);
|
||||||
|
|
||||||
|
let mem = [0f32; 8];
|
||||||
|
let ptr = mem.as_ptr().cast::<Aligned>();
|
||||||
|
// Accessing an f32 field but we still require the alignment of the pointer type.
|
||||||
|
let _val = (*ptr).0; //~ERROR: evaluation of constant value failed
|
||||||
|
//~^NOTE: accessing memory with alignment 4, but alignment 16 is required
|
||||||
|
};
|
||||||
|
|
||||||
const OOB: () = unsafe {
|
const OOB: () = unsafe {
|
||||||
let mem = [0u32; 1];
|
let mem = [0u32; 1];
|
||||||
let ptr = mem.as_ptr().cast::<u64>();
|
let ptr = mem.as_ptr().cast::<u64>();
|
||||||
|
|
|
@ -26,11 +26,17 @@ LL | y.copy_to_nonoverlapping(&mut z, 1);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error[E0080]: evaluation of constant value failed
|
error[E0080]: evaluation of constant value failed
|
||||||
--> $DIR/raw-pointer-ub.rs:32:16
|
--> $DIR/raw-pointer-ub.rs:36:16
|
||||||
|
|
|
||||||
|
LL | let _val = (*ptr).0;
|
||||||
|
| ^^^^^^^^ accessing memory with alignment 4, but alignment 16 is required
|
||||||
|
|
||||||
|
error[E0080]: evaluation of constant value failed
|
||||||
|
--> $DIR/raw-pointer-ub.rs:43:16
|
||||||
|
|
|
|
||||||
LL | let _val = *ptr;
|
LL | let _val = *ptr;
|
||||||
| ^^^^ memory access failed: allocN has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
|
| ^^^^ memory access failed: allocN has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 5 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0080`.
|
For more information about this error, try `rustc --explain E0080`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue