mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-25 13:24:22 +00:00
Auto merge of #114330 - RalfJung:dagling-ptr-deref, r=oli-obk
don't UB on dangling ptr deref, instead check inbounds on projections This implements https://github.com/rust-lang/reference/pull/1387 in Miri. See that PR for what the change is about. Detecting dangling references in `let x = &...;` is now done by validity checking only, so some tests need to have validity checking enabled. There is no longer inherently a "nodangle" check in evaluating the expression `&*ptr` (aside from the aliasing model). r? `@oli-obk` Based on: - https://github.com/rust-lang/reference/pull/1387 - https://github.com/rust-lang/rust/pull/115524
This commit is contained in:
commit
e7bdc5f9f8
compiler
src/tools/miri
src
tests
compiletest.rs
fail-dep/shims
fail
alloc
concurrency
const-ub-checks.stderrdangling_pointers
dangling_pointer_addr_of.rsdangling_pointer_addr_of.stderrdangling_pointer_deref.stderrdangling_pointer_project_underscore.rsdangling_pointer_project_underscore.stderrdangling_primitive.stderrdangling_zst_deref.stderrderef-invalid-ptr.stderrderef-partially-dangling.rsderef-partially-dangling.stderrderef_dangling_box.rsderef_dangling_box.stderrderef_dangling_ref.rsderef_dangling_ref.stderrdyn_size.rsdyn_size.stderrmaybe_null_pointer_deref_zst.stderrmaybe_null_pointer_write_zst.stderrnull_pointer_deref.stderrnull_pointer_deref_zst.rsnull_pointer_deref_zst.stderrnull_pointer_write.stderrnull_pointer_write_zst.rsnull_pointer_write_zst.stderrout_of_bounds_project.rsout_of_bounds_project.stderrout_of_bounds_read.rsout_of_bounds_read.stderrout_of_bounds_read1.rsout_of_bounds_read2.rsout_of_bounds_write.rsout_of_bounds_write.stderrstack_temporary.stderrstorage_dead_dangling.stderrwild_pointer_deref.stderr
data_race
environ-gets-deallocated.stderrfunction_pointers
generator-pinned-moved.stderrintrinsics
provenance
pointer_partial_overwrite.stderrprovenance_transmute.rsprovenance_transmute.stderrptr_int_unexposed.stderrptr_invalid.stderr
rc_as_ptr.stderrreading_half_a_pointer.stderrunaligned_pointers
alignment.stderrdrop_in_place.rsdrop_in_place.stderrdyn_alignment.stderrfield_requires_parent_struct_alignment.rsfield_requires_parent_struct_alignment.stderrfield_requires_parent_struct_alignment2.rsfield_requires_parent_struct_alignment2.stderrintptrcast_alignment_check.rsintptrcast_alignment_check.stderrreference_to_packed.rsreference_to_packed.stderrunaligned_ptr1.rsunaligned_ptr1.stderrunaligned_ptr2.rsunaligned_ptr2.stderr
@ -681,6 +681,7 @@ impl fmt::Display for AlignFromBytesError {
|
||||
|
||||
impl Align {
|
||||
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 };
|
||||
|
||||
#[inline]
|
||||
|
@ -1,11 +1,15 @@
|
||||
const_eval_address_space_full =
|
||||
there are no more free addresses in the address space
|
||||
const_eval_align_check_failed = accessing memory with alignment {$has}, but alignment {$required} is required
|
||||
|
||||
const_eval_align_offset_invalid_align =
|
||||
`align_offset` called with non-power-of-two align: {$target_align}
|
||||
|
||||
const_eval_alignment_check_failed =
|
||||
accessing memory with alignment {$has}, but alignment {$required} is required
|
||||
{$msg ->
|
||||
[AccessedPtr] accessing memory
|
||||
*[other] accessing memory based on pointer
|
||||
} with alignment {$has}, but alignment {$required} is required
|
||||
|
||||
const_eval_already_reported =
|
||||
an error has already been reported elsewhere (this should not usually be printed)
|
||||
const_eval_assume_false =
|
||||
@ -61,7 +65,6 @@ const_eval_deref_coercion_non_const =
|
||||
.target_note = deref defined here
|
||||
const_eval_deref_function_pointer =
|
||||
accessing {$allocation} which contains a function
|
||||
const_eval_deref_test = dereferencing pointer failed
|
||||
const_eval_deref_vtable_pointer =
|
||||
accessing {$allocation} which contains a vtable
|
||||
const_eval_different_allocations =
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::mem;
|
||||
|
||||
use either::{Left, Right};
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
@ -73,9 +75,9 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
||||
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)?;
|
||||
// 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);
|
||||
Ok(ret)
|
||||
|
@ -5,8 +5,9 @@ use rustc_errors::{
|
||||
use rustc_hir::ConstContext;
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::mir::interpret::{
|
||||
CheckInAllocMsg, ExpectedKind, InterpError, InvalidMetaKind, InvalidProgramInfo, PointerKind,
|
||||
ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
|
||||
CheckInAllocMsg, ExpectedKind, InterpError, InvalidMetaKind, InvalidProgramInfo, Misalignment,
|
||||
PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo,
|
||||
ValidationErrorInfo,
|
||||
};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::Span;
|
||||
@ -389,15 +390,6 @@ pub struct LiveDrop<'tcx> {
|
||||
pub dropped_at: Option<Span>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(const_eval_align_check_failed)]
|
||||
pub struct AlignmentCheckFailed {
|
||||
pub has: u64,
|
||||
pub required: u64,
|
||||
#[subdiagnostic]
|
||||
pub frames: Vec<FrameNote>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(const_eval_error, code = "E0080")]
|
||||
pub struct ConstEvalError {
|
||||
@ -459,7 +451,6 @@ fn bad_pointer_message(msg: CheckInAllocMsg, handler: &Handler) -> String {
|
||||
use crate::fluent_generated::*;
|
||||
|
||||
let msg = match msg {
|
||||
CheckInAllocMsg::DerefTest => const_eval_deref_test,
|
||||
CheckInAllocMsg::MemoryAccessTest => const_eval_memory_access_test,
|
||||
CheckInAllocMsg::PointerArithmeticTest => const_eval_pointer_arithmetic_test,
|
||||
CheckInAllocMsg::OffsetFromTest => const_eval_offset_from_test,
|
||||
@ -568,9 +559,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
|
||||
|
||||
builder.set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
|
||||
}
|
||||
AlignmentCheckFailed { required, has } => {
|
||||
AlignmentCheckFailed(Misalignment { required, has }, msg) => {
|
||||
builder.set_arg("required", required.bytes());
|
||||
builder.set_arg("has", has.bytes());
|
||||
builder.set_arg("msg", format!("{msg:?}"));
|
||||
}
|
||||
WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) => {
|
||||
builder.set_arg("allocation", alloc);
|
||||
|
@ -161,7 +161,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
||||
|
||||
#[inline(always)]
|
||||
fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
|
||||
&self.ecx
|
||||
self.ecx
|
||||
}
|
||||
|
||||
fn visit_value(&mut self, mplace: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
|
||||
@ -259,7 +259,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
||||
// to avoid could be expensive: on the potentially larger types, arrays and slices,
|
||||
// rather than on all aggregates unconditionally.
|
||||
if matches!(mplace.layout.ty.kind(), ty::Array(..) | ty::Slice(..)) {
|
||||
let Some((size, align)) = self.ecx.size_and_align_of_mplace(&mplace)? else {
|
||||
let Some((size, _align)) = self.ecx.size_and_align_of_mplace(&mplace)? else {
|
||||
// We do the walk if we can't determine the size of the mplace: we may be
|
||||
// dealing with extern types here in the future.
|
||||
return Ok(true);
|
||||
@ -267,7 +267,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
||||
|
||||
// If there is no provenance in this allocation, it does not contain references
|
||||
// that point to another allocation, and we can avoid the interning walk.
|
||||
if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr(), size, align)? {
|
||||
if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr(), size)? {
|
||||
if !alloc.has_provenance() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use rustc_middle::ty::layout::{LayoutOf as _, ValidityRequirement};
|
||||
use rustc_middle::ty::GenericArgsRef;
|
||||
use rustc_middle::ty::{Ty, TyCtxt};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_target::abi::{Abi, Align, Primitive, Size};
|
||||
use rustc_target::abi::{Abi, Primitive, Size};
|
||||
|
||||
use super::{
|
||||
util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
|
||||
@ -349,10 +349,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
// Check that the range between them is dereferenceable ("in-bounds or one past the
|
||||
// end of the same allocation"). This is like the check in ptr_offset_inbounds.
|
||||
let min_ptr = if dist >= 0 { b } else { a };
|
||||
self.check_ptr_access_align(
|
||||
self.check_ptr_access(
|
||||
min_ptr,
|
||||
Size::from_bytes(dist.unsigned_abs()),
|
||||
Align::ONE,
|
||||
CheckInAllocMsg::OffsetFromTest,
|
||||
)?;
|
||||
|
||||
@ -571,16 +570,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
pub fn ptr_offset_inbounds(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
pointee_ty: Ty<'tcx>,
|
||||
offset_count: i64,
|
||||
offset_bytes: i64,
|
||||
) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
|
||||
// We cannot overflow i64 as a type's size must be <= isize::MAX.
|
||||
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
|
||||
// The computed offset, in bytes, must not overflow an isize.
|
||||
// `checked_mul` enforces a too small bound, but no actual allocation can be big enough for
|
||||
// the difference to be noticeable.
|
||||
let offset_bytes =
|
||||
offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?;
|
||||
// The offset being in bounds cannot rely on "wrapping around" the address space.
|
||||
// So, first rule out overflows in the pointer arithmetic.
|
||||
let offset_ptr = ptr.signed_offset(offset_bytes, self)?;
|
||||
@ -589,10 +580,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
// pointers to be properly aligned (unlike a read/write operation).
|
||||
let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr };
|
||||
// This call handles checking for integer/null pointers.
|
||||
self.check_ptr_access_align(
|
||||
self.check_ptr_access(
|
||||
min_ptr,
|
||||
Size::from_bytes(offset_bytes.unsigned_abs()),
|
||||
Align::ONE,
|
||||
CheckInAllocMsg::PointerArithmeticTest,
|
||||
)?;
|
||||
Ok(offset_ptr)
|
||||
@ -621,7 +611,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
let src = self.read_pointer(src)?;
|
||||
let dst = self.read_pointer(dst)?;
|
||||
|
||||
self.mem_copy(src, align, dst, align, size, nonoverlapping)
|
||||
self.check_ptr_align(src, align)?;
|
||||
self.check_ptr_align(dst, align)?;
|
||||
|
||||
self.mem_copy(src, dst, size, nonoverlapping)
|
||||
}
|
||||
|
||||
pub(crate) fn write_bytes_intrinsic(
|
||||
@ -677,7 +670,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
size|
|
||||
-> InterpResult<'tcx, &[u8]> {
|
||||
let ptr = this.read_pointer(op)?;
|
||||
let Some(alloc_ref) = self.get_ptr_alloc(ptr, size, Align::ONE)? else {
|
||||
let Some(alloc_ref) = self.get_ptr_alloc(ptr, size)? else {
|
||||
// zero-sized access
|
||||
return Ok(&[]);
|
||||
};
|
||||
|
@ -436,6 +436,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
||||
place: &PlaceTy<'tcx, Self::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Without an aliasing model, all we can do is put `Uninit` into the place.
|
||||
// Conveniently this also ensures that the place actually points to suitable memory.
|
||||
ecx.write_uninit(place)
|
||||
}
|
||||
|
||||
|
@ -21,9 +21,9 @@ use rustc_target::abi::{Align, HasDataLayout, Size};
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
use super::{
|
||||
alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg,
|
||||
GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance,
|
||||
Scalar,
|
||||
alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckAlignMsg,
|
||||
CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer,
|
||||
PointerArithmetic, Provenance, Scalar,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
@ -258,14 +258,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
None => self.get_alloc_raw(alloc_id)?.size(),
|
||||
};
|
||||
// This will also call the access hooks.
|
||||
self.mem_copy(
|
||||
ptr,
|
||||
Align::ONE,
|
||||
new_ptr.into(),
|
||||
Align::ONE,
|
||||
old_size.min(new_size),
|
||||
/*nonoverlapping*/ true,
|
||||
)?;
|
||||
self.mem_copy(ptr, new_ptr.into(), old_size.min(new_size), /*nonoverlapping*/ true)?;
|
||||
self.deallocate_ptr(ptr, old_size_and_align, kind)?;
|
||||
|
||||
Ok(new_ptr)
|
||||
@ -367,12 +360,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Align,
|
||||
) -> InterpResult<'tcx, Option<(AllocId, Size, M::ProvenanceExtra)>> {
|
||||
self.check_and_deref_ptr(
|
||||
ptr,
|
||||
size,
|
||||
M::enforce_alignment(self).then_some(align),
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
|alloc_id, offset, prov| {
|
||||
let (size, align) = self
|
||||
@ -382,18 +373,16 @@ 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`
|
||||
/// (ignoring `M::enforce_alignment`). The caller can control the error message for the
|
||||
/// out-of-bounds case.
|
||||
/// Check if the given pointer points to live memory of the given `size`.
|
||||
/// The caller can control the error message for the out-of-bounds case.
|
||||
#[inline(always)]
|
||||
pub fn check_ptr_access_align(
|
||||
pub fn check_ptr_access(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Align,
|
||||
msg: CheckInAllocMsg,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.check_and_deref_ptr(ptr, size, Some(align), msg, |alloc_id, _, _| {
|
||||
self.check_and_deref_ptr(ptr, size, msg, |alloc_id, _, _| {
|
||||
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
|
||||
Ok((size, align, ()))
|
||||
})?;
|
||||
@ -402,15 +391,13 @@ 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
|
||||
/// 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
|
||||
/// is done.
|
||||
/// checking is offloaded to a helper closure.
|
||||
///
|
||||
/// If this returns `None`, the size is 0; it can however return `Some` even for size 0.
|
||||
fn check_and_deref_ptr<T>(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Option<Align>,
|
||||
msg: CheckInAllocMsg,
|
||||
alloc_size: impl FnOnce(
|
||||
AllocId,
|
||||
@ -425,14 +412,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
if size.bytes() > 0 || addr == 0 {
|
||||
throw_ub!(DanglingIntPointer(addr, msg));
|
||||
}
|
||||
// Must be aligned.
|
||||
if let Some(align) = align {
|
||||
self.check_offset_align(addr, align)?;
|
||||
}
|
||||
None
|
||||
}
|
||||
Ok((alloc_id, offset, prov)) => {
|
||||
let (alloc_size, alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?;
|
||||
let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?;
|
||||
// Test bounds. This also ensures non-null.
|
||||
// It is sufficient to check this for the end pointer. Also check for overflow!
|
||||
if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) {
|
||||
@ -448,20 +431,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
if M::Provenance::OFFSET_IS_ADDR {
|
||||
assert_ne!(ptr.addr(), Size::ZERO);
|
||||
}
|
||||
// Test align. Check this last; if both bounds and alignment are violated
|
||||
// we want the error to be about the bounds.
|
||||
if let Some(align) = 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
|
||||
// return `None`.
|
||||
@ -470,17 +439,65 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
})
|
||||
}
|
||||
|
||||
fn check_offset_align(&self, offset: u64, align: Align) -> InterpResult<'tcx> {
|
||||
if offset % align.bytes() == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
// The biggest power of two through which `offset` is divisible.
|
||||
let offset_pow2 = 1 << offset.trailing_zeros();
|
||||
throw_ub!(AlignmentCheckFailed {
|
||||
has: Align::from_bytes(offset_pow2).unwrap(),
|
||||
required: align
|
||||
});
|
||||
pub(super) fn check_misalign(
|
||||
&self,
|
||||
misaligned: Option<Misalignment>,
|
||||
msg: CheckAlignMsg,
|
||||
) -> InterpResult<'tcx> {
|
||||
if let Some(misaligned) = misaligned {
|
||||
throw_ub!(AlignmentCheckFailed(misaligned, msg))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn is_ptr_misaligned(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
align: Align,
|
||||
) -> Option<Misalignment> {
|
||||
if !M::enforce_alignment(self) || align.bytes() == 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn offset_misalignment(offset: u64, align: Align) -> Option<Misalignment> {
|
||||
if offset % align.bytes() == 0 {
|
||||
None
|
||||
} else {
|
||||
// The biggest power of two through which `offset` is divisible.
|
||||
let offset_pow2 = 1 << offset.trailing_zeros();
|
||||
Some(Misalignment { has: Align::from_bytes(offset_pow2).unwrap(), required: align })
|
||||
}
|
||||
}
|
||||
|
||||
match self.ptr_try_get_alloc_id(ptr) {
|
||||
Err(addr) => offset_misalignment(addr, align),
|
||||
Ok((alloc_id, offset, _prov)) => {
|
||||
let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id);
|
||||
if M::use_addr_for_alignment_check(self) {
|
||||
// `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
|
||||
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 {
|
||||
offset_misalignment(offset.bytes(), align)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks a pointer for misalignment.
|
||||
///
|
||||
/// The error assumes this is checking the pointer used directly for an access.
|
||||
pub fn check_ptr_align(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
align: Align,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.check_misalign(self.is_ptr_misaligned(ptr, align), CheckAlignMsg::AccessedPtr)
|
||||
}
|
||||
}
|
||||
|
||||
@ -575,18 +592,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
}
|
||||
}
|
||||
|
||||
/// "Safe" (bounds and align-checked) allocation access.
|
||||
/// Bounds-checked *but not align-checked* allocation access.
|
||||
pub fn get_ptr_alloc<'a>(
|
||||
&'a self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Align,
|
||||
) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
|
||||
{
|
||||
let ptr_and_alloc = self.check_and_deref_ptr(
|
||||
ptr,
|
||||
size,
|
||||
M::enforce_alignment(self).then_some(align),
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
|alloc_id, offset, prov| {
|
||||
let alloc = self.get_alloc_raw(alloc_id)?;
|
||||
@ -647,15 +662,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
Ok((alloc, &mut self.machine))
|
||||
}
|
||||
|
||||
/// "Safe" (bounds and align-checked) allocation access.
|
||||
/// Bounds-checked *but not align-checked* allocation access.
|
||||
pub fn get_ptr_alloc_mut<'a>(
|
||||
&'a mut self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Align,
|
||||
) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
|
||||
{
|
||||
let parts = self.get_ptr_access(ptr, size, align)?;
|
||||
let parts = self.get_ptr_access(ptr, size)?;
|
||||
if let Some((alloc_id, offset, prov)) = parts {
|
||||
let tcx = *self.tcx;
|
||||
// FIXME: can we somehow avoid looking up the allocation twice here?
|
||||
@ -1012,7 +1026,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
) -> InterpResult<'tcx, &[u8]> {
|
||||
let Some(alloc_ref) = self.get_ptr_alloc(ptr, size, Align::ONE)? else {
|
||||
let Some(alloc_ref) = self.get_ptr_alloc(ptr, size)? else {
|
||||
// zero-sized access
|
||||
return Ok(&[]);
|
||||
};
|
||||
@ -1038,7 +1052,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
assert_eq!(lower, len, "can only write iterators with a precise length");
|
||||
|
||||
let size = Size::from_bytes(len);
|
||||
let Some(alloc_ref) = self.get_ptr_alloc_mut(ptr, size, Align::ONE)? else {
|
||||
let Some(alloc_ref) = self.get_ptr_alloc_mut(ptr, size)? else {
|
||||
// zero-sized access
|
||||
assert_matches!(src.next(), None, "iterator said it was empty but returned an element");
|
||||
return Ok(());
|
||||
@ -1063,29 +1077,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
pub fn mem_copy(
|
||||
&mut self,
|
||||
src: Pointer<Option<M::Provenance>>,
|
||||
src_align: Align,
|
||||
dest: Pointer<Option<M::Provenance>>,
|
||||
dest_align: Align,
|
||||
size: Size,
|
||||
nonoverlapping: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.mem_copy_repeatedly(src, src_align, dest, dest_align, size, 1, nonoverlapping)
|
||||
self.mem_copy_repeatedly(src, dest, size, 1, nonoverlapping)
|
||||
}
|
||||
|
||||
pub fn mem_copy_repeatedly(
|
||||
&mut self,
|
||||
src: Pointer<Option<M::Provenance>>,
|
||||
src_align: Align,
|
||||
dest: Pointer<Option<M::Provenance>>,
|
||||
dest_align: Align,
|
||||
size: Size,
|
||||
num_copies: u64,
|
||||
nonoverlapping: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
// We need to do our own bounds-checks.
|
||||
let src_parts = self.get_ptr_access(src, size, src_align)?;
|
||||
let dest_parts = self.get_ptr_access(dest, size * num_copies, dest_align)?; // `Size` multiplication
|
||||
let src_parts = self.get_ptr_access(src, size)?;
|
||||
let dest_parts = self.get_ptr_access(dest, size * num_copies)?; // `Size` multiplication
|
||||
|
||||
// FIXME: we look up both allocations twice here, once before for the `check_ptr_access`
|
||||
// and once below to get the underlying `&[mut] Allocation`.
|
||||
|
@ -26,7 +26,7 @@ pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackP
|
||||
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
|
||||
pub use self::operand::{ImmTy, Immediate, OpTy, Readable};
|
||||
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
|
||||
pub use self::projection::Projectable;
|
||||
pub use self::projection::{OffsetMode, Projectable};
|
||||
pub use self::terminator::FnArg;
|
||||
pub use self::validity::{CtfeValidationMode, RefTracking};
|
||||
pub use self::visitor::ValueVisitor;
|
||||
|
@ -10,11 +10,12 @@ use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
|
||||
use rustc_middle::ty::{ConstInt, Ty, TyCtxt};
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
|
||||
use rustc_target::abi::{self, Abi, HasDataLayout, Size};
|
||||
|
||||
use super::{
|
||||
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, Frame, InterpCx, InterpResult,
|
||||
MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer, Projectable, Provenance, Scalar,
|
||||
MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, PlaceTy, Pointer, Projectable,
|
||||
Provenance, Scalar,
|
||||
};
|
||||
|
||||
/// An `Immediate` represents a single immediate self-contained Rust value.
|
||||
@ -43,12 +44,16 @@ impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> {
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> Immediate<Prov> {
|
||||
pub fn from_pointer(ptr: Pointer<Prov>, cx: &impl HasDataLayout) -> Self {
|
||||
Immediate::Scalar(Scalar::from_pointer(ptr, cx))
|
||||
}
|
||||
|
||||
pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self {
|
||||
Immediate::Scalar(Scalar::from_maybe_pointer(ptr, cx))
|
||||
pub fn new_pointer_with_meta(
|
||||
ptr: Pointer<Option<Prov>>,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
cx: &impl HasDataLayout,
|
||||
) -> Self {
|
||||
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 {
|
||||
@ -219,6 +224,17 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
||||
/// given layout.
|
||||
// Not called `offset` to avoid confusion with the trait method.
|
||||
fn offset_(&self, offset: Size, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self {
|
||||
debug_assert!(layout.is_sized(), "unsized immediates are not a thing");
|
||||
// `ImmTy` have already been checked to be in-bounds, so we can just check directly if this
|
||||
// remains in-bounds. This cannot actually be violated since projections are type-checked
|
||||
// and bounds-checked.
|
||||
assert!(
|
||||
offset + layout.size <= self.layout.size,
|
||||
"attempting to project to field at offset {} with size {} into immediate with layout {:#?}",
|
||||
offset.bytes(),
|
||||
layout.size.bytes(),
|
||||
self.layout,
|
||||
);
|
||||
// This makes several assumptions about what layouts we will encounter; we match what
|
||||
// codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
|
||||
let inner_val: Immediate<_> = match (**self, self.layout.abi) {
|
||||
@ -286,6 +302,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> {
|
||||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
_mode: OffsetMode,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
@ -315,14 +332,6 @@ pub(super) enum Operand<Prov: Provenance = AllocId> {
|
||||
pub struct OpTy<'tcx, Prov: Provenance = AllocId> {
|
||||
op: Operand<Prov>, // Keep this private; it helps enforce invariants.
|
||||
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> {
|
||||
@ -338,18 +347,14 @@ impl<Prov: Provenance> std::fmt::Debug for OpTy<'_, Prov> {
|
||||
impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
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> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
|
||||
OpTy {
|
||||
op: Operand::Indirect(*mplace.mplace()),
|
||||
layout: mplace.layout,
|
||||
align: Some(mplace.align),
|
||||
}
|
||||
OpTy { op: Operand::Indirect(*mplace.mplace()), layout: mplace.layout }
|
||||
}
|
||||
}
|
||||
|
||||
@ -380,14 +385,14 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> {
|
||||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
mode: OffsetMode,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
match self.as_mplace_or_imm() {
|
||||
Left(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, ecx)?.into()),
|
||||
Left(mplace) => Ok(mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into()),
|
||||
Right(imm) => {
|
||||
debug_assert!(layout.is_sized(), "unsized immediates are not a thing");
|
||||
assert_matches!(meta, MemPlaceMeta::None); // no place to store metadata here
|
||||
// Every part of an uninit is uninit.
|
||||
Ok(imm.offset_(offset, layout, ecx).into())
|
||||
@ -622,7 +627,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
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.
|
||||
@ -637,16 +642,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
Right((frame, local, offset)) => {
|
||||
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 mut field = match offset {
|
||||
Ok(match offset {
|
||||
Some(offset) => base.offset(offset, place.layout, self)?,
|
||||
None => {
|
||||
// In the common case this hasn't been projected.
|
||||
debug_assert_eq!(place.layout, base.layout);
|
||||
base
|
||||
}
|
||||
};
|
||||
field.align = Some(place.align);
|
||||
Ok(field)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -734,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 op = match val_val {
|
||||
let imm = match val_val {
|
||||
mir::ConstValue::Indirect { alloc_id, offset } => {
|
||||
// We rely on mutability being set correctly in that allocation to prevent writes
|
||||
// where none should happen.
|
||||
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::ZeroSized => Operand::Immediate(Immediate::Uninit),
|
||||
mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(),
|
||||
mir::ConstValue::ZeroSized => Immediate::Uninit,
|
||||
mir::ConstValue::Slice { data, meta } => {
|
||||
// We rely on mutability being set correctly in `data` to prevent writes
|
||||
// where none should happen.
|
||||
let ptr = Pointer::new(self.tcx.reserve_and_set_memory_alloc(data), Size::ZERO);
|
||||
Operand::Immediate(Immediate::new_slice(
|
||||
self.global_base_pointer(ptr)?.into(),
|
||||
meta,
|
||||
self,
|
||||
))
|
||||
Immediate::new_slice(self.global_base_pointer(ptr)?.into(), meta, self)
|
||||
}
|
||||
};
|
||||
Ok(OpTy { op, layout, align: Some(layout.align.abi) })
|
||||
Ok(OpTy { op: Operand::Immediate(imm), layout })
|
||||
}
|
||||
}
|
||||
|
||||
@ -767,6 +766,6 @@ mod size_asserts {
|
||||
static_assert_size!(Immediate, 48);
|
||||
static_assert_size!(ImmTy<'_>, 64);
|
||||
static_assert_size!(Operand, 56);
|
||||
static_assert_size!(OpTy<'_>, 80);
|
||||
static_assert_size!(OpTy<'_>, 72);
|
||||
// tidy-alphabetical-end
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rustc_apfloat::{Float, FloatConvert};
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{InterpResult, Scalar};
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, FloatTy, Ty};
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_target::abi::Abi;
|
||||
@ -337,7 +337,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
let offset_count = right.to_scalar().to_target_isize(self)?;
|
||||
let pointee_ty = left.layout.ty.builtin_deref(true).unwrap().ty;
|
||||
|
||||
let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?;
|
||||
// We cannot overflow i64 as a type's size must be <= isize::MAX.
|
||||
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
|
||||
// The computed offset, in bytes, must not overflow an isize.
|
||||
// `checked_mul` enforces a too small bound, but no actual allocation can be big enough for
|
||||
// the difference to be noticeable.
|
||||
let offset_bytes =
|
||||
offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?;
|
||||
|
||||
let offset_ptr = self.ptr_offset_inbounds(ptr, offset_bytes)?;
|
||||
Ok((
|
||||
ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout),
|
||||
false,
|
||||
|
@ -15,9 +15,9 @@ use rustc_middle::ty::Ty;
|
||||
use rustc_target::abi::{Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_VARIANT};
|
||||
|
||||
use super::{
|
||||
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg, ImmTy,
|
||||
Immediate, InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand, Pointer,
|
||||
PointerArithmetic, Projectable, Provenance, Readable, Scalar,
|
||||
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckAlignMsg, ImmTy,
|
||||
Immediate, InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy,
|
||||
Operand, Pointer, PointerArithmetic, Projectable, Provenance, Readable, Scalar,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
@ -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
|
||||
/// (e.g., `extern type`).
|
||||
pub meta: MemPlaceMeta<Prov>,
|
||||
/// Stores whether this place was created based on a sufficiently aligned pointer.
|
||||
misaligned: Option<Misalignment>,
|
||||
}
|
||||
|
||||
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).
|
||||
pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self {
|
||||
MemPlace { ptr: self.ptr.map_provenance(f), ..self }
|
||||
@ -78,27 +70,32 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
||||
/// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
|
||||
#[inline]
|
||||
pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Prov> {
|
||||
match self.meta {
|
||||
MemPlaceMeta::None => Immediate::from(Scalar::from_maybe_pointer(self.ptr, cx)),
|
||||
MemPlaceMeta::Meta(meta) => {
|
||||
Immediate::ScalarPair(Scalar::from_maybe_pointer(self.ptr, cx), meta)
|
||||
}
|
||||
}
|
||||
Immediate::new_pointer_with_meta(self.ptr, self.meta, cx)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// Not called `offset_with_meta` to avoid confusion with the trait method.
|
||||
fn offset_with_meta_<'tcx>(
|
||||
fn offset_with_meta_<'mir, 'tcx, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
self,
|
||||
offset: Size,
|
||||
mode: OffsetMode,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
cx: &impl HasDataLayout,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
debug_assert!(
|
||||
!meta.has_meta() || self.meta.has_meta(),
|
||||
"cannot use `offset_with_meta` to add metadata to a place"
|
||||
);
|
||||
Ok(MemPlace { ptr: self.ptr.offset(offset, cx)?, meta })
|
||||
if offset > ecx.data_layout().max_size_of_val() {
|
||||
throw_ub!(PointerArithOverflow);
|
||||
}
|
||||
let ptr = match mode {
|
||||
OffsetMode::Inbounds => {
|
||||
ecx.ptr_offset_inbounds(self.ptr, offset.bytes().try_into().unwrap())?
|
||||
}
|
||||
OffsetMode::Wrapping => self.ptr.wrapping_offset(offset, ecx),
|
||||
};
|
||||
Ok(MemPlace { ptr, meta, misaligned: self.misaligned })
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,11 +104,6 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
||||
pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||
mplace: MemPlace<Prov>,
|
||||
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> {
|
||||
@ -133,25 +125,7 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
|
||||
assert!(layout.is_zst());
|
||||
let align = layout.align.abi;
|
||||
let ptr = Pointer::from_addr_invalid(align.bytes()); // no provenance, absolute address
|
||||
MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align }
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None, misaligned: None }, layout }
|
||||
}
|
||||
|
||||
/// Adjust the provenance of the main pointer (metadata is unaffected).
|
||||
@ -189,15 +163,12 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
mode: OffsetMode,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
Ok(MPlaceTy {
|
||||
mplace: self.mplace.offset_with_meta_(offset, meta, ecx)?,
|
||||
align: self.align.restrict_for_offset(offset),
|
||||
layout,
|
||||
})
|
||||
Ok(MPlaceTy { mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?, layout })
|
||||
}
|
||||
|
||||
fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
@ -228,11 +199,6 @@ pub(super) enum Place<Prov: Provenance = AllocId> {
|
||||
pub struct PlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||
place: Place<Prov>, // Keep this private; it helps enforce invariants.
|
||||
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> {
|
||||
@ -248,7 +214,7 @@ impl<Prov: Provenance> std::fmt::Debug for PlaceTy<'_, Prov> {
|
||||
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,7 +230,7 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>)> {
|
||||
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)),
|
||||
}
|
||||
}
|
||||
@ -301,27 +267,27 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
||||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
mode: OffsetMode,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
Ok(match self.as_mplace_or_local() {
|
||||
Left(mplace) => mplace.offset_with_meta(offset, meta, layout, ecx)?.into(),
|
||||
Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(),
|
||||
Right((frame, local, old_offset)) => {
|
||||
debug_assert!(layout.is_sized(), "unsized locals should live in memory");
|
||||
assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway...
|
||||
let new_offset = ecx
|
||||
.data_layout()
|
||||
.offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?;
|
||||
PlaceTy {
|
||||
place: Place::Local {
|
||||
frame,
|
||||
local,
|
||||
offset: Some(Size::from_bytes(new_offset)),
|
||||
},
|
||||
align: self.align.restrict_for_offset(offset),
|
||||
layout,
|
||||
}
|
||||
// `Place::Local` are always in-bounds of their surrounding local, so we can just
|
||||
// check directly if this remains in-bounds. This cannot actually be violated since
|
||||
// projections are type-checked and bounds-checked.
|
||||
assert!(offset + layout.size <= self.layout.size);
|
||||
|
||||
let new_offset = Size::from_bytes(
|
||||
ecx.data_layout()
|
||||
.offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?,
|
||||
);
|
||||
|
||||
PlaceTy { place: Place::Local { frame, local, offset: Some(new_offset) }, layout }
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -339,9 +305,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
pub fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||
match self.op() {
|
||||
Operand::Indirect(mplace) => {
|
||||
Left(MPlaceTy { mplace: *mplace, layout: self.layout, align: self.align.unwrap() })
|
||||
}
|
||||
Operand::Indirect(mplace) => Left(MPlaceTy { mplace: *mplace, layout: self.layout }),
|
||||
Operand::Immediate(imm) => Right(ImmTy::from_immediate(*imm, self.layout)),
|
||||
}
|
||||
}
|
||||
@ -362,7 +326,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
||||
pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
|
||||
fn as_mplace_or_local(
|
||||
&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>>(
|
||||
&self,
|
||||
@ -374,10 +338,9 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_local(
|
||||
&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()
|
||||
.map_right(|(frame, local, offset)| (frame, local, offset, self.align, self.layout))
|
||||
.map_right(|(frame, local, offset)| (frame, local, offset, self.layout))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -393,8 +356,7 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_local(
|
||||
&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())
|
||||
}
|
||||
|
||||
@ -413,6 +375,25 @@ where
|
||||
Prov: Provenance,
|
||||
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.
|
||||
/// Alignment is just based on the type. This is the inverse of `mplace_to_ref()`.
|
||||
///
|
||||
@ -434,7 +415,8 @@ where
|
||||
|
||||
// `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.
|
||||
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.
|
||||
@ -464,7 +446,6 @@ where
|
||||
}
|
||||
|
||||
let mplace = self.ref_to_mplace(&val)?;
|
||||
self.check_mplace(&mplace)?;
|
||||
Ok(mplace)
|
||||
}
|
||||
|
||||
@ -477,8 +458,11 @@ where
|
||||
let (size, _align) = self
|
||||
.size_and_align_of_mplace(&mplace)?
|
||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||
// Due to packed places, only `mplace.align` matters.
|
||||
self.get_ptr_alloc(mplace.ptr(), size, mplace.align)
|
||||
// We check alignment separately, and *after* checking everything else.
|
||||
// If an access is both OOB and misaligned, we want to see the bounds error.
|
||||
let a = self.get_ptr_alloc(mplace.ptr(), size)?;
|
||||
self.check_misalign(mplace.mplace.misaligned, CheckAlignMsg::BasedOn)?;
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@ -490,19 +474,13 @@ where
|
||||
let (size, _align) = self
|
||||
.size_and_align_of_mplace(&mplace)?
|
||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||
// Due to packed places, only `mplace.align` matters.
|
||||
self.get_ptr_alloc_mut(mplace.ptr(), size, mplace.align)
|
||||
}
|
||||
|
||||
/// Check if this mplace is dereferenceable and sufficiently aligned.
|
||||
pub fn check_mplace(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
|
||||
let (size, _align) = self
|
||||
.size_and_align_of_mplace(&mplace)?
|
||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||
// Due to packed places, only `mplace.align` matters.
|
||||
let align = if M::enforce_alignment(self) { mplace.align } else { Align::ONE };
|
||||
self.check_ptr_access_align(mplace.ptr(), size, align, CheckInAllocMsg::DerefTest)?;
|
||||
Ok(())
|
||||
// We check alignment separately, and raise that error *after* checking everything else.
|
||||
// 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, CheckAlignMsg::BasedOn);
|
||||
let a = self.get_ptr_alloc_mut(mplace.ptr(), size)?;
|
||||
misalign_err?;
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
/// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
|
||||
@ -517,8 +495,8 @@ where
|
||||
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 layout = self.layout_of(array)?;
|
||||
assert_eq!(layout.size, mplace.layout.size);
|
||||
Ok((MPlaceTy { layout, ..*mplace }, len))
|
||||
let mplace = mplace.transmute(layout, self)?;
|
||||
Ok((mplace, len))
|
||||
}
|
||||
|
||||
/// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
|
||||
@ -554,7 +532,7 @@ where
|
||||
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
|
||||
@ -644,7 +622,7 @@ where
|
||||
// See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
|
||||
// but not factored as a separate function.
|
||||
let mplace = match dest.as_mplace_or_local() {
|
||||
Right((frame, local, offset, align, layout)) => {
|
||||
Right((frame, local, offset, layout)) => {
|
||||
if offset.is_some() {
|
||||
// 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
|
||||
@ -685,7 +663,7 @@ where
|
||||
}
|
||||
Operand::Indirect(mplace) => {
|
||||
// The local is in memory, go on below.
|
||||
MPlaceTy { mplace: *mplace, align, layout }
|
||||
MPlaceTy { mplace: *mplace, layout }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -694,7 +672,7 @@ where
|
||||
};
|
||||
|
||||
// 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.
|
||||
@ -704,7 +682,6 @@ where
|
||||
&mut self,
|
||||
value: Immediate<M::Provenance>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
align: Align,
|
||||
dest: MemPlace<M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Note that it is really important that the type here is the right one, and matches the
|
||||
@ -713,9 +690,7 @@ where
|
||||
// wrong type.
|
||||
|
||||
let tcx = *self.tcx;
|
||||
let Some(mut alloc) =
|
||||
self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout, align })?
|
||||
else {
|
||||
let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout })? else {
|
||||
// zero-sized access
|
||||
return Ok(());
|
||||
};
|
||||
@ -733,9 +708,6 @@ where
|
||||
alloc.write_scalar(alloc_range(Size::ZERO, size), scalar)
|
||||
}
|
||||
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 {
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
@ -764,7 +736,7 @@ where
|
||||
) -> InterpResult<'tcx> {
|
||||
let mplace = match dest.as_mplace_or_local() {
|
||||
Left(mplace) => mplace,
|
||||
Right((frame, local, offset, align, layout)) => {
|
||||
Right((frame, local, offset, layout)) => {
|
||||
if offset.is_some() {
|
||||
// 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
|
||||
@ -780,7 +752,7 @@ where
|
||||
}
|
||||
Operand::Indirect(mplace) => {
|
||||
// The local is in memory, go on below.
|
||||
MPlaceTy { mplace: *mplace, layout, align }
|
||||
MPlaceTy { mplace: *mplace, layout }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -873,7 +845,6 @@ where
|
||||
self.write_immediate_to_mplace_no_validate(
|
||||
*src_val,
|
||||
src.layout(),
|
||||
dest_mem.align,
|
||||
dest_mem.mplace,
|
||||
)
|
||||
};
|
||||
@ -900,14 +871,12 @@ where
|
||||
// type does not have Scalar/ScalarPair layout.
|
||||
// (Or as the `Assign` docs put it, assignments "not producing primitives" must be
|
||||
// non-overlapping.)
|
||||
self.mem_copy(
|
||||
src.ptr(),
|
||||
src.align,
|
||||
dest.ptr(),
|
||||
dest.align,
|
||||
dest_size,
|
||||
/*nonoverlapping*/ true,
|
||||
)
|
||||
// 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(src.ptr(), dest.ptr(), dest_size, /*nonoverlapping*/ true)?;
|
||||
self.check_misalign(src.mplace.misaligned, CheckAlignMsg::BasedOn)?;
|
||||
self.check_misalign(dest.mplace.misaligned, CheckAlignMsg::BasedOn)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensures that a place is in memory, and returns where it is.
|
||||
@ -941,7 +910,6 @@ where
|
||||
self.write_immediate_to_mplace_no_validate(
|
||||
local_val,
|
||||
local_layout,
|
||||
local_layout.align.abi,
|
||||
mplace.mplace,
|
||||
)?;
|
||||
}
|
||||
@ -956,7 +924,13 @@ where
|
||||
&mut Operand::Indirect(mplace) => mplace, // this already was an indirect local
|
||||
};
|
||||
if let Some(offset) = offset {
|
||||
whole_local.offset_with_meta_(offset, MemPlaceMeta::None, self)?
|
||||
// This offset is always inbounds, no need to check it again.
|
||||
whole_local.offset_with_meta_(
|
||||
offset,
|
||||
OffsetMode::Wrapping,
|
||||
MemPlaceMeta::None,
|
||||
self,
|
||||
)?
|
||||
} else {
|
||||
// Preserve wide place metadata, do not call `offset`.
|
||||
whole_local
|
||||
@ -965,7 +939,7 @@ where
|
||||
Place::Ptr(mplace) => mplace,
|
||||
};
|
||||
// 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(
|
||||
@ -978,7 +952,7 @@ where
|
||||
span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known")
|
||||
};
|
||||
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(
|
||||
@ -990,7 +964,7 @@ where
|
||||
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(
|
||||
&mut self,
|
||||
str: &str,
|
||||
@ -999,15 +973,8 @@ where
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||
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 mplace = MemPlace { ptr: ptr.into(), meta: MemPlaceMeta::Meta(meta) };
|
||||
|
||||
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 })
|
||||
let layout = self.layout_of(self.tcx.types.str_).unwrap();
|
||||
Ok(self.ptr_with_meta_to_mplace(ptr.into(), MemPlaceMeta::Meta(meta), layout))
|
||||
}
|
||||
|
||||
/// Writes the aggregate to the destination.
|
||||
@ -1046,7 +1013,7 @@ where
|
||||
let _ = self.tcx.global_alloc(raw.alloc_id);
|
||||
let ptr = self.global_base_pointer(Pointer::from(raw.alloc_id))?;
|
||||
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.
|
||||
@ -1062,12 +1029,10 @@ where
|
||||
let vtable = mplace.meta().unwrap_meta().to_pointer(self)?;
|
||||
let (ty, _) = self.get_ptr_vtable(vtable)?;
|
||||
let layout = self.layout_of(ty)?;
|
||||
|
||||
let mplace = MPlaceTy {
|
||||
mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace },
|
||||
layout,
|
||||
align: layout.align.abi,
|
||||
};
|
||||
// This is a kind of transmute, from a place with unsized type and metadata to
|
||||
// a place with sized type and no metadata.
|
||||
let mplace =
|
||||
MPlaceTy { mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace }, layout };
|
||||
Ok((mplace, vtable))
|
||||
}
|
||||
|
||||
@ -1099,10 +1064,10 @@ mod size_asserts {
|
||||
use super::*;
|
||||
use rustc_data_structures::static_assert_size;
|
||||
// tidy-alphabetical-start
|
||||
static_assert_size!(MemPlace, 40);
|
||||
static_assert_size!(MemPlace, 48);
|
||||
static_assert_size!(MemPlaceMeta, 24);
|
||||
static_assert_size!(MPlaceTy<'_>, 64);
|
||||
static_assert_size!(Place, 40);
|
||||
static_assert_size!(Place, 48);
|
||||
static_assert_size!(PlaceTy<'_>, 64);
|
||||
// tidy-alphabetical-end
|
||||
}
|
||||
|
@ -19,6 +19,15 @@ use rustc_target::abi::{self, VariantIdx};
|
||||
|
||||
use super::{InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar};
|
||||
|
||||
/// Describes the constraints placed on offset-projections.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum OffsetMode {
|
||||
/// The offset has to be inbounds, like `ptr::offset`.
|
||||
Inbounds,
|
||||
/// No constraints, just wrap around the edge of the address space.
|
||||
Wrapping,
|
||||
}
|
||||
|
||||
/// A thing that we can project into, and that has a layout.
|
||||
pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
||||
/// Get the layout.
|
||||
@ -53,12 +62,12 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
||||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
mode: OffsetMode,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Self>;
|
||||
|
||||
#[inline]
|
||||
fn offset<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
offset: Size,
|
||||
@ -66,10 +75,9 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
assert!(layout.is_sized());
|
||||
self.offset_with_meta(offset, MemPlaceMeta::None, layout, ecx)
|
||||
self.offset_with_meta(offset, OffsetMode::Inbounds, MemPlaceMeta::None, layout, ecx)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn transmute<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&self,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
@ -77,7 +85,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
assert!(self.layout().is_sized() && layout.is_sized());
|
||||
assert_eq!(self.layout().size, layout.size);
|
||||
self.offset_with_meta(Size::ZERO, MemPlaceMeta::None, layout, ecx)
|
||||
self.offset_with_meta(Size::ZERO, OffsetMode::Wrapping, MemPlaceMeta::None, layout, ecx)
|
||||
}
|
||||
|
||||
/// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for
|
||||
@ -104,7 +112,17 @@ impl<'tcx, 'a, Prov: Provenance, P: Projectable<'tcx, Prov>> ArrayIterator<'tcx,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, Option<(u64, P)>> {
|
||||
let Some(idx) = self.range.next() else { return Ok(None) };
|
||||
Ok(Some((idx, self.base.offset(self.stride * idx, self.field_layout, ecx)?)))
|
||||
// We use `Wrapping` here since the offset has already been checked when the iterator was created.
|
||||
Ok(Some((
|
||||
idx,
|
||||
self.base.offset_with_meta(
|
||||
self.stride * idx,
|
||||
OffsetMode::Wrapping,
|
||||
MemPlaceMeta::None,
|
||||
self.field_layout,
|
||||
ecx,
|
||||
)?,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,7 +177,7 @@ where
|
||||
(MemPlaceMeta::None, offset)
|
||||
};
|
||||
|
||||
base.offset_with_meta(offset, meta, field_layout, self)
|
||||
base.offset_with_meta(offset, OffsetMode::Inbounds, meta, field_layout, self)
|
||||
}
|
||||
|
||||
/// Downcasting to an enum variant.
|
||||
@ -248,6 +266,10 @@ where
|
||||
};
|
||||
let len = base.len(self)?;
|
||||
let field_layout = base.layout().field(self, 0);
|
||||
// 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)?;
|
||||
// Create the iterator.
|
||||
Ok(ArrayIterator { base, range: 0..len, stride, field_layout, _phantom: PhantomData })
|
||||
}
|
||||
|
||||
@ -305,7 +327,7 @@ where
|
||||
};
|
||||
let layout = self.layout_of(ty)?;
|
||||
|
||||
base.offset_with_meta(from_offset, meta, layout, self)
|
||||
base.offset_with_meta(from_offset, OffsetMode::Inbounds, meta, layout, self)
|
||||
}
|
||||
|
||||
/// Applying a general projection
|
||||
|
@ -206,15 +206,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
let elem_size = first.layout.size;
|
||||
let first_ptr = first.ptr();
|
||||
let rest_ptr = first_ptr.offset(elem_size, self)?;
|
||||
// For the alignment of `rest_ptr`, we crucially do *not* use `first.align` as
|
||||
// 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.
|
||||
// No alignment requirement since `copy_op` above already checked it.
|
||||
self.mem_copy_repeatedly(
|
||||
first_ptr,
|
||||
dest.align,
|
||||
rest_ptr,
|
||||
dest.align.restrict_for_offset(elem_size),
|
||||
elem_size,
|
||||
length - 1,
|
||||
/*nonoverlapping:*/ true,
|
||||
|
@ -1,6 +1,5 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use either::Either;
|
||||
use rustc_ast::ast::InlineAsmOptions;
|
||||
use rustc_middle::{
|
||||
mir,
|
||||
@ -729,13 +728,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
callee_ty: callee_fn_abi.ret.layout.ty
|
||||
});
|
||||
}
|
||||
// Ensure the return place is aligned and dereferenceable, and protect it for
|
||||
// in-place return value passing.
|
||||
if let Either::Left(mplace) = destination.as_mplace_or_local() {
|
||||
self.check_mplace(&mplace)?;
|
||||
} else {
|
||||
// Nothing to do for locals, they are always properly allocated and aligned.
|
||||
}
|
||||
// Protect return place for in-place return value passing.
|
||||
M::protect_in_place_function_argument(self, destination)?;
|
||||
|
||||
// Don't forget to mark "initially live" locals as live.
|
||||
|
@ -13,7 +13,7 @@ use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::mir::interpret::{
|
||||
ExpectedKind, InterpError, InvalidMetaKind, PointerKind, ValidationErrorInfo,
|
||||
ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, ValidationErrorInfo,
|
||||
ValidationErrorKind, ValidationErrorKind::*,
|
||||
};
|
||||
use rustc_middle::ty;
|
||||
@ -355,7 +355,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||
value: &OpTy<'tcx, M::Provenance>,
|
||||
ptr_kind: PointerKind,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Not using `deref_pointer` since we do the dereferenceable check ourselves below.
|
||||
// Not using `deref_pointer` since we want to use our `read_immediate` wrapper.
|
||||
let place = self.ecx.ref_to_mplace(&self.read_immediate(value, ptr_kind.into())?)?;
|
||||
// Handle wide pointers.
|
||||
// Check metadata early, for better diagnostics
|
||||
@ -378,18 +378,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||
.unwrap_or_else(|| (place.layout.size, place.layout.align.abi));
|
||||
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
|
||||
try_validation!(
|
||||
self.ecx.check_ptr_access_align(
|
||||
self.ecx.check_ptr_access(
|
||||
place.ptr(),
|
||||
size,
|
||||
align,
|
||||
CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
|
||||
),
|
||||
self.path,
|
||||
Ub(AlignmentCheckFailed { required, has }) => UnalignedPtr {
|
||||
ptr_kind,
|
||||
required_bytes: required.bytes(),
|
||||
found_bytes: has.bytes()
|
||||
},
|
||||
Ub(DanglingIntPointer(0, _)) => NullPtr { ptr_kind },
|
||||
Ub(DanglingIntPointer(i, _)) => DanglingPtrNoProvenance {
|
||||
ptr_kind,
|
||||
@ -405,6 +399,18 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||
ptr_kind,
|
||||
},
|
||||
);
|
||||
try_validation!(
|
||||
self.ecx.check_ptr_align(
|
||||
place.ptr(),
|
||||
align,
|
||||
),
|
||||
self.path,
|
||||
Ub(AlignmentCheckFailed(Misalignment { required, has }, _msg)) => UnalignedPtr {
|
||||
ptr_kind,
|
||||
required_bytes: required.bytes(),
|
||||
found_bytes: has.bytes()
|
||||
},
|
||||
);
|
||||
// Do not allow pointers to uninhabited types.
|
||||
if place.layout.abi.is_uninhabited() {
|
||||
let ty = place.layout.ty;
|
||||
@ -645,7 +651,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
||||
|
||||
#[inline(always)]
|
||||
fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
|
||||
&self.ecx
|
||||
self.ecx
|
||||
}
|
||||
|
||||
fn read_discriminant(
|
||||
@ -781,14 +787,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
||||
// Optimization: we just check the entire range at once.
|
||||
// NOTE: Keep this in sync with the handling of integer and float
|
||||
// types above, in `visit_primitive`.
|
||||
// In run-time mode, we accept pointers in here. This is actually more
|
||||
// permissive than a per-element check would be, e.g., we accept
|
||||
// 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");
|
||||
// No need for an alignment check here, this is not an actual memory access.
|
||||
let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size)?.expect("we already excluded size 0");
|
||||
|
||||
match alloc.get_bytes_strip_provenance() {
|
||||
// In the happy case, we needn't check anything else.
|
||||
|
@ -216,10 +216,8 @@ pub enum InvalidProgramInfo<'tcx> {
|
||||
}
|
||||
|
||||
/// Details of why a pointer had to be in-bounds.
|
||||
#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum CheckInAllocMsg {
|
||||
/// We are dereferencing a pointer (i.e., creating a place).
|
||||
DerefTest,
|
||||
/// We are access memory.
|
||||
MemoryAccessTest,
|
||||
/// We are doing pointer arithmetic.
|
||||
@ -230,7 +228,16 @@ pub enum CheckInAllocMsg {
|
||||
InboundsTest,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
|
||||
/// Details of which pointer is not aligned.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum CheckAlignMsg {
|
||||
/// The accessed pointer did not have proper alignment.
|
||||
AccessedPtr,
|
||||
/// The access ocurred with a place that was based on a misaligned pointer.
|
||||
BasedOn,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum InvalidMetaKind {
|
||||
/// Size of a `[T]` is too big
|
||||
SliceTooBig,
|
||||
@ -263,6 +270,13 @@ pub struct ScalarSizeMismatch {
|
||||
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 {
|
||||
($($ty:ty),*$(,)?) => {$(
|
||||
impl IntoDiagnosticArg for $ty {
|
||||
@ -324,7 +338,7 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
||||
/// Using an integer as a pointer in the wrong way.
|
||||
DanglingIntPointer(u64, CheckInAllocMsg),
|
||||
/// Used a pointer with bad alignment.
|
||||
AlignmentCheckFailed { required: Align, has: Align },
|
||||
AlignmentCheckFailed(Misalignment, CheckAlignMsg),
|
||||
/// Writing to read-only memory.
|
||||
WriteToReadOnly(AllocId),
|
||||
/// Trying to access the data behind a function pointer.
|
||||
|
@ -142,11 +142,12 @@ use crate::ty::GenericArgKind;
|
||||
use crate::ty::{self, Instance, Ty, TyCtxt};
|
||||
|
||||
pub use self::error::{
|
||||
struct_error, BadBytesAccess, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult,
|
||||
EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo,
|
||||
InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, PointerKind,
|
||||
ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo,
|
||||
UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
|
||||
struct_error, BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled,
|
||||
EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind,
|
||||
InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, InvalidProgramInfo,
|
||||
MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo,
|
||||
ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
|
||||
ValidationErrorKind,
|
||||
};
|
||||
|
||||
pub use self::value::Scalar;
|
||||
|
@ -986,18 +986,15 @@ pub type AssertMessage<'tcx> = AssertKind<Operand<'tcx>>;
|
||||
/// pointee's type. The resulting address is the address that was stored in the pointer. If the
|
||||
/// pointee type is unsized, the pointer additionally stored the value of the metadata.
|
||||
///
|
||||
/// Computing a place may cause UB. One possibility is that the pointer used for a `Deref` may not
|
||||
/// be suitably aligned. Another possibility is that the place is not in bounds, meaning it does not
|
||||
/// point to an actual allocation.
|
||||
///
|
||||
/// However, if this is actually UB and when the UB kicks in is undecided. This is being discussed
|
||||
/// in [UCG#319]. The options include that every place must obey those rules, that only some places
|
||||
/// must obey them, or that places impose no rules of their own.
|
||||
///
|
||||
/// [UCG#319]: https://github.com/rust-lang/unsafe-code-guidelines/issues/319
|
||||
///
|
||||
/// Rust currently requires that every place obey those two rules. This is checked by Miri and taken
|
||||
/// advantage of by codegen (via `gep inbounds`). That is possibly subject to change.
|
||||
/// The "validity invariant" of places is the same as that of raw pointers, meaning that e.g.
|
||||
/// `*ptr` on a dangling or unaligned pointer is never UB. (Later doing a load/store on that place
|
||||
/// or turning it into a reference can be UB though!) The only ways for a place computation can
|
||||
/// cause UB are:
|
||||
/// - On a `Deref` projection, we do an actual load of the inner place, with all the usual
|
||||
/// consequences (the inner place must be based on an aligned pointer, it must point to allocated
|
||||
/// memory, the aliasig model must allow reads, this must not be a data race).
|
||||
/// - For the projections that perform pointer arithmetic, the offset must in-bounds of an
|
||||
/// allocation (i.e., the preconditions of `ptr::offset` must be met).
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct Place<'tcx> {
|
||||
pub local: Local,
|
||||
|
@ -14,7 +14,7 @@ use log::trace;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_middle::mir::{Mutability, RetagKind};
|
||||
use rustc_middle::ty::{self, layout::HasParamEnv, Ty};
|
||||
use rustc_target::abi::{Abi, Align, Size};
|
||||
use rustc_target::abi::{Abi, Size};
|
||||
|
||||
use crate::borrow_tracker::{
|
||||
stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder},
|
||||
@ -616,7 +616,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||
) -> InterpResult<'tcx, Option<Provenance>> {
|
||||
let this = self.eval_context_mut();
|
||||
// Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
|
||||
this.check_ptr_access_align(place.ptr(), size, Align::ONE, CheckInAllocMsg::InboundsTest)?;
|
||||
this.check_ptr_access(place.ptr(), size, CheckInAllocMsg::InboundsTest)?;
|
||||
|
||||
// It is crucial that this gets called on all code paths, to ensure we track tag creation.
|
||||
let log_creation = |this: &MiriInterpCx<'mir, 'tcx>,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use log::trace;
|
||||
|
||||
use rustc_target::abi::{Abi, Align, Size};
|
||||
use rustc_target::abi::{Abi, Size};
|
||||
|
||||
use crate::borrow_tracker::{
|
||||
AccessKind, GlobalState, GlobalStateInner, ProtectorKind, RetagFields,
|
||||
@ -206,10 +206,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||
// Make sure the new permission makes sense as the initial permission of a fresh tag.
|
||||
assert!(new_perm.initial_state.is_initial());
|
||||
// Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
|
||||
this.check_ptr_access_align(
|
||||
this.check_ptr_access(
|
||||
place.ptr(),
|
||||
ptr_size,
|
||||
Align::ONE,
|
||||
CheckInAllocMsg::InboundsTest,
|
||||
)?;
|
||||
|
||||
|
@ -1017,11 +1017,9 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> {
|
||||
// even if the type they wrap would be less aligned (e.g. AtomicU64 on 32bit must
|
||||
// be 8-aligned).
|
||||
let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
|
||||
this.check_ptr_access_align(
|
||||
this.check_ptr_align(
|
||||
place.ptr(),
|
||||
place.layout.size,
|
||||
align,
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
)?;
|
||||
// Ensure the allocation is mutable. Even failing (read-only) compare_exchange need mutable
|
||||
// memory on many targets (i.e., they segfault if taht memory is mapped read-only), and
|
||||
|
@ -697,27 +697,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
|
||||
let this = self.eval_context_ref();
|
||||
let ptr = this.read_pointer(op)?;
|
||||
|
||||
let mplace = MPlaceTy::from_aligned_ptr(ptr, layout);
|
||||
|
||||
this.check_mplace(&mplace)?;
|
||||
|
||||
Ok(mplace)
|
||||
}
|
||||
|
||||
/// Deref' a pointer *without* checking that the place is dereferenceable.
|
||||
fn deref_pointer_unchecked(
|
||||
&self,
|
||||
val: &ImmTy<'tcx, Provenance>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
|
||||
let this = self.eval_context_ref();
|
||||
let mut mplace = this.ref_to_mplace(val)?;
|
||||
|
||||
mplace.layout = layout;
|
||||
mplace.align = layout.align.abi;
|
||||
|
||||
Ok(mplace)
|
||||
Ok(this.ptr_to_mplace(ptr, layout))
|
||||
}
|
||||
|
||||
/// Calculates the MPlaceTy given the offset and layout of an access on an operand
|
||||
@ -805,7 +785,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
loop {
|
||||
// FIXME: We are re-getting the allocation each time around the loop.
|
||||
// Would be nice if we could somehow "extend" an existing AllocRange.
|
||||
let alloc = this.get_ptr_alloc(ptr.offset(len, this)?, size1, Align::ONE)?.unwrap(); // not a ZST, so we will get a result
|
||||
let alloc = this.get_ptr_alloc(ptr.offset(len, this)?, size1)?.unwrap(); // not a ZST, so we will get a result
|
||||
let byte = alloc.read_integer(alloc_range(Size::ZERO, size1))?.to_u8()?;
|
||||
if byte == 0 {
|
||||
break;
|
||||
@ -845,13 +825,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
fn read_wide_str(&self, mut ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, Vec<u16>> {
|
||||
let this = self.eval_context_ref();
|
||||
let size2 = Size::from_bytes(2);
|
||||
let align2 = Align::from_bytes(2).unwrap();
|
||||
this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
|
||||
|
||||
let mut wchars = Vec::new();
|
||||
loop {
|
||||
// FIXME: We are re-getting the allocation each time around the loop.
|
||||
// Would be nice if we could somehow "extend" an existing AllocRange.
|
||||
let alloc = this.get_ptr_alloc(ptr, size2, align2)?.unwrap(); // not a ZST, so we will get a result
|
||||
let alloc = this.get_ptr_alloc(ptr, size2)?.unwrap(); // not a ZST, so we will get a result
|
||||
let wchar = alloc.read_integer(alloc_range(Size::ZERO, size2))?.to_u16()?;
|
||||
if wchar == 0 {
|
||||
break;
|
||||
@ -887,8 +867,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
// Store the UTF-16 string.
|
||||
let size2 = Size::from_bytes(2);
|
||||
let this = self.eval_context_mut();
|
||||
this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
|
||||
let mut alloc = this
|
||||
.get_ptr_alloc_mut(ptr, size2 * string_length, Align::from_bytes(2).unwrap())?
|
||||
.get_ptr_alloc_mut(ptr, size2 * string_length)?
|
||||
.unwrap(); // not a ZST, so we will get a result
|
||||
for (offset, wchar) in wide_str.iter().copied().chain(iter::once(0x0000)).enumerate() {
|
||||
let offset = u64::try_from(offset).unwrap();
|
||||
|
@ -1285,6 +1285,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
|
||||
// We do need to write `uninit` so that even after the call ends, the former contents of
|
||||
// this place cannot be observed any more. We do the write after retagging so that for
|
||||
// Tree Borrows, this is considered to activate the new tag.
|
||||
// Conveniently this also ensures that the place actually points to suitable memory.
|
||||
ecx.write_uninit(&protected_place)?;
|
||||
// Now we throw away the protected place, ensuring its tag is never used again.
|
||||
Ok(())
|
||||
|
@ -807,9 +807,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
|
||||
this.mem_copy(
|
||||
ptr_src,
|
||||
Align::ONE,
|
||||
ptr_dest,
|
||||
Align::ONE,
|
||||
Size::from_bytes(n),
|
||||
true,
|
||||
)?;
|
||||
@ -830,9 +828,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
let n = this.read_c_str(ptr_src)?.len().checked_add(1).unwrap();
|
||||
this.mem_copy(
|
||||
ptr_src,
|
||||
Align::ONE,
|
||||
ptr_dest,
|
||||
Align::ONE,
|
||||
Size::from_bytes(n),
|
||||
true,
|
||||
)?;
|
||||
|
@ -13,7 +13,7 @@ use log::trace;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_target::abi::{Align, Size};
|
||||
use rustc_target::abi::Size;
|
||||
|
||||
use crate::shims::os_str::bytes_to_os_str;
|
||||
use crate::*;
|
||||
@ -756,10 +756,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
trace!("Reading from FD {}, size {}", fd, count);
|
||||
|
||||
// Check that the *entire* buffer is actually valid memory.
|
||||
this.check_ptr_access_align(
|
||||
this.check_ptr_access(
|
||||
buf,
|
||||
Size::from_bytes(count),
|
||||
Align::ONE,
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
)?;
|
||||
|
||||
@ -810,10 +809,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
// Isolation check is done via `FileDescriptor` trait.
|
||||
|
||||
// Check that the *entire* buffer is actually valid memory.
|
||||
this.check_ptr_access_align(
|
||||
this.check_ptr_access(
|
||||
buf,
|
||||
Size::from_bytes(count),
|
||||
Align::ONE,
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
)?;
|
||||
|
||||
@ -1370,7 +1368,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
("d_reclen", size.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)?;
|
||||
|
@ -34,7 +34,7 @@ pub fn futex<'tcx>(
|
||||
|
||||
let thread = this.get_active_thread();
|
||||
// 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 futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG");
|
||||
@ -85,9 +85,8 @@ pub fn futex<'tcx>(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// `read_timespec` will check the place when it is not null.
|
||||
let timeout = this.deref_pointer_unchecked(
|
||||
&this.read_immediate(&args[3])?,
|
||||
let timeout = this.deref_pointer_as(
|
||||
&args[3],
|
||||
this.libc_ty_layout("timespec"),
|
||||
)?;
|
||||
let timeout_time = if this.ptr_is_null(timeout.ptr())? {
|
||||
|
@ -322,8 +322,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
|
||||
let layout = this.machine.layouts.uint(size).unwrap();
|
||||
let futex_val = this
|
||||
.read_scalar_atomic(&MPlaceTy::from_aligned_ptr(ptr, layout), AtomicReadOrd::Relaxed)?;
|
||||
let compare_val = this.read_scalar(&MPlaceTy::from_aligned_ptr(compare, layout))?;
|
||||
.read_scalar_atomic(&this.ptr_to_mplace(ptr, layout), AtomicReadOrd::Relaxed)?;
|
||||
let compare_val = this.read_scalar(&this.ptr_to_mplace(compare, layout))?;
|
||||
|
||||
if futex_val == compare_val {
|
||||
// If the values are the same, we have to block.
|
||||
|
@ -1,6 +1,5 @@
|
||||
use rustc_middle::mir;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::abi::Align;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use super::horizontal_bin_op;
|
||||
@ -76,9 +75,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
|
||||
|
||||
this.mem_copy(
|
||||
src_ptr,
|
||||
Align::ONE,
|
||||
dest.ptr(),
|
||||
Align::ONE,
|
||||
dest.layout.size,
|
||||
/*nonoverlapping*/ true,
|
||||
)?;
|
||||
|
@ -181,6 +181,7 @@ regexes! {
|
||||
r"0x[0-9a-fA-F]+[0-9a-fA-F]{2,2}" => "$$HEX",
|
||||
// erase specific alignments
|
||||
"alignment [0-9]+" => "alignment ALIGN",
|
||||
"[0-9]+ byte alignment but found [0-9]+" => "ALIGN byte alignment but found ALIGN",
|
||||
// erase thread caller ids
|
||||
r"call [0-9]+" => "call ID",
|
||||
// erase platform module paths
|
||||
|
@ -13,11 +13,11 @@ LL | libc::munmap(ptr, 4096);
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at $DIR/mmap_use_after_munmap.rs:LL:CC
|
||||
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/mmap_use_after_munmap.rs:LL:CC
|
||||
|
|
||||
LL | let _x = *(ptr as *mut u8);
|
||||
| ^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
| ^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/reallocate-change-alloc.rs:LL:CC
|
||||
|
|
||||
LL | let _z = *x;
|
||||
| ^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
| ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/thread_local_static_dealloc.rs:LL:CC
|
||||
|
|
||||
LL | let _val = *dangling_ptr.0;
|
||||
| ^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
| ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -2,7 +2,7 @@ error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/const-ub-checks.rs:LL:CC
|
||||
|
|
||||
LL | ptr.read();
|
||||
| ^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
| ^^^^^^^^^^ accessing memory based on pointer with alignment ALIGN, but alignment ALIGN is required
|
||||
|
||||
note: erroneous constant encountered
|
||||
--> $DIR/const-ub-checks.rs:LL:CC
|
||||
|
@ -1,12 +0,0 @@
|
||||
// Make sure we find these even with many checks disabled.
|
||||
//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation
|
||||
use std::ptr;
|
||||
|
||||
fn main() {
|
||||
let p = {
|
||||
let b = Box::new(42);
|
||||
&*b as *const i32
|
||||
};
|
||||
let x = unsafe { ptr::addr_of!(*p) }; //~ ERROR: has been freed
|
||||
panic!("this should never print: {:?}", x);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/dangling_pointer_addr_of.rs:LL:CC
|
||||
|
|
||||
LL | let x = unsafe { ptr::addr_of!(*p) };
|
||||
| ^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
help: ALLOC was allocated here:
|
||||
--> $DIR/dangling_pointer_addr_of.rs:LL:CC
|
||||
|
|
||||
LL | let b = Box::new(42);
|
||||
| ^^^^^^^^^^^^
|
||||
help: ALLOC was deallocated here:
|
||||
--> $DIR/dangling_pointer_addr_of.rs:LL:CC
|
||||
|
|
||||
LL | };
|
||||
| ^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `main` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
|
||||
= note: this error originates in the macro `ptr::addr_of` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/dangling_pointer_deref.rs:LL:CC
|
||||
|
|
||||
LL | let x = unsafe { *p };
|
||||
| ^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
| ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -4,10 +4,9 @@
|
||||
fn main() {
|
||||
let p = {
|
||||
let b = Box::new(42);
|
||||
&*b as *const i32
|
||||
&*b as *const i32 as *const (u8, u8, u8, u8)
|
||||
};
|
||||
unsafe {
|
||||
let _ = *p; //~ ERROR: has been freed
|
||||
let _ = (*p).1; //~ ERROR: out-of-bounds pointer arithmetic
|
||||
}
|
||||
panic!("this should never print");
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/dangling_pointer_project_underscore.rs:LL:CC
|
||||
|
|
||||
LL | let _ = *p;
|
||||
| ^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
LL | let _ = (*p).1;
|
||||
| ^^^^^^ out-of-bounds pointer arithmetic: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/dangling_primitive.rs:LL:CC
|
||||
|
|
||||
LL | dbg!(*ptr);
|
||||
| ^^^^^^^^^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
| ^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/dangling_zst_deref.rs:LL:CC
|
||||
|
|
||||
LL | let _x = unsafe { *p };
|
||||
| ^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
| ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: 0x10[noalloc] is a dangling pointer (it has no provenance)
|
||||
error: Undefined Behavior: out-of-bounds pointer use: 0x10[noalloc] is a dangling pointer (it has no provenance)
|
||||
--> $DIR/deref-invalid-ptr.rs:LL:CC
|
||||
|
|
||||
LL | let _y = unsafe { &*x as *const u32 };
|
||||
| ^^^ dereferencing pointer failed: 0x10[noalloc] is a dangling pointer (it has no provenance)
|
||||
| ^^^ out-of-bounds pointer use: 0x10[noalloc] is a dangling pointer (it has no provenance)
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +0,0 @@
|
||||
// Deref a raw ptr to access a field of a large struct, where the field
|
||||
// is allocated but not the entire struct is.
|
||||
fn main() {
|
||||
let x = (1, 13);
|
||||
let xptr = &x as *const _ as *const (i32, i32, i32);
|
||||
let val = unsafe { (*xptr).1 }; //~ ERROR: pointer to 12 bytes starting at offset 0 is out-of-bounds
|
||||
assert_eq!(val, 13);
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has size 8, so pointer to 12 bytes starting at offset 0 is out-of-bounds
|
||||
--> $DIR/deref-partially-dangling.rs:LL:CC
|
||||
|
|
||||
LL | let val = unsafe { (*xptr).1 };
|
||||
| ^^^^^^^^^ dereferencing pointer failed: ALLOC has size 8, so pointer to 12 bytes starting at offset 0 is out-of-bounds
|
||||
|
|
||||
= 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
|
||||
help: ALLOC was allocated here:
|
||||
--> $DIR/deref-partially-dangling.rs:LL:CC
|
||||
|
|
||||
LL | let x = (1, 13);
|
||||
| ^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `main` at $DIR/deref-partially-dangling.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,16 @@
|
||||
// Should be caught even without retagging
|
||||
//@compile-flags: -Zmiri-disable-stacked-borrows
|
||||
#![feature(strict_provenance)]
|
||||
use std::ptr::{addr_of_mut, self};
|
||||
|
||||
// Deref'ing a dangling raw pointer is fine, but for a dangling box it is not.
|
||||
// We do this behind a pointer indirection to potentially fool validity checking.
|
||||
// (This test relies on the `deref_copy` pass that lowers `**ptr` to materialize the intermediate pointer.)
|
||||
|
||||
fn main() {
|
||||
let mut inner = ptr::invalid::<i32>(24);
|
||||
let outer = addr_of_mut!(inner).cast::<Box<i32>>();
|
||||
// Now `outer` is a pointer to a dangling reference.
|
||||
// Deref'ing that should be UB.
|
||||
let _val = unsafe { addr_of_mut!(**outer) }; //~ERROR: dangling box
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
error: Undefined Behavior: constructing invalid value: encountered a dangling box (0x18[noalloc] has no provenance)
|
||||
--> $DIR/deref_dangling_box.rs:LL:CC
|
||||
|
|
||||
LL | let _val = unsafe { addr_of_mut!(**outer) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (0x18[noalloc] has no provenance)
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
|
||||
= note: this error originates in the macro `addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,16 @@
|
||||
// Should be caught even without retagging
|
||||
//@compile-flags: -Zmiri-disable-stacked-borrows
|
||||
#![feature(strict_provenance)]
|
||||
use std::ptr::{addr_of_mut, self};
|
||||
|
||||
// Deref'ing a dangling raw pointer is fine, but for a dangling reference it is not.
|
||||
// We do this behind a pointer indirection to potentially fool validity checking.
|
||||
// (This test relies on the `deref_copy` pass that lowers `**ptr` to materialize the intermediate pointer.)
|
||||
|
||||
fn main() {
|
||||
let mut inner = ptr::invalid::<i32>(24);
|
||||
let outer = addr_of_mut!(inner).cast::<&'static mut i32>();
|
||||
// Now `outer` is a pointer to a dangling reference.
|
||||
// Deref'ing that should be UB.
|
||||
let _val = unsafe { addr_of_mut!(**outer) }; //~ERROR: dangling reference
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
error: Undefined Behavior: constructing invalid value: encountered a dangling reference (0x18[noalloc] has no provenance)
|
||||
--> $DIR/deref_dangling_ref.rs:LL:CC
|
||||
|
|
||||
LL | let _val = unsafe { addr_of_mut!(**outer) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x18[noalloc] has no provenance)
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
|
||||
= note: this error originates in the macro `addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1,13 +1,13 @@
|
||||
// should find the bug even without these
|
||||
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows
|
||||
// should find the bug even without retagging
|
||||
//@compile-flags: -Zmiri-disable-stacked-borrows
|
||||
|
||||
struct SliceWithHead(u8, [u8]);
|
||||
|
||||
fn main() {
|
||||
let buf = [0u32; 1];
|
||||
// We craft a wide pointer `*const SliceWithHead` such that the unsized tail is only partially allocated.
|
||||
// That should be UB, as the reference is not fully dereferencable.
|
||||
// That should lead to UB, as the reference is not fully dereferenceable.
|
||||
let ptr: *const SliceWithHead = unsafe { std::mem::transmute((&buf, 4usize)) };
|
||||
// Re-borrow that. This should be UB.
|
||||
let _ptr = unsafe { &*ptr }; //~ ERROR: pointer to 5 bytes starting at offset 0 is out-of-bounds
|
||||
let _ptr = unsafe { &*ptr }; //~ ERROR: encountered a dangling reference (going beyond the bounds of its allocation)
|
||||
}
|
||||
|
@ -1,17 +1,12 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has size 4, so pointer to 5 bytes starting at offset 0 is out-of-bounds
|
||||
error: Undefined Behavior: constructing invalid value: encountered a dangling reference (going beyond the bounds of its allocation)
|
||||
--> $DIR/dyn_size.rs:LL:CC
|
||||
|
|
||||
LL | let _ptr = unsafe { &*ptr };
|
||||
| ^^^^^ dereferencing pointer failed: ALLOC has size 4, so pointer to 5 bytes starting at offset 0 is out-of-bounds
|
||||
| ^^^^^ constructing invalid value: encountered a dangling reference (going beyond the bounds of its allocation)
|
||||
|
|
||||
= 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
|
||||
help: ALLOC was allocated here:
|
||||
--> $DIR/dyn_size.rs:LL:CC
|
||||
|
|
||||
LL | let buf = [0u32; 1];
|
||||
| ^^^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at $DIR/dyn_size.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
|
||||
error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
|
||||
--> $DIR/maybe_null_pointer_deref_zst.rs:LL:CC
|
||||
|
|
||||
LL | let _x: () = unsafe { *ptr };
|
||||
| ^^^^ dereferencing pointer failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
|
||||
| ^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
|
||||
error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
|
||||
--> $DIR/maybe_null_pointer_write_zst.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { *ptr = zst_val };
|
||||
| ^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
|
||||
| ^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
|
||||
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
|
||||
--> $DIR/null_pointer_deref.rs:LL:CC
|
||||
|
|
||||
LL | let x: i32 = unsafe { *std::ptr::null() };
|
||||
| ^^^^^^^^^^^^^^^^^ dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
|
||||
| ^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
|
||||
|
|
||||
= 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
|
||||
|
@ -1,5 +1,5 @@
|
||||
#[allow(deref_nullptr)]
|
||||
fn main() {
|
||||
let x: () = unsafe { *std::ptr::null() }; //~ ERROR: dereferencing pointer failed: null pointer is a dangling pointer
|
||||
let x: () = unsafe { *std::ptr::null() }; //~ ERROR: memory access failed: null pointer is a dangling pointer
|
||||
panic!("this should never print: {:?}", x);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
|
||||
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
|
||||
--> $DIR/null_pointer_deref_zst.rs:LL:CC
|
||||
|
|
||||
LL | let x: () = unsafe { *std::ptr::null() };
|
||||
| ^^^^^^^^^^^^^^^^^ dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
|
||||
| ^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
|
||||
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
|
||||
--> $DIR/null_pointer_write.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { *std::ptr::null_mut() = 0i32 };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
|
||||
|
|
||||
= 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
|
||||
|
@ -4,5 +4,5 @@ fn main() {
|
||||
// Also not assigning directly as that's array initialization, not assignment.
|
||||
let zst_val = [1u8; 0];
|
||||
unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) };
|
||||
//~^ERROR: dereferencing pointer failed: null pointer is a dangling pointer
|
||||
//~^ERROR: memory access failed: null pointer is a dangling pointer
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
|
||||
error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
|
||||
--> $DIR/null_pointer_write_zst.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
|
||||
|
|
||||
= 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
|
||||
|
@ -0,0 +1,12 @@
|
||||
// Make sure we find these even with many checks disabled.
|
||||
//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation
|
||||
use std::ptr::addr_of;
|
||||
|
||||
fn main() {
|
||||
let v = 0u32;
|
||||
let ptr = addr_of!(v).cast::<(u32, u32, u32)>();
|
||||
unsafe {
|
||||
let _field = addr_of!((*ptr).1); // still just in-bounds
|
||||
let _field = addr_of!((*ptr).2); //~ ERROR: out-of-bounds pointer arithmetic
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
|
||||
--> $DIR/out_of_bounds_project.rs:LL:CC
|
||||
|
|
||||
LL | let _field = addr_of!((*ptr).2);
|
||||
| ^^^^^^^^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
|
||||
|
|
||||
= 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
|
||||
help: ALLOC was allocated here:
|
||||
--> $DIR/out_of_bounds_project.rs:LL:CC
|
||||
|
|
||||
LL | let v = 0u32;
|
||||
| ^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `main` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
|
||||
= note: this error originates in the macro `addr_of` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,8 @@
|
||||
#![feature(pointer_byte_offsets)]
|
||||
|
||||
fn main() {
|
||||
let v: Vec<u16> = vec![1, 2];
|
||||
// This read is also misaligned. We make sure that the OOB message has priority.
|
||||
let x = unsafe { *v.as_ptr().wrapping_byte_add(5) }; //~ ERROR: out-of-bounds
|
||||
panic!("this should never print: {}", x);
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has size 2, so pointer to 1 byte starting at offset 5 is out-of-bounds
|
||||
--> $DIR/out_of_bounds_read1.rs:LL:CC
|
||||
error: Undefined Behavior: memory access failed: ALLOC has size 4, so pointer to 2 bytes starting at offset 5 is out-of-bounds
|
||||
--> $DIR/out_of_bounds_read.rs:LL:CC
|
||||
|
|
||||
LL | let x = unsafe { *v.as_ptr().wrapping_offset(5) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has size 2, so pointer to 1 byte starting at offset 5 is out-of-bounds
|
||||
LL | let x = unsafe { *v.as_ptr().wrapping_byte_add(5) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 4, so pointer to 2 bytes starting at offset 5 is out-of-bounds
|
||||
|
|
||||
= 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
|
||||
help: ALLOC was allocated here:
|
||||
--> $DIR/out_of_bounds_read1.rs:LL:CC
|
||||
--> $DIR/out_of_bounds_read.rs:LL:CC
|
||||
|
|
||||
LL | let v: Vec<u8> = vec![1, 2];
|
||||
| ^^^^^^^^^^
|
||||
LL | let v: Vec<u16> = vec![1, 2];
|
||||
| ^^^^^^^^^^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `main` at $DIR/out_of_bounds_read1.rs:LL:CC
|
||||
= note: inside `main` at $DIR/out_of_bounds_read.rs:LL:CC
|
||||
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
@ -1,5 +0,0 @@
|
||||
fn main() {
|
||||
let v: Vec<u8> = vec![1, 2];
|
||||
let x = unsafe { *v.as_ptr().wrapping_offset(5) }; //~ ERROR: out-of-bounds
|
||||
panic!("this should never print: {}", x);
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
fn main() {
|
||||
let v: Vec<u8> = vec![1, 2];
|
||||
let x = unsafe { *v.as_ptr().wrapping_offset(5) }; //~ ERROR: out-of-bounds
|
||||
panic!("this should never print: {}", x);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
#![feature(pointer_byte_offsets)]
|
||||
|
||||
fn main() {
|
||||
let mut v: Vec<u16> = vec![1, 2];
|
||||
// This read is also misaligned. We make sure that the OOB message has priority.
|
||||
unsafe { *v.as_mut_ptr().wrapping_byte_add(5) = 0 }; //~ ERROR: out-of-bounds
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has size 2, so pointer to 1 byte starting at offset 5 is out-of-bounds
|
||||
--> $DIR/out_of_bounds_read2.rs:LL:CC
|
||||
error: Undefined Behavior: memory access failed: ALLOC has size 4, so pointer to 2 bytes starting at offset 5 is out-of-bounds
|
||||
--> $DIR/out_of_bounds_write.rs:LL:CC
|
||||
|
|
||||
LL | let x = unsafe { *v.as_ptr().wrapping_offset(5) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has size 2, so pointer to 1 byte starting at offset 5 is out-of-bounds
|
||||
LL | unsafe { *v.as_mut_ptr().wrapping_byte_add(5) = 0 };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 4, so pointer to 2 bytes starting at offset 5 is out-of-bounds
|
||||
|
|
||||
= 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
|
||||
help: ALLOC was allocated here:
|
||||
--> $DIR/out_of_bounds_read2.rs:LL:CC
|
||||
--> $DIR/out_of_bounds_write.rs:LL:CC
|
||||
|
|
||||
LL | let v: Vec<u8> = vec![1, 2];
|
||||
| ^^^^^^^^^^
|
||||
LL | let mut v: Vec<u16> = vec![1, 2];
|
||||
| ^^^^^^^^^^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `main` at $DIR/out_of_bounds_read2.rs:LL:CC
|
||||
= note: inside `main` at $DIR/out_of_bounds_write.rs:LL:CC
|
||||
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/stack_temporary.rs:LL:CC
|
||||
|
|
||||
LL | let val = *x;
|
||||
| ^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
| ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
error: Undefined Behavior: out-of-bounds pointer use: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
--> $DIR/storage_dead_dangling.rs:LL:CC
|
||||
|
|
||||
LL | let _ = unsafe { &mut *(LEAK as *mut i32) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: 0x2c[noalloc] is a dangling pointer (it has no provenance)
|
||||
error: Undefined Behavior: memory access failed: 0x2c[noalloc] is a dangling pointer (it has no provenance)
|
||||
--> $DIR/wild_pointer_deref.rs:LL:CC
|
||||
|
|
||||
LL | let x = unsafe { *p };
|
||||
| ^^ dereferencing pointer failed: 0x2c[noalloc] is a dangling pointer (it has no provenance)
|
||||
| ^^ memory access failed: 0x2c[noalloc] is a dangling pointer (it has no provenance)
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/dealloc_read_race2.rs:LL:CC
|
||||
|
|
||||
LL | *ptr.0
|
||||
| ^^^^^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
| ^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/dealloc_write_race2.rs:LL:CC
|
||||
|
|
||||
LL | *ptr.0 = 2;
|
||||
| ^^^^^^^^^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
| ^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/environ-gets-deallocated.rs:LL:CC
|
||||
|
|
||||
LL | let _y = unsafe { *pointer };
|
||||
| ^^^^^^^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
| ^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -2,7 +2,7 @@ fn f() {}
|
||||
|
||||
fn main() {
|
||||
let x: u8 = unsafe {
|
||||
*std::mem::transmute::<fn(), *const u8>(f) //~ ERROR: out-of-bounds
|
||||
*std::mem::transmute::<fn(), *const u8>(f) //~ ERROR: contains a function
|
||||
};
|
||||
panic!("this should never print: {}", x);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds
|
||||
error: Undefined Behavior: accessing ALLOC which contains a function
|
||||
--> $DIR/deref_fn_ptr.rs:LL:CC
|
||||
|
|
||||
LL | *std::mem::transmute::<fn(), *const u8>(f)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing ALLOC which contains a function
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/generator-pinned-moved.rs:LL:CC
|
||||
|
|
||||
LL | *num += 1;
|
||||
| ^^^^^^^^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
| ^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds
|
||||
error: Undefined Behavior: memory access failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds
|
||||
--> $DIR/simd-gather.rs:LL:CC
|
||||
|
|
||||
LL | let _result = Simd::gather_select_unchecked(&vec, Mask::splat(true), idxs, Simd::splat(0));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds
|
||||
|
|
||||
= 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
|
||||
|
@ -1,4 +1,4 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds
|
||||
error: Undefined Behavior: memory access failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds
|
||||
--> $DIR/simd-scatter.rs:LL:CC
|
||||
|
|
||||
LL | / Simd::from_array([-27, 82, -41, 124]).scatter_select_unchecked(
|
||||
@ -7,7 +7,7 @@ LL | | &mut vec,
|
||||
LL | | Mask::splat(true),
|
||||
LL | | idxs,
|
||||
LL | | );
|
||||
| |_________^ dereferencing pointer failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds
|
||||
| |_________^ memory access failed: ALLOC has size 9, so pointer to 1 byte starting at offset 9 is out-of-bounds
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
error: Undefined Behavior: memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
--> $DIR/pointer_partial_overwrite.rs:LL:CC
|
||||
|
|
||||
LL | let x = *p;
|
||||
| ^^ dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
| ^^ memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
|
|
||||
= 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
|
||||
|
@ -13,7 +13,7 @@ unsafe fn deref(left: *const u8, right: *const u8) {
|
||||
// The compiler is allowed to replace `left_int` by `right_int` here...
|
||||
let left_ptr: *const u8 = mem::transmute(left_int);
|
||||
// ...which however means here it could be dereferencing the wrong pointer.
|
||||
let _val = *left_ptr; //~ERROR: dereferencing pointer failed
|
||||
let _val = *left_ptr; //~ERROR: dangling pointer
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
error: Undefined Behavior: memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
--> $DIR/provenance_transmute.rs:LL:CC
|
||||
|
|
||||
LL | let _val = *left_ptr;
|
||||
| ^^^^^^^^^ dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
| ^^^^^^^^^ memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
error: Undefined Behavior: memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
--> $DIR/ptr_int_unexposed.rs:LL:CC
|
||||
|
|
||||
LL | assert_eq!(unsafe { *ptr }, 3);
|
||||
| ^^^^ dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
| ^^^^ memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
error: Undefined Behavior: memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
--> $DIR/ptr_invalid.rs:LL:CC
|
||||
|
|
||||
LL | let _val = unsafe { *xptr_invalid };
|
||||
| ^^^^^^^^^^^^^ dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
| ^^^^^^^^^^^^^ memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
error: Undefined Behavior: out-of-bounds pointer use: ALLOC has been freed, so this pointer is dangling
|
||||
--> $DIR/rc_as_ptr.rs:LL:CC
|
||||
|
|
||||
LL | assert_eq!(42, **unsafe { &*Weak::as_ptr(&weak) });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: ALLOC has been freed, so this pointer is dangling
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: ALLOC has been freed, so this pointer is dangling
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
error: Undefined Behavior: memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
--> $DIR/reading_half_a_pointer.rs:LL:CC
|
||||
|
|
||||
LL | let _val = *x;
|
||||
| ^^ dereferencing pointer failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
| ^^ memory access failed: $HEX[noalloc] is a dangling pointer (it has no provenance)
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
error: Undefined Behavior: accessing memory based on pointer with alignment ALIGN, but alignment ALIGN is required
|
||||
--> $DIR/alignment.rs:LL:CC
|
||||
|
|
||||
LL | *(x_ptr as *mut u32) = 42; *(x_ptr.add(1) as *mut u32) = 42;
|
||||
| ^ accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
| ^ accessing memory based on pointer 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
|
||||
|
@ -13,7 +13,7 @@ struct PartialDrop {
|
||||
b: u8,
|
||||
}
|
||||
|
||||
//@error-in-other-file: /alignment 2 is required/
|
||||
//@error-in-other-file: /required 2 byte alignment/
|
||||
fn main() {
|
||||
unsafe {
|
||||
// Create an unaligned pointer
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
error: Undefined Behavior: constructing invalid value: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN)
|
||||
--> RUSTLIB/core/src/ptr/mod.rs:LL:CC
|
||||
|
|
||||
LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN)
|
||||
|
|
||||
= 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
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: constructing invalid value: encountered an unaligned reference (required 256 byte alignment but found $ALIGN)
|
||||
error: Undefined Behavior: constructing invalid value: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN)
|
||||
--> $DIR/dyn_alignment.rs:LL:CC
|
||||
|
|
||||
LL | let _ptr = &*ptr;
|
||||
| ^^^^^ constructing invalid value: encountered an unaligned reference (required 256 byte alignment but found $ALIGN)
|
||||
| ^^^^^ constructing invalid value: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN)
|
||||
|
|
||||
= 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
|
||||
|
@ -8,7 +8,7 @@ pub struct S {
|
||||
}
|
||||
|
||||
unsafe fn foo(x: *const S) -> u8 {
|
||||
unsafe { (*x).x } //~ERROR: accessing memory with alignment 1, but alignment 4 is required
|
||||
unsafe { (*x).x } //~ERROR: based on pointer with alignment 1, but alignment 4 is required
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
error: Undefined Behavior: accessing memory based on pointer with alignment ALIGN, but alignment ALIGN is required
|
||||
--> $DIR/field_requires_parent_struct_alignment.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { (*x).x }
|
||||
| ^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
| ^^^^^^ accessing memory based on pointer 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
|
||||
|
@ -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: based on pointer 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 based on pointer with alignment ALIGN, but alignment ALIGN is required
|
||||
--> $DIR/field_requires_parent_struct_alignment2.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { (*x).packed.x }
|
||||
| ^^^^^^^^^^^^^ accessing memory based on pointer 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
|
||||
|
@ -12,6 +12,6 @@ fn main() {
|
||||
// Manually make sure the pointer is properly aligned.
|
||||
let base_addr_aligned = if base_addr % 2 == 0 { base_addr } else { base_addr + 1 };
|
||||
let u16_ptr = base_addr_aligned as *mut u16;
|
||||
unsafe { *u16_ptr = 2 }; //~ERROR: memory with alignment 1, but alignment 2 is required
|
||||
unsafe { *u16_ptr = 2 }; //~ERROR: with alignment 1, but alignment 2 is required
|
||||
println!("{:?}", x);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
error: Undefined Behavior: accessing memory based on pointer with alignment ALIGN, but alignment ALIGN is required
|
||||
--> $DIR/intptrcast_alignment_check.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { *u16_ptr = 2 };
|
||||
| ^^^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
| ^^^^^^^^^^^^ accessing memory based on pointer with alignment ALIGN, but alignment ALIGN is required
|
||||
|
|
||||
= help: this usually indicates that your program performed an invalid operation and caused Undefined Behavior
|
||||
= help: but due to `-Zmiri-symbolic-alignment-check`, alignment errors can also be false positives
|
||||
|
@ -1,5 +1,5 @@
|
||||
// This should fail even without validation/SB
|
||||
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Cdebug-assertions=no
|
||||
// This should fail even without SB
|
||||
//@compile-flags: -Zmiri-disable-stacked-borrows -Cdebug-assertions=no
|
||||
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
@ -12,15 +12,14 @@ struct Foo {
|
||||
}
|
||||
|
||||
unsafe fn raw_to_ref<'a, T>(x: *const T) -> &'a T {
|
||||
mem::transmute(x)
|
||||
mem::transmute(x) //~ERROR: required 4 byte alignment
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Try many times as this might work by chance.
|
||||
for _ in 0..20 {
|
||||
let foo = Foo { x: 42, y: 99 };
|
||||
// There seem to be implicit reborrows, which make the error already appear here
|
||||
let p: &i32 = unsafe { raw_to_ref(ptr::addr_of!(foo.x)) }; //~ERROR: alignment 4 is required
|
||||
let p: &i32 = unsafe { raw_to_ref(ptr::addr_of!(foo.x)) };
|
||||
let i = *p;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,18 @@
|
||||
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
error: Undefined Behavior: constructing invalid value: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN)
|
||||
--> $DIR/reference_to_packed.rs:LL:CC
|
||||
|
|
||||
LL | let p: &i32 = unsafe { raw_to_ref(ptr::addr_of!(foo.x)) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
LL | mem::transmute(x)
|
||||
| ^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required ALIGN byte alignment but found ALIGN)
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at $DIR/reference_to_packed.rs:LL:CC
|
||||
= note: inside `raw_to_ref::<'_, i32>` at $DIR/reference_to_packed.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> $DIR/reference_to_packed.rs:LL:CC
|
||||
|
|
||||
LL | let p: &i32 = unsafe { raw_to_ref(ptr::addr_of!(foo.x)) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
|
@ -7,6 +7,6 @@ fn main() {
|
||||
let x = [2u16, 3, 4]; // Make it big enough so we don't get an out-of-bounds error.
|
||||
let x = &x[0] as *const _ as *const u32;
|
||||
// This must fail because alignment is violated: the allocation's base is not sufficiently aligned.
|
||||
let _x = unsafe { *x }; //~ERROR: memory with alignment 2, but alignment 4 is required
|
||||
let _x = unsafe { *x }; //~ERROR: with alignment 2, but alignment 4 is required
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
error: Undefined Behavior: accessing memory based on pointer with alignment ALIGN, but alignment ALIGN is required
|
||||
--> $DIR/unaligned_ptr1.rs:LL:CC
|
||||
|
|
||||
LL | let _x = unsafe { *x };
|
||||
| ^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
| ^^ accessing memory based on pointer 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
|
||||
|
@ -8,5 +8,5 @@ fn main() {
|
||||
let x = (x.as_ptr() as *const u8).wrapping_offset(3) as *const u32;
|
||||
// This must fail because alignment is violated: the offset is not sufficiently aligned.
|
||||
// Also make the offset not a power of 2, that used to ICE.
|
||||
let _x = unsafe { *x }; //~ERROR: memory with alignment 1, but alignment 4 is required
|
||||
let _x = unsafe { *x }; //~ERROR: with alignment 1, but alignment 4 is required
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
error: Undefined Behavior: accessing memory based on pointer with alignment ALIGN, but alignment ALIGN is required
|
||||
--> $DIR/unaligned_ptr2.rs:LL:CC
|
||||
|
|
||||
LL | let _x = unsafe { *x };
|
||||
| ^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
|
||||
| ^^ accessing memory based on pointer 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
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user