Skip to content

Commit e7bdc5f

Browse files
committed
Auto merge of #114330 - RalfJung:dagling-ptr-deref, r=oli-obk
don't UB on dangling ptr deref, instead check inbounds on projections This implements rust-lang/reference#1387 in Miri. See that PR for what the change is about. Detecting dangling references in `let x = &...;` is now done by validity checking only, so some tests need to have validity checking enabled. There is no longer inherently a "nodangle" check in evaluating the expression `&*ptr` (aside from the aliasing model). r? `@oli-obk` Based on: - rust-lang/reference#1387 - #115524
2 parents a00c09e + 1ac153f commit e7bdc5f

File tree

142 files changed

+986
-845
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

142 files changed

+986
-845
lines changed

compiler/rustc_abi/src/lib.rs

+1
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/messages.ftl

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
const_eval_address_space_full =
22
there are no more free addresses in the address space
3-
const_eval_align_check_failed = accessing memory with alignment {$has}, but alignment {$required} is required
3+
44
const_eval_align_offset_invalid_align =
55
`align_offset` called with non-power-of-two align: {$target_align}
66
77
const_eval_alignment_check_failed =
8-
accessing memory with alignment {$has}, but alignment {$required} is required
8+
{$msg ->
9+
[AccessedPtr] accessing memory
10+
*[other] accessing memory based on pointer
11+
} with alignment {$has}, but alignment {$required} is required
12+
913
const_eval_already_reported =
1014
an error has already been reported elsewhere (this should not usually be printed)
1115
const_eval_assume_false =
@@ -61,7 +65,6 @@ const_eval_deref_coercion_non_const =
6165
.target_note = deref defined here
6266
const_eval_deref_function_pointer =
6367
accessing {$allocation} which contains a function
64-
const_eval_deref_test = dereferencing pointer failed
6568
const_eval_deref_vtable_pointer =
6669
accessing {$allocation} which contains a vtable
6770
const_eval_different_allocations =

compiler/rustc_const_eval/src/const_eval/eval_queries.rs

+4-2
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

+5-13
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;
@@ -389,15 +390,6 @@ pub struct LiveDrop<'tcx> {
389390
pub dropped_at: Option<Span>,
390391
}
391392

