about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2023-09-05 15:12:18 +0200
committerRalf Jung <post@ralfj.de>2023-10-15 18:13:31 +0200
commitf3f9b795bdaccd8284baba295810e87646754c28 (patch)
tree850bc64dc61769301b12001add54ac4ef672e76d
parentea9a24e32e5ce4d049c1ac0d85613f9f100fa7fb (diff)
downloadrust-f3f9b795bdaccd8284baba295810e87646754c28.tar.gz
rust-f3f9b795bdaccd8284baba295810e87646754c28.zip
place evaluation: require the original pointer to be aligned if an access happens
-rw-r--r--compiler/rustc_abi/src/lib.rs1
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs6
-rw-r--r--compiler/rustc_const_eval/src/errors.rs7
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs92
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs60
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs191
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs1
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs10
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs16
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs9
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs2
-rw-r--r--src/tools/miri/src/helpers.rs5
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs2
-rw-r--r--src/tools/miri/src/shims/unix/linux/sync.rs2
-rw-r--r--src/tools/miri/src/shims/windows/sync.rs4
-rw-r--r--src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment2.rs30
-rw-r--r--src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment2.stderr20
-rw-r--r--tests/ui/const-generics/generic_const_exprs/issue-80742.rs1
-rw-r--r--tests/ui/const-generics/generic_const_exprs/issue-80742.stderr4
-rw-r--r--tests/ui/consts/const-deref-ptr.rs1
-rw-r--r--tests/ui/consts/const-eval/raw-pointer-ub.rs11
-rw-r--r--tests/ui/consts/const-eval/raw-pointer-ub.stderr10
22 files changed, 266 insertions, 219 deletions
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 45b3e76cca6..494ab451560 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -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]
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 4adfbe336af..6b612c34837 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -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)
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 96575c31c08..6214ce0f511 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -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;
@@ -567,7 +568,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
 
                 builder.set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
             }
-            AlignmentCheckFailed { required, has } => {
+            AlignmentCheckFailed(Misalignment { required, has }) => {
                 builder.set_arg("required", required.bytes());
                 builder.set_arg("has", has.bytes());
             }
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index edd8c540226..4322c39f0ac 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -22,8 +22,8 @@ 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,
+    GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer,
+    PointerArithmetic, Provenance, Scalar,
 };
 
 #[derive(Debug, PartialEq, Copy, Clone)]
@@ -372,7 +372,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         self.check_and_deref_ptr(
             ptr,
             size,
-            M::enforce_alignment(self).then_some(align),
+            align,
             CheckInAllocMsg::MemoryAccessTest,
             |alloc_id, offset, prov| {
                 let (size, align) = self
@@ -382,9 +382,8 @@ 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 given `size` and `align`.
+    /// The caller can control the error message for the out-of-bounds case.
     #[inline(always)]
     pub fn check_ptr_access_align(
         &self,
@@ -393,7 +392,7 @@ 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, Some(align), msg, |alloc_id, _, _| {
+        self.check_and_deref_ptr(ptr, size, align, msg, |alloc_id, _, _| {
             let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
             Ok((size, align, ()))
         })?;
@@ -402,15 +401,14 @@ 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>,
+        align: Align,
         msg: CheckInAllocMsg,
         alloc_size: impl FnOnce(
             AllocId,
@@ -426,9 +424,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     throw_ub!(DanglingIntPointer(addr, msg));
                 }
                 // Must be aligned.
-                if let Some(align) = align {
-                    self.check_offset_align(addr, align)?;
-                }
+                self.check_misalign(Self::offset_misalignment(addr, align))?;
                 None
             }
             Ok((alloc_id, offset, prov)) => {
@@ -450,18 +446,7 @@ 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 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)?;
-                    }
-                }
+                self.check_misalign(self.alloc_misalignment(ptr, offset, align, alloc_align))?;
 
                 // We can still be zero-sized in this branch, in which case we have to
                 // return `None`.
@@ -470,16 +455,59 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         })
     }
 
