mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-19 19:23:10 +00:00
A MIR transform that checks pointers are aligned
This commit is contained in:
parent
e216300876
commit
8ccf53332e
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
220
compiler/rustc_mir_transform/src/check_alignment.rs
Normal file
220
compiler/rustc_mir_transform/src/check_alignment.rs
Normal 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,
|
||||
},
|
||||
});
|
||||
}
|
@ -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,
|
||||
|
@ -1067,6 +1067,7 @@ symbols! {
|
||||
panic_implementation,
|
||||
panic_info,
|
||||
panic_location,
|
||||
panic_misaligned_pointer_dereference,
|
||||
panic_nounwind,
|
||||
panic_runtime,
|
||||
panic_str,
|
||||
|
@ -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
|
||||
|
@ -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, _, _) => {
|
||||
|
@ -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(
|
||||
|
@ -1,4 +1,5 @@
|
||||
//@normalize-stderr-test: "\| +\^+" -> "| ^"
|
||||
//@compile-flags: -Cdebug-assertions=no
|
||||
|
||||
fn main() {
|
||||
// No retry needed, this fails reliably.
|
||||
|
@ -1,4 +1,4 @@
|
||||
//@compile-flags: -Zmiri-symbolic-alignment-check
|
||||
//@compile-flags: -Zmiri-symbolic-alignment-check -Cdebug-assertions=no
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
fn main() {
|
||||
|
@ -1,3 +1,5 @@
|
||||
//@compile-flags: -Cdebug-assertions=no
|
||||
|
||||
#[repr(transparent)]
|
||||
struct HasDrop(u8);
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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
|
||||
|
@ -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)]
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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() {
|
||||
|
@ -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.
|
||||
|
9
src/tools/miri/tests/panic/alignment-assertion.rs
Normal file
9
src/tools/miri/tests/panic/alignment-assertion.rs
Normal 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;
|
||||
}
|
||||
}
|
2
src/tools/miri/tests/panic/alignment-assertion.stderr
Normal file
2
src/tools/miri/tests/panic/alignment-assertion.stderr
Normal 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
|
@ -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];
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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]+]]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
11
tests/ui/mir/mir_alignment_check.rs
Normal file
11
tests/ui/mir/mir_alignment_check.rs
Normal 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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user