Skip to content

Commit 136dab6

Browse files
committed
Auto merge of #113569 - RalfJung:miri, r=oli-obk
miri: protect Move() function arguments during the call This gives `Move` operands a meaning specific to function calls: - for the duration of the call, the place the operand comes from is protected, making all read and write accesses insta-UB. - the contents of that place are reset to `Uninit`, so looking at them again after the function returns, we cannot observe their contents Turns out we can replace the existing "retag return place" hack with the exact same sort of protection on the return place, which is nicely symmetric. Fixes #112564 Fixes rust-lang/miri#2927 This starts with a Miri rustc-push, since we'd otherwise conflict with a PR that recently landed in Miri. (The "miri tree borrows" commit is an unrelated cleanup I noticed while doing the PR. I can remove it if you prefer.) r? `@oli-obk`
2 parents 910be1b + e7c6db7 commit 136dab6

37 files changed

+747
-161
lines changed

compiler/rustc_const_eval/src/const_eval/machine.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use rustc_target::spec::abi::Abi as CallAbi;
2222

2323
use crate::errors::{LongRunning, LongRunningWarn};
2424
use crate::interpret::{
25-
self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx,
25+
self, compile_time_machine, AllocId, ConstAllocation, FnArg, FnVal, Frame, ImmTy, InterpCx,
2626
InterpResult, OpTy, PlaceTy, Pointer, Scalar,
2727
};
2828
use crate::{errors, fluent_generated as fluent};
@@ -201,7 +201,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
201201
fn hook_special_const_fn(
202202
&mut self,
203203
instance: ty::Instance<'tcx>,
204-
args: &[OpTy<'tcx>],
204+
args: &[FnArg<'tcx>],
205205
dest: &PlaceTy<'tcx>,
206206
ret: Option<mir::BasicBlock>,
207207
) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
@@ -210,6 +210,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
210210
if Some(def_id) == self.tcx.lang_items().panic_display()
211211
|| Some(def_id) == self.tcx.lang_items().begin_panic_fn()
212212
{
213+
let args = self.copy_fn_args(args)?;
213214
// &str or &&str
214215
assert!(args.len() == 1);
215216

@@ -236,8 +237,9 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
236237

237238
return Ok(Some(new_instance));
238239
} else if Some(def_id) == self.tcx.lang_items().align_offset_fn() {
240+
let args = self.copy_fn_args(args)?;
239241
// For align_offset, we replace the function call if the pointer has no address.
240-
match self.align_offset(instance, args, dest, ret)? {
242+
match self.align_offset(instance, &args, dest, ret)? {
241243
ControlFlow::Continue(()) => return Ok(Some(instance)),
242244
ControlFlow::Break(()) => return Ok(None),
243245
}
@@ -293,7 +295,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
293295
self.eval_fn_call(
294296
FnVal::Instance(instance),
295297
(CallAbi::Rust, fn_abi),
296-
&[addr, align],
298+
&[FnArg::Copy(addr), FnArg::Copy(align)],
297299
/* with_caller_location = */ false,
298300
dest,
299301
ret,
@@ -427,7 +429,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
427429
ecx: &mut InterpCx<'mir, 'tcx, Self>,
428430
instance: ty::Instance<'tcx>,
429431
_abi: CallAbi,
430-
args: &[OpTy<'tcx>],
432+
args: &[FnArg<'tcx>],
431433
dest: &PlaceTy<'tcx>,
432434
ret: Option<mir::BasicBlock>,
433435
_unwind: mir::UnwindAction, // unwinding is not supported in consts

compiler/rustc_const_eval/src/interpret/eval_context.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -682,11 +682,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
682682
return_to_block: StackPopCleanup,
683683
) -> InterpResult<'tcx> {
684684
trace!("body: {:#?}", body);
685-
// Clobber previous return place contents, nobody is supposed to be able to see them any more
686-
// This also checks dereferenceable, but not align. We rely on all constructed places being
687-
// sufficiently aligned (in particular we rely on `deref_operand` checking alignment).
688-
self.write_uninit(return_place)?;
689-
// first push a stack frame so we have access to the local substs
685+
// First push a stack frame so we have access to the local substs
690686
let pre_frame = Frame {
691687
body,
692688
loc: Right(body.span), // Span used for errors caused during preamble.
@@ -805,6 +801,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
805801
throw_ub_custom!(fluent::const_eval_unwind_past_top);
806802
}
807803

804+
M::before_stack_pop(self, self.frame())?;
805+
808806
// Copy return value. Must of course happen *before* we deallocate the locals.
809807
let copy_ret_result = if !unwinding {
810808
let op = self

compiler/rustc_const_eval/src/interpret/intern.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use super::{
3030
use crate::const_eval;
3131
use crate::errors::{DanglingPtrInFinal, UnsupportedUntypedPointer};
3232

33-
pub trait CompileTimeMachine<'mir, 'tcx, T> = Machine<
33+
pub trait CompileTimeMachine<'mir, 'tcx: 'mir, T> = Machine<
3434
'mir,
3535
'tcx,
3636
MemoryKind = T,

compiler/rustc_const_eval/src/interpret/machine.rs

+25-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_target::spec::abi::Abi as CallAbi;
1717
use crate::const_eval::CheckAlignment;
1818

1919
use super::{
20-
AllocBytes, AllocId, AllocRange, Allocation, ConstAllocation, Frame, ImmTy, InterpCx,
20+
AllocBytes, AllocId, AllocRange, Allocation, ConstAllocation, FnArg, Frame, ImmTy, InterpCx,
2121
InterpResult, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar,
2222
};
2323

@@ -84,7 +84,7 @@ pub trait AllocMap<K: Hash + Eq, V> {
8484

8585
/// Methods of this trait signifies a point where CTFE evaluation would fail
8686
/// and some use case dependent behaviour can instead be applied.
87-
pub trait Machine<'mir, 'tcx>: Sized {
87+
pub trait Machine<'mir, 'tcx: 'mir>: Sized {
8888
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
8989
type MemoryKind: Debug + std::fmt::Display + MayLeak + Eq + 'static;
9090

@@ -182,7 +182,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
182182
ecx: &mut InterpCx<'mir, 'tcx, Self>,
183183
instance: ty::Instance<'tcx>,
184184
abi: CallAbi,
185-
args: &[OpTy<'tcx, Self::Provenance>],
185+
args: &[FnArg<'tcx, Self::Provenance>],
186186
destination: &PlaceTy<'tcx, Self::Provenance>,
187187
target: Option<mir::BasicBlock>,
188188
unwind: mir::UnwindAction,
@@ -194,7 +194,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
194194
ecx: &mut InterpCx<'mir, 'tcx, Self>,
195195
fn_val: Self::ExtraFnVal,
196196
abi: CallAbi,
197-
args: &[OpTy<'tcx, Self::Provenance>],
197+
args: &[FnArg<'tcx, Self::Provenance>],
198198
destination: &PlaceTy<'tcx, Self::Provenance>,
199199
target: Option<mir::BasicBlock>,
200200
unwind: mir::UnwindAction,
@@ -418,6 +418,18 @@ pub trait Machine<'mir, 'tcx>: Sized {
418418
Ok(())
419419
}
420420

421+
/// Called on places used for in-place function argument and return value handling.
422+
///
423+
/// These places need to be protected to make sure the program cannot tell whether the
424+
/// argument/return value was actually copied or passed in-place..
425+
fn protect_in_place_function_argument(
426+
ecx: &mut InterpCx<'mir, 'tcx, Self>,
427+
place: &PlaceTy<'tcx, Self::Provenance>,
428+
) -> InterpResult<'tcx> {
429+
// Without an aliasing model, all we can do is put `Uninit` into the place.
430+
ecx.write_uninit(place)
431+
}
432+
421433
/// Called immediately before a new stack frame gets pushed.
422434
fn init_frame_extra(
423435
ecx: &mut InterpCx<'mir, 'tcx, Self>,
@@ -439,6 +451,14 @@ pub trait Machine<'mir, 'tcx>: Sized {
439451
Ok(())
440452
}
441453

454+
/// Called just before the return value is copied to the caller-provided return place.
455+
fn before_stack_pop(
456+
_ecx: &InterpCx<'mir, 'tcx, Self>,
457+
_frame: &Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
458+
) -> InterpResult<'tcx> {
459+
Ok(())
460+
}
461+
442462
/// Called immediately after a stack frame got popped, but before jumping back to the caller.
443463
/// The `locals` have already been destroyed!
444464
fn after_stack_pop(
@@ -484,7 +504,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
484504
_ecx: &mut InterpCx<$mir, $tcx, Self>,
485505
fn_val: !,
486506
_abi: CallAbi,
487-
_args: &[OpTy<$tcx>],
507+
_args: &[FnArg<$tcx>],
488508
_destination: &PlaceTy<$tcx, Self::Provenance>,
489509
_target: Option<mir::BasicBlock>,
490510
_unwind: mir::UnwindAction,

compiler/rustc_const_eval/src/interpret/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackP
2626
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
2727
pub use self::operand::{ImmTy, Immediate, OpTy, Operand};
2828
pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy};
29+
pub use self::terminator::FnArg;
2930
pub use self::validity::{CtfeValidationMode, RefTracking};
3031
pub use self::visitor::{MutValueVisitor, Value, ValueVisitor};
3132

compiler/rustc_const_eval/src/interpret/operand.rs

-8
Original file line numberDiff line numberDiff line change
@@ -575,14 +575,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
575575
Ok(op)
576576
}
577577

578-
/// Evaluate a bunch of operands at once
579-
pub(super) fn eval_operands(
580-
&self,
581-
ops: &[mir::Operand<'tcx>],
582-
) -> InterpResult<'tcx, Vec<OpTy<'tcx, M::Provenance>>> {
583-
ops.iter().map(|op| self.eval_operand(op, None)).collect()
584-
}
585-
586578
fn eval_ty_constant(
587579
&self,
588580
val: ty::Const<'tcx>,

compiler/rustc_const_eval/src/interpret/place.rs

+18-14
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,8 @@ where
328328
};
329329

330330
let mplace = MemPlace { ptr: ptr.to_pointer(self)?, meta };
331-
// When deref'ing a pointer, the *static* alignment given by the type is what matters.
331+
// `ref_to_mplace` is called on raw pointers even if they don't actually get dereferenced;
332+
// we hence can't call `size_and_align_of` since that asserts more validity than we want.
332333
let align = layout.align.abi;
333334
Ok(MPlaceTy { mplace, layout, align })
334335
}
@@ -354,34 +355,37 @@ where
354355
#[inline]
355356
pub(super) fn get_place_alloc(
356357
&self,
357-
place: &MPlaceTy<'tcx, M::Provenance>,
358+
mplace: &MPlaceTy<'tcx, M::Provenance>,
358359
) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
359360
{
360-
assert!(place.layout.is_sized());
361-
assert!(!place.meta.has_meta());
362-
let size = place.layout.size;
363-
self.get_ptr_alloc(place.ptr, size, place.align)
361+
let (size, _align) = self
362+
.size_and_align_of_mplace(&mplace)?
363+
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
364+
// Due to packed places, only `mplace.align` matters.
365+
self.get_ptr_alloc(mplace.ptr, size, mplace.align)
364366
}
365367

366368
#[inline]
367369
pub(super) fn get_place_alloc_mut(
368370
&mut self,
369-
place: &MPlaceTy<'tcx, M::Provenance>,
371+
mplace: &MPlaceTy<'tcx, M::Provenance>,
370372
) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>>
371373
{
372-
assert!(place.layout.is_sized());
373-
assert!(!place.meta.has_meta());
374-
let size = place.layout.size;
375-
self.get_ptr_alloc_mut(place.ptr, size, place.align)
374+
let (size, _align) = self
375+
.size_and_align_of_mplace(&mplace)?
376+
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
377+
// Due to packed places, only `mplace.align` matters.
378+
self.get_ptr_alloc_mut(mplace.ptr, size, mplace.align)
376379
}
377380

378381
/// Check if this mplace is dereferenceable and sufficiently aligned.
379382
pub fn check_mplace(&self, mplace: MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
380-
let (size, align) = self
383+
let (size, _align) = self
381384
.size_and_align_of_mplace(&mplace)?
382385
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
383-
assert!(mplace.align <= align, "dynamic alignment less strict than static one?");
384-
let align = if M::enforce_alignment(self).should_check() { align } else { Align::ONE };
386+
// Due to packed places, only `mplace.align` matters.
387+
let align =
388+
if M::enforce_alignment(self).should_check() { mplace.align } else { Align::ONE };
385389
self.check_ptr_access_align(mplace.ptr, size, align, CheckInAllocMsg::DerefTest)?;
386390
Ok(())
387391
}

0 commit comments

Comments
 (0)