-    fn check_offset_align(&self, offset: u64, align: Align) -> InterpResult<'tcx> {
+    #[inline(always)]
+    pub(super) fn check_misalign(&self, misaligned: Option<Misalignment>) -> InterpResult<'tcx> {
+        if M::enforce_alignment(self) {
+            if let Some(misaligned) = misaligned {
+                throw_ub!(AlignmentCheckFailed(misaligned))
+            }
+        }
+        Ok(())
+    }
+
+    #[must_use]
+    fn offset_misalignment(offset: u64, align: Align) -> Option<Misalignment> {
         if offset % align.bytes() == 0 {
-            Ok(())
+            None
         } 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
-            });
+            Some(Misalignment { has: Align::from_bytes(offset_pow2).unwrap(), required: align })
+        }
+    }
+
+    #[must_use]
+    fn alloc_misalignment(
+        &self,
+        ptr: Pointer<Option<M::Provenance>>,
+        offset: Size,
+        align: Align,
+        alloc_align: Align,
+    ) -> Option<Misalignment> {
+        if M::use_addr_for_alignment_check(self) {
+            // `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
+            Self::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 {
+                Self::offset_misalignment(offset.bytes(), align)
+            }
+        }
+    }
+
+    pub(super) fn is_ptr_misaligned(
+        &self,
+        ptr: Pointer<Option<M::Provenance>>,
+        align: Align,
+    ) -> Option<Misalignment> {
+        match self.ptr_try_get_alloc_id(ptr) {
+            Err(addr) => Self::offset_misalignment(addr, align),
+            Ok((alloc_id, offset, _prov)) => {
+                let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id);
+                self.alloc_misalignment(ptr, offset, align, alloc_align)
+            }
         }
     }
 }
@@ -597,7 +625,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let ptr_and_alloc = self.check_and_deref_ptr(
             ptr,
             size,
-            M::enforce_alignment(self).then_some(align),
+            align,
             CheckInAllocMsg::MemoryAccessTest,
             |alloc_id, offset, prov| {
                 let alloc = self.get_alloc_raw(alloc_id)?;
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index 0f259f6c1a2..99424518ad4 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -10,7 +10,7 @@ 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,
@@ -44,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 {
@@ -328,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> {
@@ -351,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 }
     }
 }
 
@@ -635,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.
@@ -650,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)
+                })
             }
         }
     }
@@ -747,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 })
     }
 }
 
@@ -780,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
 }
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index f48417f207a..240613bdff6 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -16,7 +16,7 @@ use rustc_target::abi::{Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_VARIANT
 
 use super::{
     alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, ImmTy, Immediate,
-    InterpCx, InterpResult, Machine, MemoryKind, OffsetMode, OpTy, Operand, Pointer,
+    InterpCx, InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer,
     PointerArithmetic, Projectable, Provenance, Readable, Scalar,
 };
 
@@ -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,12 +70,7 @@ 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]
@@ -108,7 +95,7 @@ impl<Prov: Provenance> MemPlace<Prov> {
             }
             OffsetMode::Wrapping => self.ptr.wrapping_offset(offset, ecx),
         };
-        Ok(MemPlace { ptr, meta })
+        Ok(MemPlace { ptr, meta, misaligned: self.misaligned })
     }
 }
 
@@ -117,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> {
@@ -143,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).
@@ -204,11 +168,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
         layout: TyAndLayout<'tcx>,
         ecx: &InterpCx<'mir, 'tcx, M>,
     ) -> InterpResult<'tcx, Self> {
-        Ok(MPlaceTy {
-            mplace: self.mplace.offset_with_meta_(offset, mode, 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>>(
@@ -239,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> {
@@ -259,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 }
     }
 }
 
@@ -275,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)),
         }
     }
@@ -332,11 +287,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
                         .offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?,
                 );
 
-                PlaceTy {
-                    place: Place::Local { frame, local, offset: Some(new_offset) },
-                    align: self.align.restrict_for_offset(offset),
-                    layout,
-                }
+                PlaceTy { place: Place::Local { frame, local, offset: Some(new_offset) }, layout }
             }
         })
     }
@@ -354,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)),
         }
     }
@@ -377,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,
@@ -389,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)]
@@ -408,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())
     }
 