392-
#[derive(LintDiagnostic)]
393-
#[diag(const_eval_align_check_failed)]
394-
pub struct AlignmentCheckFailed {
395-
pub has: u64,
396-
pub required: u64,
397-
#[subdiagnostic]
398-
pub frames: Vec<FrameNote>,
399-
}
400-
401393
#[derive(Diagnostic)]
402394
#[diag(const_eval_error, code = "E0080")]
403395
pub struct ConstEvalError {
@@ -459,7 +451,6 @@ fn bad_pointer_message(msg: CheckInAllocMsg, handler: &Handler) -> String {
459451
use crate::fluent_generated::*;
460452

461453
let msg = match msg {
462-
CheckInAllocMsg::DerefTest => const_eval_deref_test,
463454
CheckInAllocMsg::MemoryAccessTest => const_eval_memory_access_test,
464455
CheckInAllocMsg::PointerArithmeticTest => const_eval_pointer_arithmetic_test,
465456
CheckInAllocMsg::OffsetFromTest => const_eval_offset_from_test,
@@ -568,9 +559,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
568559

569560
builder.set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
570561
}
571-
AlignmentCheckFailed { required, has } => {
562+
AlignmentCheckFailed(Misalignment { required, has }, msg) => {
572563
builder.set_arg("required", required.bytes());
573564
builder.set_arg("has", has.bytes());
565+
builder.set_arg("msg", format!("{msg:?}"));
574566
}
575567
WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) => {
576568
builder.set_arg("allocation", alloc);

compiler/rustc_const_eval/src/interpret/intern.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
161161

162162
#[inline(always)]
163163
fn ecx(&self) -> &InterpCx<'mir, 'tcx, M> {
164-
&self.ecx
164+
self.ecx
165165
}
166166

167167
fn visit_value(&mut self, mplace: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
@@ -259,15 +259,15 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
259259
// to avoid could be expensive: on the potentially larger types, arrays and slices,
260260
// rather than on all aggregates unconditionally.
261261
if matches!(mplace.layout.ty.kind(), ty::Array(..) | ty::Slice(..)) {
262-
let Some((size, align)) = self.ecx.size_and_align_of_mplace(&mplace)? else {
262+
let Some((size, _align)) = self.ecx.size_and_align_of_mplace(&mplace)? else {
263263
// We do the walk if we can't determine the size of the mplace: we may be
264264
// dealing with extern types here in the future.
265265
return Ok(true);
266266
};
267267

268268
// If there is no provenance in this allocation, it does not contain references
269269
// that point to another allocation, and we can avoid the interning walk.
270-
if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr(), size, align)? {
270+
if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr(), size)? {
271271
if !alloc.has_provenance() {
272272
return Ok(false);
273273
}

compiler/rustc_const_eval/src/interpret/intrinsics.rs

+9-16
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_middle::ty::layout::{LayoutOf as _, ValidityRequirement};
1313
use rustc_middle::ty::GenericArgsRef;
1414
use rustc_middle::ty::{Ty, TyCtxt};
1515
use rustc_span::symbol::{sym, Symbol};
16-
use rustc_target::abi::{Abi, Align, Primitive, Size};
16+
use rustc_target::abi::{Abi, Primitive, Size};
1717

1818
use super::{
1919
util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy,
@@ -349,10 +349,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
349349
// Check that the range between them is dereferenceable ("in-bounds or one past the
350350
// end of the same allocation"). This is like the check in ptr_offset_inbounds.
351351
let min_ptr = if dist >= 0 { b } else { a };
352-
self.check_ptr_access_align(
352+
self.check_ptr_access(
353353
min_ptr,
354354
Size::from_bytes(dist.unsigned_abs()),
355-
Align::ONE,
356355
CheckInAllocMsg::OffsetFromTest,
357356
)?;
358357

@@ -571,16 +570,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
571570
pub fn ptr_offset_inbounds(
572571
&self,
573572
ptr: Pointer<Option<M::Provenance>>,
574-
pointee_ty: Ty<'tcx>,
575-
offset_count: i64,
573+
offset_bytes: i64,
576574
) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
577-
// We cannot overflow i64 as a type's size must be <= isize::MAX.
578-
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
579-
// The computed offset, in bytes, must not overflow an isize.
580-
// `checked_mul` enforces a too small bound, but no actual allocation can be big enough for
581-
// the difference to be noticeable.
582-
let offset_bytes =
583-
offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?;
584575
// The offset being in bounds cannot rely on "wrapping around" the address space.
585576
// So, first rule out overflows in the pointer arithmetic.
586577
let offset_ptr = ptr.signed_offset(offset_bytes, self)?;
@@ -589,10 +580,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
589580
// pointers to be properly aligned (unlike a read/write operation).
590581
let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr };
591582
// This call handles checking for integer/null pointers.
592-
self.check_ptr_access_align(
583+
self.check_ptr_access(
593584
min_ptr,
594585
Size::from_bytes(offset_bytes.unsigned_abs()),
595-
Align::ONE,
596586
CheckInAllocMsg::PointerArithmeticTest,
597587
)?;
598588
Ok(offset_ptr)
@@ -621,7 +611,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
621611
let src = self.read_pointer(src)?;
622612
let dst = self.read_pointer(dst)?;
623613

624-
self.mem_copy(src, align, dst, align, size, nonoverlapping)
614+
self.check_ptr_align(src, align)?;
615+
self.check_ptr_align(dst, align)?;
616+
617+
self.mem_copy(src, dst, size, nonoverlapping)
625618
}
626619

627620
pub(crate) fn write_bytes_intrinsic(
@@ -677,7 +670,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
677670
size|
678671
-> InterpResult<'tcx, &[u8]> {
679672
let ptr = this.read_pointer(op)?;
680-
let Some(alloc_ref) = self.get_ptr_alloc(ptr, size, Align::ONE)? else {
673+
let Some(alloc_ref) = self.get_ptr_alloc(ptr, size)? else {
681674
// zero-sized access
682675
return Ok(&[]);
683676
};

compiler/rustc_const_eval/src/interpret/machine.rs

+1
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
436436
place: &PlaceTy<'tcx, Self::Provenance>,
437437
) -> InterpResult<'tcx> {
438438
// Without an aliasing model, all we can do is put `Uninit` into the place.
439+
// Conveniently this also ensures that the place actually points to suitable memory.
439440
ecx.write_uninit(place)
440441
}
441442

0 commit comments

Comments
 (0)