Skip to content

Commit f3f9b79

Browse files
committed
place evaluation: require the original pointer to be aligned if an access happens
1 parent ea9a24e commit f3f9b79

File tree

22 files changed

+266
-219
lines changed

22 files changed

+266
-219
lines changed

compiler/rustc_abi/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ impl fmt::Display for AlignFromBytesError {
681681

682682
impl Align {
683683
pub const ONE: Align = Align { pow2: 0 };
684+
// LLVM has a maximal supported alignment of 2^29, we inherit that.
684685
pub const MAX: Align = Align { pow2: 29 };
685686

686687
#[inline]

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::mem;
2+
13
use either::{Left, Right};
24

35
use rustc_hir::def::DefKind;
@@ -73,9 +75,9 @@ fn eval_body_using_ecx<'mir, 'tcx>(
7375
None => InternKind::Constant,
7476
}
7577
};
76-
ecx.machine.check_alignment = CheckAlignment::No; // interning doesn't need to respect alignment
78+
let check_alignment = mem::replace(&mut ecx.machine.check_alignment, CheckAlignment::No); // interning doesn't need to respect alignment
7779
intern_const_alloc_recursive(ecx, intern_kind, &ret)?;
78-
// we leave alignment checks off, since this `ecx` will not be used for further evaluation anyway
80+
ecx.machine.check_alignment = check_alignment;
7981

8082
debug!("eval_body_using_ecx done: {:?}", ret);
8183
Ok(ret)

compiler/rustc_const_eval/src/errors.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use rustc_errors::{
55
use rustc_hir::ConstContext;
66
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
77
use rustc_middle::mir::interpret::{
8-
CheckInAllocMsg, ExpectedKind, InterpError, InvalidMetaKind, InvalidProgramInfo, PointerKind,
9-
ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
8+
CheckInAllocMsg, ExpectedKind, InterpError, InvalidMetaKind, InvalidProgramInfo, Misalignment,
9+
PointerKind, ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo,
10+
ValidationErrorInfo,
1011
};
1112
use rustc_middle::ty::{self, Ty};
1213
use rustc_span::Span;
@@ -567,7 +568,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
567568

568569
builder.set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
569570
}
570-
AlignmentCheckFailed { required, has } => {
571+
AlignmentCheckFailed(Misalignment { required, has }) => {
571572
builder.set_arg("required", required.bytes());
572573
builder.set_arg("has", has.bytes());
573574
}

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 60 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ use crate::fluent_generated as fluent;
2222

2323
use super::{
2424
alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg,
25-
GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance,
26-
Scalar,
25+
GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer,
26+
PointerArithmetic, Provenance, Scalar,
2727
};
2828

2929
#[derive(Debug, PartialEq, Copy, Clone)]
@@ -372,7 +372,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
372372
self.check_and_deref_ptr(
373373
ptr,
374374
size,
375-
M::enforce_alignment(self).then_some(align),
375+
align,
376376
CheckInAllocMsg::MemoryAccessTest,
377377
|alloc_id, offset, prov| {
378378
let (size, align) = self
@@ -382,9 +382,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
382382
)
383383
}
384384

385-
/// Check if the given pointer points to live memory of given `size` and `align`
386-
/// (ignoring `M::enforce_alignment`). The caller can control the error message for the
387-
/// out-of-bounds case.
385+
/// Check if the given pointer points to live memory of given `size` and `align`.
386+
/// The caller can control the error message for the out-of-bounds case.
388387
#[inline(always)]
389388
pub fn check_ptr_access_align(
390389
&self,
@@ -393,7 +392,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
393392
align: Align,
394393
msg: CheckInAllocMsg,
395394
) -> InterpResult<'tcx> {
396-
self.check_and_deref_ptr(ptr, size, Some(align), msg, |alloc_id, _, _| {
395+
self.check_and_deref_ptr(ptr, size, align, msg, |alloc_id, _, _| {
397396
let (size, align) = self.get_live_alloc_size_and_align(alloc_id, msg)?;
398397
Ok((size, align, ()))
399398
})?;
@@ -402,15 +401,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
402401

403402
/// Low-level helper function to check if a ptr is in-bounds and potentially return a reference
404403
/// to the allocation it points to. Supports both shared and mutable references, as the actual
405-
/// checking is offloaded to a helper closure. `align` defines whether and which alignment check
406-
/// is done.
404+
/// checking is offloaded to a helper closure.
407405
///
408406
/// If this returns `None`, the size is 0; it can however return `Some` even for size 0.
409407
fn check_and_deref_ptr<T>(
410408
&self,
411409
ptr: Pointer<Option<M::Provenance>>,
412410
size: Size,
413-
align: Option<Align>,
411+
align: Align,
414412
msg: CheckInAllocMsg,
415413
alloc_size: impl FnOnce(
416414
AllocId,
@@ -426,9 +424,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
426424
throw_ub!(DanglingIntPointer(addr, msg));
427425
}
428426
// Must be aligned.
429-
if let Some(align) = align {
430-
self.check_offset_align(addr, align)?;
431-
}
427+
self.check_misalign(Self::offset_misalignment(addr, align))?;
432428
None
433429
}
434430
Ok((alloc_id, offset, prov)) => {
@@ -450,18 +446,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
450446
}
451447
// Test align. Check this last; if both bounds and alignment are violated
452448
// we want the error to be about the bounds.
453-
if let Some(align) = align {
454-
if M::use_addr_for_alignment_check(self) {
455-
// `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
456-
self.check_offset_align(ptr.addr().bytes(), align)?;
457-
} else {
458-
// Check allocation alignment and offset alignment.
459-
if alloc_align.bytes() < align.bytes() {
460-
throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align });
461-
}
462-
self.check_offset_align(offset.bytes(), align)?;
463-
}
464-
}
449+
self.check_misalign(self.alloc_misalignment(ptr, offset, align, alloc_align))?;
465450

466451
// We can still be zero-sized in this branch, in which case we have to
467452
// return `None`.
@@ -470,16 +455,59 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
470455
})
471456
}
472457

