const-eval: make misalignment a hard error
This commit is contained in:
parent
8bf0dec101
commit
a993a8bf3f
10 changed files with 42 additions and 193 deletions
|
@ -1,6 +1,3 @@
|
|||
use crate::const_eval::CheckAlignment;
|
||||
use crate::errors::ConstEvalError;
|
||||
|
||||
use either::{Left, Right};
|
||||
|
||||
use rustc_hir::def::DefKind;
|
||||
|
@ -15,7 +12,9 @@ use rustc_span::source_map::Span;
|
|||
use rustc_target::abi::{self, Abi};
|
||||
|
||||
use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
|
||||
use crate::const_eval::CheckAlignment;
|
||||
use crate::errors;
|
||||
use crate::errors::ConstEvalError;
|
||||
use crate::interpret::eval_nullary_intrinsic;
|
||||
use crate::interpret::{
|
||||
intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate, InternKind, InterpCx,
|
||||
|
@ -290,14 +289,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
|||
key.param_env,
|
||||
// Statics (and promoteds inside statics) may access other statics, because unlike consts
|
||||
// they do not have to behave "as if" they were evaluated at runtime.
|
||||
CompileTimeInterpreter::new(
|
||||
CanAccessStatics::from(is_static),
|
||||
if tcx.sess.opts.unstable_opts.extra_const_ub_checks {
|
||||
CheckAlignment::Error
|
||||
} else {
|
||||
CheckAlignment::FutureIncompat
|
||||
},
|
||||
),
|
||||
CompileTimeInterpreter::new(CanAccessStatics::from(is_static), CheckAlignment::Error),
|
||||
);
|
||||
|
||||
let res = ecx.load_mir(cid.instance.def, cid.promoted);
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::{LangItem, CRATE_HIR_ID};
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::PointerArithmetic;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::lint::builtin::INVALID_ALIGNMENT;
|
||||
use std::borrow::Borrow;
|
||||
use std::hash::Hash;
|
||||
use std::ops::ControlFlow;
|
||||
|
@ -21,11 +20,11 @@ use rustc_target::abi::{Align, Size};
|
|||
use rustc_target::spec::abi::Abi as CallAbi;
|
||||
|
||||
use crate::errors::{LongRunning, LongRunningWarn};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::interpret::{
|
||||
self, compile_time_machine, AllocId, ConstAllocation, FnArg, FnVal, Frame, ImmTy, InterpCx,
|
||||
InterpResult, OpTy, PlaceTy, Pointer, Scalar,
|
||||
};
|
||||
use crate::{errors, fluent_generated as fluent};
|
||||
|
||||
use super::error::*;
|
||||
|
||||
|
@ -65,22 +64,11 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
|
|||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum CheckAlignment {
|
||||
/// Ignore alignment when following relocations.
|
||||
/// Ignore all alignment requirements.
|
||||
/// This is mainly used in interning.
|
||||
No,
|
||||
/// Hard error when dereferencing a misaligned pointer.
|
||||
Error,
|
||||
/// Emit a future incompat lint when dereferencing a misaligned pointer.
|
||||
FutureIncompat,
|
||||
}
|
||||
|
||||
impl CheckAlignment {
|
||||
pub fn should_check(&self) -> bool {
|
||||
match self {
|
||||
CheckAlignment::No => false,
|
||||
CheckAlignment::Error | CheckAlignment::FutureIncompat => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
|
@ -358,8 +346,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
|||
const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error
|
||||
|
||||
#[inline(always)]
|
||||
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
|
||||
ecx.machine.check_alignment
|
||||
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
|
||||
matches!(ecx.machine.check_alignment, CheckAlignment::Error)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -367,39 +355,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
|||
ecx.tcx.sess.opts.unstable_opts.extra_const_ub_checks || layout.abi.is_uninhabited()
|
||||
}
|
||||
|
||||
fn alignment_check_failed(
|
||||
ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
has: Align,
|
||||
required: Align,
|
||||
check: CheckAlignment,
|
||||
) -> InterpResult<'tcx, ()> {
|
||||
let err = err_ub!(AlignmentCheckFailed { has, required }).into();
|
||||
match check {
|
||||
CheckAlignment::Error => Err(err),
|
||||
CheckAlignment::No => span_bug!(
|
||||
ecx.cur_span(),
|
||||
"`alignment_check_failed` called when no alignment check requested"
|
||||
),
|
||||
CheckAlignment::FutureIncompat => {
|
||||
let (_, backtrace) = err.into_parts();
|
||||
backtrace.print_backtrace();
|
||||
let (span, frames) = super::get_span_and_frames(&ecx);
|
||||
|
||||
ecx.tcx.emit_spanned_lint(
|
||||
INVALID_ALIGNMENT,
|
||||
ecx.stack().iter().find_map(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID),
|
||||
span,
|
||||
errors::AlignmentCheckFailed {
|
||||
has: has.bytes(),
|
||||
required: required.bytes(),
|
||||
frames,
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_mir(
|
||||
ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
instance: ty::InstanceDef<'tcx>,
|
||||
|
|
|
@ -11,11 +11,9 @@ use rustc_middle::mir;
|
|||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_target::abi::{Align, Size};
|
||||
use rustc_target::abi::Size;
|
||||
use rustc_target::spec::abi::Abi as CallAbi;
|
||||
|
||||
use crate::const_eval::CheckAlignment;
|
||||
|
||||
use super::{
|
||||
AllocBytes, AllocId, AllocRange, Allocation, ConstAllocation, FnArg, Frame, ImmTy, InterpCx,
|
||||
InterpResult, MPlaceTy, MemoryKind, OpTy, PlaceTy, Pointer, Provenance,
|
||||
|
@ -134,7 +132,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
|||
const POST_MONO_CHECKS: bool = true;
|
||||
|
||||
/// Whether memory accesses should be alignment-checked.
|
||||
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment;
|
||||
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
|
||||
|
||||
/// Whether, when checking alignment, we should look at the actual address and thus support
|
||||
/// custom alignment logic based on whatever the integer address happens to be.
|
||||
|
@ -142,13 +140,6 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
|||
/// If this returns true, Provenance::OFFSET_IS_ADDR must be true.
|
||||
fn use_addr_for_alignment_check(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
|
||||
|
||||
fn alignment_check_failed(
|
||||
ecx: &InterpCx<'mir, 'tcx, Self>,
|
||||
has: Align,
|
||||
required: Align,
|
||||
check: CheckAlignment,
|
||||
) -> InterpResult<'tcx, ()>;
|
||||
|
||||
/// Whether to enforce the validity invariant for a specific layout.
|
||||
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool;
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ use rustc_middle::mir::display_allocation;
|
|||
use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
|
||||
use rustc_target::abi::{Align, HasDataLayout, Size};
|
||||
|
||||
use crate::const_eval::CheckAlignment;
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
use super::{
|
||||
|
@ -373,8 +372,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
self.check_and_deref_ptr(
|
||||
ptr,
|
||||
size,
|
||||
align,
|
||||
M::enforce_alignment(self),
|
||||
M::enforce_alignment(self).then_some(align),
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
|alloc_id, offset, prov| {
|
||||
let (size, align) = self
|
||||
|
@ -395,17 +393,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
align: Align,
|
||||
msg: CheckInAllocMsg,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.check_and_deref_ptr(
|
||||
ptr,
|
||||
size,
|
||||
align,
|
||||
CheckAlignment::Error,
|
||||
msg,
|
||||
|alloc_id, _, _| {
|
||||
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
|
||||
Ok((size, align, ()))
|
||||
},
|
||||
)?;
|
||||
self.check_and_deref_ptr(ptr, size, Some(align), msg, |alloc_id, _, _| {
|
||||
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
|
||||
Ok((size, align, ()))
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -419,8 +410,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
size: Size,
|
||||
align: Align,
|
||||
check: CheckAlignment,
|
||||
align: Option<Align>,
|
||||
msg: CheckInAllocMsg,
|
||||
alloc_size: impl FnOnce(
|
||||
AllocId,
|
||||
|
@ -436,8 +426,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
throw_ub!(DanglingIntPointer(addr, msg));
|
||||
}
|
||||
// Must be aligned.
|
||||
if check.should_check() {
|
||||
self.check_offset_align(addr, align, check)?;
|
||||
if let Some(align) = align {
|
||||
self.check_offset_align(addr, align)?;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -460,16 +450,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
// Test align. Check this last; if both bounds and alignment are violated
|
||||
// we want the error to be about the bounds.
|
||||
if check.should_check() {
|
||||
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, check)?;
|
||||
self.check_offset_align(ptr.addr().bytes(), align)?;
|
||||
} else {
|
||||
// Check allocation alignment and offset alignment.
|
||||
if alloc_align.bytes() < align.bytes() {
|
||||
M::alignment_check_failed(self, alloc_align, align, check)?;
|
||||
throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align });
|
||||
}
|
||||
self.check_offset_align(offset.bytes(), align, check)?;
|
||||
self.check_offset_align(offset.bytes(), align)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -480,18 +470,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
})
|
||||
}
|
||||
|
||||
fn check_offset_align(
|
||||
&self,
|
||||
offset: u64,
|
||||
align: Align,
|
||||
check: CheckAlignment,
|
||||
) -> InterpResult<'tcx> {
|
||||
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();
|
||||
M::alignment_check_failed(self, Align::from_bytes(offset_pow2).unwrap(), align, check)
|
||||
throw_ub!(AlignmentCheckFailed {
|
||||
has: Align::from_bytes(offset_pow2).unwrap(),
|
||||
required: align
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -609,8 +597,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let ptr_and_alloc = self.check_and_deref_ptr(
|
||||
ptr,
|
||||
size,
|
||||
align,
|
||||
M::enforce_alignment(self),
|
||||
M::enforce_alignment(self).then_some(align),
|
||||
CheckInAllocMsg::MemoryAccessTest,
|
||||
|alloc_id, offset, prov| {
|
||||
let alloc = self.get_alloc_raw(alloc_id)?;
|
||||
|
|
|
@ -500,8 +500,7 @@ where
|
|||
.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).should_check() { mplace.align } else { Align::ONE };
|
||||
let align = if M::enforce_alignment(self) { mplace.align } else { Align::ONE };
|
||||
self.check_ptr_access_align(mplace.ptr(), size, align, CheckInAllocMsg::DerefTest)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue