+use crate::const_eval::CheckAlignment;
use std::borrow::Cow;
use either::{Left, Right};
None => InternKind::Constant,
}
};
- ecx.machine.check_alignment = false; // interning doesn't need to respect alignment
+ 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
tcx,
root_span,
param_env,
- CompileTimeInterpreter::new(
- tcx.const_eval_limit(),
- can_access_statics,
- /*check_alignment:*/ false,
- ),
+ CompileTimeInterpreter::new(tcx.const_eval_limit(), can_access_statics, CheckAlignment::No),
)
}
CompileTimeInterpreter::new(
tcx.const_eval_limit(),
/*can_access_statics:*/ is_static,
- /*check_alignment:*/ true,
+ if tcx.sess.opts.unstable_opts.extra_const_ub_checks {
+ CheckAlignment::Error
+ } else {
+ CheckAlignment::FutureIncompat
+ },
),
);
pub(super) can_access_statics: bool,
/// Whether to check alignment during evaluation.
- pub(super) check_alignment: bool,
+ pub(super) check_alignment: CheckAlignment,
+}
+
+#[derive(Copy, Clone)]
+pub enum CheckAlignment {
+ /// Ignore alignment when following relocations.
+ /// 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,
+ }
+ }
}
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
pub(crate) fn new(
const_eval_limit: Limit,
can_access_statics: bool,
- check_alignment: bool,
+ check_alignment: CheckAlignment,
) -> Self {
CompileTimeInterpreter {
steps_remaining: const_eval_limit.0,
const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error
#[inline(always)]
- fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+ fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
ecx.machine.check_alignment
}
Right(span) => span,
}
}
+
+ pub fn lint_root(&self) -> Option<hir::HirId> {
+ self.current_source_info().and_then(|source_info| {
+ match &self.body.source_scopes[source_info.scope].local_data {
+ mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
+ mir::ClearCrossCrate::Clear => None,
+ }
+ })
+ }
}
impl<'tcx> fmt::Display for FrameInfo<'tcx> {
// This deliberately does *not* honor `requires_caller_location` since it is used for much
// more than just panics.
for frame in stack.iter().rev() {
- let lint_root = frame.current_source_info().and_then(|source_info| {
- match &frame.body.source_scopes[source_info.scope].local_data {
- mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
- mir::ClearCrossCrate::Clear => None,
- }
- });
+ let lint_root = frame.lint_root();
let span = frame.current_span();
frames.push(FrameInfo { span, instance: frame.instance, lint_root });
use rustc_target::abi::Size;
use rustc_target::spec::abi::Abi as CallAbi;
+use crate::const_eval::CheckAlignment;
+
use super::{
AllocId, AllocRange, Allocation, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar, StackPopUnwind,
const PANIC_ON_ALLOC_FAIL: bool;
/// Whether memory accesses should be alignment-checked.
- fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
+ fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment;
/// 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.
use rustc_ast::Mutability;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::CRATE_HIR_ID;
use rustc_middle::mir::display_allocation;
+use rustc_middle::mir::interpret::UndefinedBehaviorInfo;
use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
+use rustc_session::lint::builtin::INVALID_ALIGNMENT;
use rustc_target::abi::{Align, HasDataLayout, Size};
+use crate::const_eval::CheckAlignment;
+
use super::{
alloc_range, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg, GlobalAlloc, InterpCx,
InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance, Scalar,
ptr,
size,
align,
- /* force_alignment_check */ true,
+ CheckAlignment::Error,
msg,
|alloc_id, _, _| {
let (size, align) = self.get_live_alloc_size_and_align(alloc_id)?;
ptr: Pointer<Option<M::Provenance>>,
size: Size,
align: Align,
- force_alignment_check: bool,
+ check: CheckAlignment,
msg: CheckInAllocMsg,
alloc_size: impl FnOnce(
AllocId,
M::ProvenanceExtra,
) -> InterpResult<'tcx, (Size, Align, T)>,
) -> InterpResult<'tcx, Option<T>> {
- fn check_offset_align<'tcx>(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,
- })
- }
- }
-
Ok(match self.ptr_try_get_alloc_id(ptr) {
Err(addr) => {
// We couldn't get a proper allocation. This is only okay if the access size is 0,
throw_ub!(DanglingIntPointer(addr, msg));
}
// Must be aligned.
- if force_alignment_check {
- check_offset_align(addr, align)?;
+ if check.should_check() {
+ self.check_offset_align(addr, align, check)?;
}
None
}
}
// Test align. Check this last; if both bounds and alignment are violated
// we want the error to be about the bounds.
- if force_alignment_check {
+ if check.should_check() {
if M::use_addr_for_alignment_check(self) {
// `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
- check_offset_align(ptr.addr().bytes(), align)?;
+ self.check_offset_align(ptr.addr().bytes(), align, check)?;
} else {
// Check allocation alignment and offset alignment.
if alloc_align.bytes() < align.bytes() {
- throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align });
+ self.alignment_check_failed(alloc_align, align, check)?;
}
- check_offset_align(offset.bytes(), align)?;
+ self.check_offset_align(offset.bytes(), align, check)?;
}
}
}
})
}
+
+ fn check_offset_align(
+ &self,
+ offset: u64,
+ align: Align,
+ check: CheckAlignment,
+ ) -> 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();
+ self.alignment_check_failed(Align::from_bytes(offset_pow2).unwrap(), align, check)
+ }
+ }
+
+ fn alignment_check_failed(
+ &self,
+ has: Align,
+ required: Align,
+ check: CheckAlignment,
+ ) -> InterpResult<'tcx, ()> {
+ match check {
+ CheckAlignment::Error => {
+ throw_ub!(AlignmentCheckFailed { has, required })
+ }
+ CheckAlignment::No => span_bug!(
+ self.cur_span(),
+ "`alignment_check_failed` called when no alignment check requested"
+ ),
+ CheckAlignment::FutureIncompat => self.tcx.struct_span_lint_hir(
+ INVALID_ALIGNMENT,
+ self.stack().iter().find_map(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID),
+ self.cur_span(),
+ UndefinedBehaviorInfo::AlignmentCheckFailed { has, required }.to_string(),
+ |db| {
+ let mut stacktrace = self.generate_stacktrace();
+ // Filter out `requires_caller_location` frames.
+ stacktrace
+ .retain(|frame| !frame.instance.def.requires_caller_location(*self.tcx));
+ for frame in stacktrace {
+ db.span_label(frame.span, format!("inside `{}`", frame.instance));
+ }
+ db
+ },
+ ),
+ }
+ Ok(())
+ }
}
/// Allocation accessors
.size_and_align_of_mplace(&mplace)?
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
assert!(mplace.align <= align, "dynamic alignment less strict than static one?");
- let align = M::enforce_alignment(self).then_some(align);
- self.check_ptr_access_align(
- mplace.ptr,
- size,
- align.unwrap_or(Align::ONE),
- CheckInAllocMsg::DerefTest,
- )?;
+ let align = if M::enforce_alignment(self).should_check() { align } else { Align::ONE };
+ self.check_ptr_access_align(mplace.ptr, size, align, CheckInAllocMsg::DerefTest)?;
Ok(())
}
use rustc_session::Limit;
use rustc_target::abi::{Abi, FieldsShape, InitKind, Scalar, Variants};
-use crate::const_eval::CompileTimeInterpreter;
+use crate::const_eval::{CheckAlignment, CompileTimeInterpreter};
use crate::interpret::{InterpCx, MemoryKind, OpTy};
/// Determines if this type permits "raw" initialization by just transmuting some memory into an
let machine = CompileTimeInterpreter::new(
Limit::new(0),
/*can_access_statics:*/ false,
- /*check_alignment:*/ true,
+ CheckAlignment::Error,
);
let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
};
}
+declare_lint! {
+ /// The `invalid_alignment` lint detects dereferences of misaligned pointers during
+ /// constant evluation.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// const FOO: () = unsafe {
+ /// let x = [0_u8; 10];
+ /// let y = x.as_ptr() as *const u32;
+ /// *y; // the address of a `u8` array is unknown and thus we don't know if
+ /// // it is aligned enough for reading a `u32`.
+ /// }
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// The compiler allowed dereferencing raw pointers irrespective of alignment
+ /// during const eval due to the const evaluator at the time not making it easy
+ /// or cheap to check. Now that it is both, this is not accepted anymore.
+ ///
+ /// Since it was undefined behaviour to begin with, this breakage does not violate
+ /// Rust's stability guarantees. Using undefined behaviour can cause arbitrary
+ /// behaviour, including failure to build.
+ ///
+ /// [future-incompatible]: ../index.md#future-incompatible-lints
+ pub INVALID_ALIGNMENT,
+ Deny,
+ "raw pointers must be aligned before dereferencing",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #68585 <https://github.com/rust-lang/rust/issues/104616>",
+ };
+}
+
declare_lint! {
/// The `exported_private_dependencies` lint detects private dependencies
/// that are exposed in a public interface.
use either::Right;
use rustc_ast::Mutability;
+use rustc_const_eval::const_eval::CheckAlignment;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind;
use rustc_index::bit_set::BitSet;
type MemoryKind = !;
#[inline(always)]
- fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+ fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
// We do not check for alignment to avoid having to carry an `Align`
// in `ConstValue::ByRef`.
- false
+ CheckAlignment::No
}
#[inline(always)]
//!
//! Currently, this pass only propagates scalar values.
+use rustc_const_eval::const_eval::CheckAlignment;
use rustc_const_eval::interpret::{ConstValue, ImmTy, Immediate, InterpCx, Scalar};
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::mir::visit::{MutVisitor, Visitor};
type MemoryKind = !;
const PANIC_ON_ALLOC_FAIL: bool = true;
- fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+ fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
unimplemented!()
}
╾─alloc41─╼ │ ╾──╼
}
-error[E0080]: evaluation of constant value failed
+error: accessing memory with alignment 1, but alignment 4 is required
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
- = note: accessing memory with alignment 1, but alignment 4 is required
- |
-note: inside `std::ptr::read::<u32>`
- --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
-note: inside `ptr::const_ptr::<impl *const u32>::read`
+ = note: inside `std::ptr::read::<u32>`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-note: inside `UNALIGNED_READ`
- --> $DIR/ub-ref-ptr.rs:65:5
+ |
+ = note: inside `ptr::const_ptr::<impl *const u32>::read`
+ |
+ ::: $DIR/ub-ref-ptr.rs:65:5
|
LL | ptr.read();
- | ^^^^^^^^^^
+ | ---------- inside `UNALIGNED_READ`
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #68585 <https://github.com/rust-lang/rust/issues/104616>
+ = note: `#[deny(invalid_alignment)]` on by default
error: aborting due to 15 previous errors
╾───────alloc41───────╼ │ ╾──────╼
}
-error[E0080]: evaluation of constant value failed
+error: accessing memory with alignment 1, but alignment 4 is required
--> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
|
- = note: accessing memory with alignment 1, but alignment 4 is required
- |
-note: inside `std::ptr::read::<u32>`
- --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL
-note: inside `ptr::const_ptr::<impl *const u32>::read`
+ = note: inside `std::ptr::read::<u32>`
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-note: inside `UNALIGNED_READ`
- --> $DIR/ub-ref-ptr.rs:65:5
+ |
+ = note: inside `ptr::const_ptr::<impl *const u32>::read`
+ |
+ ::: $DIR/ub-ref-ptr.rs:65:5
|
LL | ptr.read();
- | ^^^^^^^^^^
+ | ---------- inside `UNALIGNED_READ`
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #68585 <https://github.com/rust-lang/rust/issues/104616>
+ = note: `#[deny(invalid_alignment)]` on by default
error: aborting due to 15 previous errors
use rustc_span::Symbol;
use rustc_target::abi::Size;
use rustc_target::spec::abi::Abi;
+use rustc_const_eval::const_eval::CheckAlignment;
use crate::{
concurrency::{data_race, weak_memory},
const PANIC_ON_ALLOC_FAIL: bool = false;
#[inline(always)]
- fn enforce_alignment(ecx: &MiriInterpCx<'mir, 'tcx>) -> bool {
- ecx.machine.check_alignment != AlignmentCheck::None
+ fn enforce_alignment(ecx: &MiriInterpCx<'mir, 'tcx>) -> CheckAlignment {
+ if ecx.machine.check_alignment == AlignmentCheck::None {
+ CheckAlignment::No
+ } else {
+ CheckAlignment::Error
+ }
}
#[inline(always)]