473-
fn check_offset_align(&self, offset: u64, align: Align) -> InterpResult<'tcx> {
458+
#[inline(always)]
459+
pub(super) fn check_misalign(&self, misaligned: Option<Misalignment>) -> InterpResult<'tcx> {
460+
if M::enforce_alignment(self) {
461+
if let Some(misaligned) = misaligned {
462+
throw_ub!(AlignmentCheckFailed(misaligned))
463+
}
464+
}
465+
Ok(())
466+
}
467+
468+
#[must_use]
469+
fn offset_misalignment(offset: u64, align: Align) -> Option<Misalignment> {
474470
if offset % align.bytes() == 0 {
475-
Ok(())
471+
None
476472
} else {
477473
// The biggest power of two through which `offset` is divisible.
478474
let offset_pow2 = 1 << offset.trailing_zeros();
479-
throw_ub!(AlignmentCheckFailed {
480-
has: Align::from_bytes(offset_pow2).unwrap(),
481-
required: align
482-
});
475+
Some(Misalignment { has: Align::from_bytes(offset_pow2).unwrap(), required: align })
476+
}
477+
}
478+
479+
#[must_use]
480+
fn alloc_misalignment(
481+
&self,
482+
ptr: Pointer<Option<M::Provenance>>,
483+
offset: Size,
484+
align: Align,
485+
alloc_align: Align,
486+
) -> Option<Misalignment> {
487+
if M::use_addr_for_alignment_check(self) {
488+
// `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
489+
Self::offset_misalignment(ptr.addr().bytes(), align)
490+
} else {
491+
// Check allocation alignment and offset alignment.
492+
if alloc_align.bytes() < align.bytes() {
493+
Some(Misalignment { has: alloc_align, required: align })
494+
} else {
495+
Self::offset_misalignment(offset.bytes(), align)
496+
}
497+
}
498+
}
499+
500+
pub(super) fn is_ptr_misaligned(
501+
&self,
502+
ptr: Pointer<Option<M::Provenance>>,
503+
align: Align,
504+
) -> Option<Misalignment> {
505+
match self.ptr_try_get_alloc_id(ptr) {
506+
Err(addr) => Self::offset_misalignment(addr, align),
507+
Ok((alloc_id, offset, _prov)) => {
508+
let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id);
509+
self.alloc_misalignment(ptr, offset, align, alloc_align)
510+
}
483511
}
484512
}
485513
}
@@ -597,7 +625,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
597625
let ptr_and_alloc = self.check_and_deref_ptr(
598626
ptr,
599627
size,
600-
M::enforce_alignment(self).then_some(align),
628+
align,
601629
CheckInAllocMsg::MemoryAccessTest,
602630
|alloc_id, offset, prov| {
603631
let alloc = self.get_alloc_raw(alloc_id)?;

compiler/rustc_const_eval/src/interpret/operand.rs

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
1010
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
1111
use rustc_middle::ty::{ConstInt, Ty, TyCtxt};
1212
use rustc_middle::{mir, ty};
13-
use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
13+
use rustc_target::abi::{self, Abi, HasDataLayout, Size};
1414

1515
use super::{
1616
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> {
4444
}
4545

4646
impl<Prov: Provenance> Immediate<Prov> {
47-
pub fn from_pointer(ptr: Pointer<Prov>, cx: &impl HasDataLayout) -> Self {
48-
Immediate::Scalar(Scalar::from_pointer(ptr, cx))
49-
}
50-
51-
pub fn from_maybe_pointer(ptr: Pointer<Option<Prov>>, cx: &impl HasDataLayout) -> Self {
52-
Immediate::Scalar(Scalar::from_maybe_pointer(ptr, cx))
47+
pub fn new_pointer_with_meta(
48+
ptr: Pointer<Option<Prov>>,
49+
meta: MemPlaceMeta<Prov>,
50+
cx: &impl HasDataLayout,
51+
) -> Self {
52+
let ptr = Scalar::from_maybe_pointer(ptr, cx);
53+
match meta {
54+
MemPlaceMeta::None => Immediate::from(ptr),
55+
MemPlaceMeta::Meta(meta) => Immediate::ScalarPair(ptr, meta),
56+
}
5357
}
5458

5559
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> {
328332
pub struct OpTy<'tcx, Prov: Provenance = AllocId> {
329333
op: Operand<Prov>, // Keep this private; it helps enforce invariants.
330334
pub layout: TyAndLayout<'tcx>,
331-
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
332-
/// it needs to have a different alignment than the field type would usually have.
333-
/// So we represent this here with a separate field that "overwrites" `layout.align`.
334-
/// This means `layout.align` should never be used for an `OpTy`!
335-
/// `None` means "alignment does not matter since this is a by-value operand"
336-
/// (`Operand::Immediate`); this field is only relevant for `Operand::Indirect`.
337-
/// Also CTFE ignores alignment anyway, so this is for Miri only.
338-
pub align: Option<Align>,
339335
}
340336

341337
impl<Prov: Provenance> std::fmt::Debug for OpTy<'_, Prov> {
@@ -351,18 +347,14 @@ impl<Prov: Provenance> std::fmt::Debug for OpTy<'_, Prov> {
351347
impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
352348
#[inline(always)]
353349
fn from(val: ImmTy<'tcx, Prov>) -> Self {
354-
OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None }
350+
OpTy { op: Operand::Immediate(val.imm), layout: val.layout }
355351
}
356352
}
357353

358354
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
359355
#[inline(always)]
360356
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
361-
OpTy {
362-
op: Operand::Indirect(*mplace.mplace()),
363-
layout: mplace.layout,
364-
align: Some(mplace.align),
365-
}
357+
OpTy { op: Operand::Indirect(*mplace.mplace()), layout: mplace.layout }
366358
}
367359
}
368360

@@ -635,7 +627,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
635627
throw_inval!(ConstPropNonsense);
636628
}
637629
}
638-
Ok(OpTy { op, layout, align: Some(layout.align.abi) })
630+
Ok(OpTy { op, layout })
639631
}
640632

