A MIR transform that checks pointers are aligned

This commit is contained in:
Ben Kimock 2022-11-10 11:37:28 -05:00
parent e216300876
commit 8ccf53332e
34 changed files with 360 additions and 20 deletions

View File

@ -379,6 +379,18 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
source_info.span,
);
}
AssertKind::MisalignedPointerDereference { ref required, ref found } => {
let required = codegen_operand(fx, required).load_scalar(fx);
let found = codegen_operand(fx, found).load_scalar(fx);
let location = fx.get_caller_location(source_info).load_scalar(fx);
codegen_panic_inner(
fx,
rustc_hir::LangItem::PanicBoundsCheck,
&[required, found, location],
source_info.span,
);
}
_ => {
let msg_str = msg.description();
codegen_panic(fx, msg_str, source_info);

View File

@ -600,6 +600,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// and `#[track_caller]` adds an implicit third argument.
(LangItem::PanicBoundsCheck, vec![index, len, location])
}
AssertKind::MisalignedPointerDereference { ref required, ref found } => {
let required = self.codegen_operand(bx, required).immediate();
let found = self.codegen_operand(bx, found).immediate();
// It's `fn panic_bounds_check(index: usize, len: usize)`,
// and `#[track_caller]` adds an implicit third argument.
(LangItem::PanicMisalignedPointerDereference, vec![required, found, location])
}
_ => {
let msg = bx.const_str(msg.description());
// It's `pub fn panic(expr: &str)`, with the wide reference being passed

View File

@ -544,6 +544,12 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
RemainderByZero(op) => RemainderByZero(eval_to_int(op)?),
ResumedAfterReturn(generator_kind) => ResumedAfterReturn(*generator_kind),
ResumedAfterPanic(generator_kind) => ResumedAfterPanic(*generator_kind),
MisalignedPointerDereference { ref required, ref found } => {
MisalignedPointerDereference {
required: eval_to_int(required)?,
found: eval_to_int(found)?,
}
}
};
Err(ConstEvalErrKind::AssertFailure(err).into())
}

View File

@ -237,6 +237,7 @@ language_item_table! {
PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None;
ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
PanicMisalignedPointerDereference, sym::panic_misaligned_pointer_dereference, panic_misaligned_pointer_dereference_fn, Target::Fn, GenericRequirement::Exact(0);
PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None;
PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None;
PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None;

View File

@ -1277,7 +1277,7 @@ impl<O> AssertKind<O> {
/// Getting a description does not require `O` to be printable, and does not
/// require allocation.
/// The caller is expected to handle `BoundsCheck` separately.
/// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` separately.
pub fn description(&self) -> &'static str {
use AssertKind::*;
match self {
@ -1296,7 +1296,9 @@ impl<O> AssertKind<O> {
ResumedAfterReturn(GeneratorKind::Async(_)) => "`async fn` resumed after completion",
ResumedAfterPanic(GeneratorKind::Gen) => "generator resumed after panicking",
ResumedAfterPanic(GeneratorKind::Async(_)) => "`async fn` resumed after panicking",
BoundsCheck { .. } => bug!("Unexpected AssertKind"),
BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
bug!("Unexpected AssertKind")
}
}
}
@ -1353,6 +1355,13 @@ impl<O> AssertKind<O> {
Overflow(BinOp::Shl, _, r) => {
write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {:?}", r)
}
MisalignedPointerDereference { required, found } => {
write!(
f,
"\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {:?}, {:?}",
required, found
)
}
_ => write!(f, "\"{}\"", self.description()),
}
}
@ -1397,6 +1406,13 @@ impl<O: fmt::Debug> fmt::Debug for AssertKind<O> {
Overflow(BinOp::Shl, _, r) => {
write!(f, "attempt to shift left by `{:#?}`, which would overflow", r)
}
MisalignedPointerDereference { required, found } => {
write!(
f,
"misaligned pointer dereference: address must be a multiple of {:?} but is {:?}",
required, found
)
}
_ => write!(f, "{}", self.description()),
}
}

View File

@ -760,6 +760,7 @@ pub enum AssertKind<O> {
RemainderByZero(O),
ResumedAfterReturn(GeneratorKind),
ResumedAfterPanic(GeneratorKind),
MisalignedPointerDereference { required: O, found: O },
}
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]

View File

@ -608,6 +608,10 @@ macro_rules! make_mir_visitor {
ResumedAfterReturn(_) | ResumedAfterPanic(_) => {
// Nothing to visit
}
MisalignedPointerDereference { required, found } => {
self.visit_operand(required, location);
self.visit_operand(found, location);
}
}
}

View File

@ -0,0 +1,220 @@
use crate::MirPass;
use rustc_hir::def_id::DefId;
use rustc_index::vec::IndexVec;
use rustc_middle::mir::*;
use rustc_middle::mir::{
interpret::{ConstValue, Scalar},
visit::{PlaceContext, Visitor},
};
use rustc_middle::ty::{Ty, TyCtxt, TypeAndMut};
use rustc_session::Session;
pub struct CheckAlignment;
impl<'tcx> MirPass<'tcx> for CheckAlignment {
fn is_enabled(&self, sess: &Session) -> bool {
sess.opts.debug_assertions
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let basic_blocks = body.basic_blocks.as_mut();
let local_decls = &mut body.local_decls;
for block in (0..basic_blocks.len()).rev() {
let block = block.into();
for statement_index in (0..basic_blocks[block].statements.len()).rev() {
let location = Location { block, statement_index };
let statement = &basic_blocks[block].statements[statement_index];
let source_info = statement.source_info;
let mut finder = PointerFinder {
local_decls,
tcx,
pointers: Vec::new(),
def_id: body.source.def_id(),
};
for (pointer, pointee_ty) in finder.find_pointers(statement) {
debug!("Inserting alignment check for {:?}", pointer.ty(&*local_decls, tcx).ty);
let new_block = split_block(basic_blocks, location);
insert_alignment_check(
tcx,
local_decls,
&mut basic_blocks[block],
pointer,
pointee_ty,
source_info,
new_block,
);
}
}
}
}
}
impl<'tcx, 'a> PointerFinder<'tcx, 'a> {
fn find_pointers(&mut self, statement: &Statement<'tcx>) -> Vec<(Place<'tcx>, Ty<'tcx>)> {
self.pointers.clear();
self.visit_statement(statement, Location::START);
core::mem::take(&mut self.pointers)
}
}
struct PointerFinder<'tcx, 'a> {
local_decls: &'a mut LocalDecls<'tcx>,
tcx: TyCtxt<'tcx>,
def_id: DefId,
pointers: Vec<(Place<'tcx>, Ty<'tcx>)>,
}
impl<'tcx, 'a> Visitor<'tcx> for PointerFinder<'tcx, 'a> {
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
if let PlaceContext::NonUse(_) = context {
return;
}
if !place.is_indirect() {
return;
}
let pointer = Place::from(place.local);
let pointer_ty = pointer.ty(&*self.local_decls, self.tcx).ty;
// We only want to check unsafe pointers
if !pointer_ty.is_unsafe_ptr() {
trace!("Indirect, but not an unsafe ptr, not checking {:?}", pointer_ty);
return;
}
let Some(pointee) = pointer_ty.builtin_deref(true) else {
debug!("Indirect but no builtin deref: {:?}", pointer_ty);
return;
};
let mut pointee_ty = pointee.ty;
if pointee_ty.is_array() || pointee_ty.is_slice() || pointee_ty.is_str() {
pointee_ty = pointee_ty.sequence_element_type(self.tcx);
}
if !pointee_ty.is_sized(self.tcx, self.tcx.param_env_reveal_all_normalized(self.def_id)) {
debug!("Unsafe pointer, but unsized: {:?}", pointer_ty);
return;
}
self.pointers.push((pointer, pointee_ty))
}
}
fn split_block(
basic_blocks: &mut IndexVec<BasicBlock, BasicBlockData<'_>>,
location: Location,
) -> BasicBlock {
let block_data = &mut basic_blocks[location.block];
// Drain every statement after this one and move the current terminator to a new basic block
let new_block = BasicBlockData {
statements: block_data.statements.drain(location.statement_index..).collect(),
terminator: block_data.terminator.take(),
is_cleanup: block_data.is_cleanup,
};
basic_blocks.push(new_block)
}
fn insert_alignment_check<'tcx>(
tcx: TyCtxt<'tcx>,
local_decls: &mut LocalDecls<'tcx>,
block_data: &mut BasicBlockData<'tcx>,
pointer: Place<'tcx>,
pointee_ty: Ty<'tcx>,
source_info: SourceInfo,
new_block: BasicBlock,
) {
// Cast the pointer to a *const ()
let const_raw_ptr = tcx.mk_ptr(TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not });
let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr);
let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into();
block_data
.statements
.push(Statement { source_info, kind: StatementKind::Assign(Box::new((thin_ptr, rvalue))) });
// Cast the pointer to a usize
let rvalue = Rvalue::Cast(CastKind::Transmute, Operand::Copy(thin_ptr), tcx.types.usize);
let addr = local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
block_data
.statements
.push(Statement { source_info, kind: StatementKind::Assign(Box::new((addr, rvalue))) });
// Get the alignment of the pointee
let alignment =
local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
let rvalue = Rvalue::NullaryOp(NullOp::AlignOf, pointee_ty);
block_data.statements.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((alignment, rvalue))),
});
// Subtract 1 from the alignment to get the alignment mask
let alignment_mask =
local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
let one = Operand::Constant(Box::new(Constant {
span: source_info.span,
user_ty: None,
literal: ConstantKind::Val(
ConstValue::Scalar(Scalar::from_target_usize(1, &tcx)),
tcx.types.usize,
),
}));
block_data.statements.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((
alignment_mask,
Rvalue::BinaryOp(BinOp::Sub, Box::new((Operand::Copy(alignment), one))),
))),
});
// BitAnd the alignment mask with the pointer
let alignment_bits =
local_decls.push(LocalDecl::with_source_info(tcx.types.usize, source_info)).into();
block_data.statements.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((
alignment_bits,
Rvalue::BinaryOp(
BinOp::BitAnd,
Box::new((Operand::Copy(addr), Operand::Copy(alignment_mask))),
),
))),
});
// Check if the alignment bits are all zero
let is_ok = local_decls.push(LocalDecl::with_source_info(tcx.types.bool, source_info)).into();
let zero = Operand::Constant(Box::new(Constant {
span: source_info.span,
user_ty: None,
literal: ConstantKind::Val(
ConstValue::Scalar(Scalar::from_target_usize(0, &tcx)),
tcx.types.usize,
),
}));
block_data.statements.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((
is_ok,
Rvalue::BinaryOp(BinOp::Eq, Box::new((Operand::Copy(alignment_bits), zero.clone()))),
))),
});
// Set this block's terminator to our assert, continuing to new_block if we pass
block_data.terminator = Some(Terminator {
source_info,
kind: TerminatorKind::Assert {
cond: Operand::Copy(is_ok),
expected: true,
target: new_block,
msg: AssertKind::MisalignedPointerDereference {
required: Operand::Copy(alignment),
found: Operand::Copy(addr),
},
cleanup: None,
},
});
}