@@ -428,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()`.
     ///
@@ -449,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.
@@ -491,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, Align::ONE)?;
+        self.check_misalign(mplace.mplace.misaligned)?;
+        Ok(a)
     }
 
     #[inline]
@@ -504,8 +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)
+        // 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);
+        let a = self.get_ptr_alloc_mut(mplace.ptr(), size, Align::ONE)?;
+        misalign_err?;
+        Ok(a)
     }
 
     /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
@@ -520,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.
@@ -557,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
@@ -647,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
@@ -688,7 +663,7 @@ where
                         }
                         Operand::Indirect(mplace) => {
                             // The local is in memory, go on below.
-                            MPlaceTy { mplace: *mplace, align, layout }
+                            MPlaceTy { mplace: *mplace, layout }
                         }
                     }
                 }
@@ -697,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.
@@ -707,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
@@ -716,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(());
         };
@@ -736,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(),
@@ -767,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
@@ -783,7 +752,7 @@ where
                         }
                         Operand::Indirect(mplace) => {
                             // The local is in memory, go on below.
-                            MPlaceTy { mplace: *mplace, layout, align }
+                            MPlaceTy { mplace: *mplace, layout }
                         }
                     }
                 }
@@ -876,7 +845,6 @@ where
                     self.write_immediate_to_mplace_no_validate(
                         *src_val,
                         src.layout(),
-                        dest_mem.align,
                         dest_mem.mplace,
                     )
                 };
@@ -903,14 +871,19 @@ where
         // type does not have Scalar/ScalarPair layout.
         // (Or as the `Assign` docs put it, assignments "not producing primitives" must be
         // non-overlapping.)
+        // 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(),
-            src.align,
+            Align::ONE,
             dest.ptr(),
-            dest.align,
+            Align::ONE,
             dest_size,
             /*nonoverlapping*/ true,
-        )
+        )?;
+        self.check_misalign(src.mplace.misaligned)?;
+        self.check_misalign(dest.mplace.misaligned)?;
+        Ok(())
     }
 
     /// Ensures that a place is in memory, and returns where it is.
@@ -944,7 +917,6 @@ where
                             self.write_immediate_to_mplace_no_validate(
                                 local_val,
                                 local_layout,
-                                local_layout.align.abi,
                                 mplace.mplace,
                             )?;
                         }
@@ -974,7 +946,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(
@@ -987,7 +959,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(
@@ -999,7 +971,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,
@@ -1008,15 +980,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.
@@ -1055,7 +1020,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.
@@ -1071,12 +1036,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))
     }
 
@@ -1108,10 +1071,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
 }
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index f5666787fcd..6694c43c992 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -267,6 +267,7 @@ 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 })
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 07b5f5ffe21..90b30cca95c 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -7,6 +7,7 @@ use either::Either;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{InterpResult, Scalar};
 use rustc_middle::ty::layout::LayoutOf;
+use rustc_target::abi::Align;
 
 use super::{ImmTy, InterpCx, Machine, Projectable};
 use crate::util;
@@ -206,15 +207,12 @@ 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,
+                        Align::ONE,
                         rest_ptr,
-                        dest.align.restrict_for_offset(elem_size),
+                        Align::ONE,
                         elem_size,
                         length - 1,
                         /*nonoverlapping:*/ true,
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index c72c855aad2..b8cc4e8007e 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -13,14 +13,14 @@ 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;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
 use rustc_span::symbol::{sym, Symbol};
 use rustc_target::abi::{
-    Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
+    Abi, Align, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
 };
 
 use std::hash::Hash;
@@ -385,7 +385,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                 CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
             ),
             self.path,
-            Ub(AlignmentCheckFailed { required, has }) => UnalignedPtr {
+            Ub(AlignmentCheckFailed(Misalignment { required, has })) => UnalignedPtr {
                 ptr_kind,
                 required_bytes: required.bytes(),
                 found_bytes: has.bytes()
@@ -781,14 +781,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, Align::ONE)?.expect("we already excluded size 0");
 
                 match alloc.get_bytes_strip_provenance() {
                     // In the happy case, we needn't check anything else.
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 360b2032c52..9083c6b3cda 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -261,6 +261,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 {
@@ -322,7 +329,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),
     /// Writing to read-only memory.
     WriteToReadOnly(AllocId),
     /// Trying to access the data behind a function pointer.
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index d21f82f04f6..42d58264186 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -144,7 +144,7 @@ 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,
+    InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, Misalignment, PointerKind,
     ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo,
     UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
 };
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index 55591938043..b01dd288c55 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -697,10 +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);
-
-        Ok(mplace)
+        Ok(this.ptr_to_mplace(ptr, layout))
     }
 
     /// Calculates the MPlaceTy given the offset and layout of an access on an operand
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index 1014a61b75e..5403d05a895 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -1370,7 +1370,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)?;
diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs
index 48ffaee56c7..17803b52baf 100644
--- a/src/tools/miri/src/shims/unix/linux/sync.rs
+++ b/src/tools/miri/src/shims/unix/linux/sync.rs
@@ -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");
diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs
index c8c8173aa51..5e46404e7f1 100644
--- a/src/tools/miri/src/shims/windows/sync.rs
+++ b/src/tools/miri/src/shims/windows/sync.rs
@@ -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.
diff --git a/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment2.rs b/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment2.rs
new file mode 100644
index 00000000000..80a1a5dba54
--- /dev/null
+++ b/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment2.rs
@@ -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: accessing memory 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());
+    }
+}
diff --git a/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment2.stderr b/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment2.stderr
new file mode 100644
index 00000000000..421c16880db
--- /dev/null
+++ b/src/tools/miri/tests/fail/unaligned_pointers/field_requires_parent_struct_alignment2.stderr
@@ -0,0 +1,20 @@
+error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
+  --> $DIR/field_requires_parent_struct_alignment2.rs:LL:CC
+   |
+LL |     unsafe { (*x).packed.x }
+   |              ^^^^^^^^^^^^^ accessing memory 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
+
diff --git a/tests/ui/const-generics/generic_const_exprs/issue-80742.rs b/tests/ui/const-generics/generic_const_exprs/issue-80742.rs
index 6b2a0153f51..5f612780f39 100644
--- a/tests/ui/const-generics/generic_const_exprs/issue-80742.rs
+++ b/tests/ui/const-generics/generic_const_exprs/issue-80742.rs
@@ -3,6 +3,7 @@
 // failure-status: 101
 // normalize-stderr-test "note: .*\n\n" -> ""
 // normalize-stderr-test "thread 'rustc' panicked.*\n" -> ""
+// normalize-stderr-test "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: "
 // rustc-env:RUST_BACKTRACE=0
 
 // This test used to cause an ICE in rustc_mir::interpret::step::eval_rvalue_into_place
diff --git a/tests/ui/const-generics/generic_const_exprs/issue-80742.stderr b/tests/ui/const-generics/generic_const_exprs/issue-80742.stderr
index 00b8a397702..9b66fc502b7 100644
--- a/tests/ui/const-generics/generic_const_exprs/issue-80742.stderr
+++ b/tests/ui/const-generics/generic_const_exprs/issue-80742.stderr
@@ -1,9 +1,9 @@
-error: internal compiler error: compiler/rustc_const_eval/src/interpret/step.rs:274:21: SizeOf MIR operator called for unsized type dyn Debug
+error: internal compiler error: compiler/rustc_const_eval/src/interpret/step.rs:LL:CC: SizeOf MIR operator called for unsized type dyn Debug
   --> $SRC_DIR/core/src/mem/mod.rs:LL:COL
 
 Box<dyn Any>
 query stack during panic:
-#0 [eval_to_allocation_raw] const-evaluating + checking `<impl at $DIR/issue-80742.rs:25:1: 27:32>::{constant#0}`
+#0 [eval_to_allocation_raw] const-evaluating + checking `<impl at $DIR/issue-80742.rs:26:1: 28:32>::{constant#0}`
 #1 [eval_to_valtree] evaluating type-level constant
 end of query stack
 error: aborting due to previous error
diff --git a/tests/ui/consts/const-deref-ptr.rs b/tests/ui/consts/const-deref-ptr.rs
index 4aca75e3a17..2607d4de229 100644
--- a/tests/ui/consts/const-deref-ptr.rs
+++ b/tests/ui/consts/const-deref-ptr.rs
@@ -3,5 +3,6 @@
 fn main() {
     static C: u64 = unsafe {*(0xdeadbeef as *const u64)};
     //~^ ERROR could not evaluate static initializer
+    //~| dangling pointer
     println!("{}", C);
 }
diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.rs b/tests/ui/consts/const-eval/raw-pointer-ub.rs
index e53865309eb..e6d60414a3f 100644
--- a/tests/ui/consts/const-eval/raw-pointer-ub.rs
+++ b/tests/ui/consts/const-eval/raw-pointer-ub.rs
@@ -26,6 +26,17 @@ const MISALIGNED_COPY: () = unsafe {
     // The actual error points into the implementation of `copy_to_nonoverlapping`.
 };
 
+const MISALIGNED_FIELD: () = unsafe {
+    #[repr(align(16))]
+    struct Aligned(f32);
+
+    let mem = [0f32; 8];
+    let ptr = mem.as_ptr().cast::<Aligned>();
+    // Accessing an f32 field but we still require the alignment of the pointer type.
+    let _val = (*ptr).0; //~ERROR: evaluation of constant value failed
+    //~^NOTE: accessing memory with alignment 4, but alignment 16 is required
+};
+
 const OOB: () = unsafe {
     let mem = [0u32; 1];
     let ptr = mem.as_ptr().cast::<u64>();
diff --git a/tests/ui/consts/const-eval/raw-pointer-ub.stderr b/tests/ui/consts/const-eval/raw-pointer-ub.stderr
index 5055e0f881a..13e9b0d9dfe 100644
--- a/tests/ui/consts/const-eval/raw-pointer-ub.stderr
+++ b/tests/ui/consts/const-eval/raw-pointer-ub.stderr
@@ -26,11 +26,17 @@ LL |     y.copy_to_nonoverlapping(&mut z, 1);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/raw-pointer-ub.rs:32:16
+  --> $DIR/raw-pointer-ub.rs:36:16
+   |
+LL |     let _val = (*ptr).0;
+   |                ^^^^^^^^ accessing memory with alignment 4, but alignment 16 is required
+
+error[E0080]: evaluation of constant value failed
+  --> $DIR/raw-pointer-ub.rs:43:16
    |
 LL |     let _val = *ptr;
    |                ^^^^ memory access failed: allocN has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds
 
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
 
 For more information about this error, try `rustc --explain E0080`.