641633
/// 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> {
650642
Right((frame, local, offset)) => {
651643
debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`.
652644
let base = self.local_to_op(&self.stack()[frame], local, None)?;
653-
let mut field = match offset {
645+
Ok(match offset {
654646
Some(offset) => base.offset(offset, place.layout, self)?,
655647
None => {
656648
// In the common case this hasn't been projected.
657649
debug_assert_eq!(place.layout, base.layout);
658650
base
659651
}
660-
};
661-
field.align = Some(place.align);
662-
Ok(field)
652+
})
663653
}
664654
}
665655
}
@@ -747,27 +737,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
747737
})
748738
};
749739
let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
750-
let op = match val_val {
740+
let imm = match val_val {
751741
mir::ConstValue::Indirect { alloc_id, offset } => {
752742
// We rely on mutability being set correctly in that allocation to prevent writes
753743
// where none should happen.
754744
let ptr = self.global_base_pointer(Pointer::new(alloc_id, offset))?;
755-
Operand::Indirect(MemPlace::from_ptr(ptr.into()))
745+
return Ok(self.ptr_to_mplace(ptr.into(), layout).into());
756746
}
757-
mir::ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()),
758-
mir::ConstValue::ZeroSized => Operand::Immediate(Immediate::Uninit),
747+
mir::ConstValue::Scalar(x) => adjust_scalar(x)?.into(),
748+
mir::ConstValue::ZeroSized => Immediate::Uninit,
759749
mir::ConstValue::Slice { data, meta } => {
760750
// We rely on mutability being set correctly in `data` to prevent writes
761751
// where none should happen.
762752
let ptr = Pointer::new(self.tcx.reserve_and_set_memory_alloc(data), Size::ZERO);
763-
Operand::Immediate(Immediate::new_slice(
764-
self.global_base_pointer(ptr)?.into(),
765-
meta,
766-
self,
767-
))
753+
Immediate::new_slice(self.global_base_pointer(ptr)?.into(), meta, self)
768754
}
769755
};
770-
Ok(OpTy { op, layout, align: Some(layout.align.abi) })
756+
Ok(OpTy { op: Operand::Immediate(imm), layout })
771757
}
772758
}
773759

@@ -780,6 +766,6 @@ mod size_asserts {
780766
static_assert_size!(Immediate, 48);
781767
static_assert_size!(ImmTy<'_>, 64);
782768
static_assert_size!(Operand, 56);
783-
static_assert_size!(OpTy<'_>, 80);
769+
static_assert_size!(OpTy<'_>, 72);
784770
// tidy-alphabetical-end
785771
}

0 commit comments

Comments
 (0)