View File

@ -91,6 +91,7 @@ mod separate_const_switch;
mod shim;
mod ssa;
// This pass is public to allow external drivers to perform MIR cleanup
mod check_alignment;
pub mod simplify;
mod simplify_branches;
mod simplify_comparison_integral;
@ -546,6 +547,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
tcx,
body,
&[
&check_alignment::CheckAlignment,
&reveal_all::RevealAll, // has to be done before inlining, since inlined code is in RevealAll mode.
&lower_slice_len::LowerSliceLenCalls, // has to be done before inlining, otherwise actual call will be almost always inlined. Also simple, so can just do first
&unreachable_prop::UnreachablePropagation,

View File

@ -1067,6 +1067,7 @@ symbols! {
panic_implementation,
panic_info,
panic_location,
panic_misaligned_pointer_dereference,
panic_nounwind,
panic_runtime,
panic_str,

View File

@ -159,6 +159,20 @@ fn panic_bounds_check(index: usize, len: usize) -> ! {
panic!("index out of bounds: the len is {len} but the index is {index}")
}
#[cold]
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
#[track_caller]
#[cfg_attr(not(bootstrap), lang = "panic_misaligned_pointer_dereference")] // needed by codegen for panic on misaligned pointer deref
fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! {
if cfg!(feature = "panic_immediate_abort") {
super::intrinsics::abort()
}
panic!(
"misaligned pointer dereference: address must be a multiple of {required:#x} but is {found:#x}"
)
}
/// Panic because we cannot unwind out of a function.
///
/// This function is called directly by the codegen backend, and must not have

View File

@ -169,7 +169,7 @@ fn check_rvalue<'tcx>(
Err((span, "unsizing casts are not allowed in const fn".into()))
}
},
Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => {
Rvalue::Cast(CastKind::PointerExposeAddress | CastKind::PointerAddress, _, _) => {
Err((span, "casting pointers to ints is unstable in const fn".into()))
},
Rvalue::Cast(CastKind::DynStar, _, _) => {

View File

@ -220,6 +220,34 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
},
)?;
}
MisalignedPointerDereference { required, found } => {
// Forward to `panic_misaligned_pointer_dereference` lang item.
// First arg: required.
let required = this.read_scalar(&this.eval_operand(required, None)?)?;
// Second arg: found.
let found = this.read_scalar(&this.eval_operand(found, None)?)?;
// Call the lang item.
let panic_misaligned_pointer_dereference =
this.tcx.lang_items().panic_misaligned_pointer_dereference_fn().unwrap();
let panic_misaligned_pointer_dereference =
ty::Instance::mono(this.tcx.tcx, panic_misaligned_pointer_dereference);
this.call_function(
panic_misaligned_pointer_dereference,
Abi::Rust,
&[required.into(), found.into()],
None,
StackPopCleanup::Goto {
ret: None,
unwind: match unwind {
Some(cleanup) => StackPopUnwind::Cleanup(cleanup),
None => StackPopUnwind::Skip,
},
},
)?;
}
_ => {
// Forward everything else to `panic` lang item.
this.start_panic(

View File

@ -1,4 +1,5 @@
//@normalize-stderr-test: "\| +\^+" -> "| ^"
//@compile-flags: -Cdebug-assertions=no
fn main() {
// No retry needed, this fails reliably.

View File

@ -1,4 +1,4 @@
//@compile-flags: -Zmiri-symbolic-alignment-check
//@compile-flags: -Zmiri-symbolic-alignment-check -Cdebug-assertions=no
#![feature(core_intrinsics)]
fn main() {

View File

@ -1,3 +1,5 @@
//@compile-flags: -Cdebug-assertions=no
#[repr(transparent)]
struct HasDrop(u8);

View File

@ -1,5 +1,5 @@
// should find the bug even without validation and stacked borrows, but gets masked by optimizations
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Zmir-opt-level=0
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Zmir-opt-level=0 -Cdebug-assertions=no
#[repr(align(256))]
#[derive(Debug)]

View File

@ -1,4 +1,4 @@
//@compile-flags: -Zmiri-symbolic-alignment-check -Zmiri-permissive-provenance
//@compile-flags: -Zmiri-symbolic-alignment-check -Zmiri-permissive-provenance -Cdebug-assertions=no
// With the symbolic alignment check, even with intptrcast and without
// validation, we want to be *sure* to catch bugs that arise from pointers being
// insufficiently aligned. The only way to achieve that is not not let programs

View File

@ -1,5 +1,5 @@
// This should fail even without validation/SB
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Cdebug-assertions=no
#![allow(dead_code, unused_variables)]

View File

@ -1,5 +1,5 @@
// This should fail even without validation or Stacked Borrows.
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Cdebug-assertions=no
fn main() {
// Try many times as this might work by chance.

View File

@ -1,5 +1,5 @@
// This should fail even without validation or Stacked Borrows.
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Cdebug-assertions=no
fn main() {
// No retry needed, this fails reliably.

View File

@ -1,5 +1,5 @@
// This should fail even without validation or Stacked Borrows.
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Cdebug-assertions=no
fn main() {
// Try many times as this might work by chance.

View File

@ -1,5 +1,5 @@
// This should fail even without validation or Stacked Borrows.
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Cdebug-assertions=no
fn main() {
// Make sure we notice when a u16 is loaded at offset 1 into a u8 allocation.

View File

@ -1,5 +1,5 @@
// This should fail even without validation or Stacked Borrows.
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows
//@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -Cdebug-assertions=no
use std::ptr;
fn main() {

View File

@ -1,6 +1,6 @@
// This should fail even without validation
// Some optimizations remove ZST accesses, thus masking this UB.
//@compile-flags: -Zmir-opt-level=0 -Zmiri-disable-validation
//@compile-flags: -Zmir-opt-level=0 -Zmiri-disable-validation -Cdebug-assertions=no
fn main() {
// Try many times as this might work by chance.

View File

@ -0,0 +1,9 @@
//@compile-flags: -Zmiri-disable-alignment-check -Cdebug-assertions=yes
fn main() {
let mut x = [0u32; 2];
let ptr: *mut u8 = x.as_mut_ptr().cast::<u8>();
unsafe {
*(ptr.add(1).cast::<u32>()) = 42;
}
}

View File

@ -0,0 +1,2 @@
thread 'main' panicked at 'misaligned pointer dereference: address must be a multiple of 0x4 but is $HEX', $DIR/alignment-assertion.rs:LL:CC
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

View File

@ -1,6 +1,6 @@
//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows
//@compile-flags: -Zmiri-disable-alignment-check
//@compile-flags: -Zmiri-disable-alignment-check -Cdebug-assertions=no
fn main() {
let mut x = [0u8; 20];

View File

@ -4,6 +4,7 @@
// ignore-emscripten
// ignore-gnux32
// ignore 32-bit platforms (LLVM has a bug with them)
// ignore-debug
// Check that LLVM understands that `Iter` pointer is not null. Issue #37945.

View File

@ -1,5 +1,6 @@
// compile-flags: -Zvirtual-function-elimination -Clto -O -Csymbol-mangling-version=v0
// ignore-32bit
// ignore-debug
// CHECK: @vtable.0 = {{.*}}, !type ![[TYPE0:[0-9]+]], !vcall_visibility ![[VCALL_VIS0:[0-9]+]]
// CHECK: @vtable.1 = {{.*}}, !type ![[TYPE1:[0-9]+]], !vcall_visibility ![[VCALL_VIS0:[0-9]+]]

View File

@ -8,10 +8,10 @@
scope 1 {
debug _x => _1; // in scope 1 at $DIR/inline_into_box_place.rs:+1:9: +1:11
}
+ scope 2 (inlined Vec::<u32>::new) { // at $DIR/inline_into_box_place.rs:7:38: 7:48
+ scope 2 (inlined Vec::<u32>::new) { // at $DIR/inline_into_box_place.rs:8:38: 8:48
+ let mut _3: alloc::raw_vec::RawVec<u32>; // in scope 2 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ }
+ scope 3 (inlined Box::<Vec<u32>>::new) { // at $DIR/inline_into_box_place.rs:7:29: 7:49
+ scope 3 (inlined Box::<Vec<u32>>::new) { // at $DIR/inline_into_box_place.rs:8:29: 8:49
+ debug x => _2; // in scope 3 at $SRC_DIR/alloc/src/boxed.rs:LL:COL
+ let mut _4: usize; // in scope 3 at $SRC_DIR/alloc/src/boxed.rs:LL:COL
+ let mut _5: usize; // in scope 3 at $SRC_DIR/alloc/src/boxed.rs:LL:COL
@ -28,7 +28,7 @@
+ StorageLive(_3); // scope 2 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ _3 = const _; // scope 2 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
// mir::Constant
- // + span: $DIR/inline_into_box_place.rs:7:38: 7:46
- // + span: $DIR/inline_into_box_place.rs:8:38: 8:46
- // + user_ty: UserType(2)
- // + literal: Const { ty: fn() -> Vec<u32> {Vec::<u32>::new}, val: Value(<ZST>) }
+ // + span: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
@ -47,7 +47,7 @@
bb1: {
- _1 = Box::<Vec<u32>>::new(move _2) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline_into_box_place.rs:+1:29: +1:49
- // mir::Constant
- // + span: $DIR/inline_into_box_place.rs:7:29: 7:37
- // + span: $DIR/inline_into_box_place.rs:8:29: 8:37
- // + user_ty: UserType(1)
- // + literal: Const { ty: fn(Vec<u32>) -> Box<Vec<u32>> {Box::<Vec<u32>>::new}, val: Value(<ZST>) }
+ StorageDead(_1); // scope 0 at $DIR/inline_into_box_place.rs:+2:1: +2:2

View File

@ -1,5 +1,6 @@
// ignore-endian-big
// ignore-wasm32-bare compiled with panic=abort by default
// ignore-debug MIR alignment checks in std alter the diff, breaking the test
// compile-flags: -Z mir-opt-level=4
// EMIT_MIR inline_into_box_place.main.Inline.diff

View File

@ -11,11 +11,11 @@ else
NM = nm
PANIC_SYMS = panic_bounds_check pad_integral Display Debug
PANIC_SYMS = panic_bounds_check Debug
# Allow for debug_assert!() in debug builds of std.
ifdef NO_DEBUG_ASSERTIONS
PANIC_SYMS += panicking panic_fmt
PANIC_SYMS += panicking panic_fmt pad_integral Display Debug
endif
all: main.rs

View File

@ -0,0 +1,11 @@
// run-fail
// compile-flags: -C debug-assertions
// error-pattern: misaligned pointer dereference: address must be a multiple of 0x4 but is
fn main() {
let mut x = [0u32; 2];
let ptr: *mut u8 = x.as_mut_ptr().cast::<u8>();
unsafe {
*(ptr.add(1).cast::<u32>()) = 42;
}
}