Skip to content

Commit 2005c2e

Browse files
committed
interpret: ensure that Place is never used for a different frame
1 parent 1678c5c commit 2005c2e

File tree

4 files changed

+46
-33
lines changed

4 files changed

+46
-33
lines changed

compiler/rustc_const_eval/src/interpret/eval_context.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::cell::Cell;
2+
use std::ptr;
23
use std::{fmt, mem};
34

45
use either::{Either, Left, Right};
@@ -279,6 +280,11 @@ impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> {
279280
}
280281
})
281282
}
283+
284+
#[inline(always)]
285+
pub(super) fn locals_addr(&self) -> usize {
286+
ptr::addr_of!(self.locals).addr()
287+
}
282288
}
283289

284290
// FIXME: only used by miri, should be removed once translatable.
@@ -1212,18 +1218,16 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
12121218
{
12131219
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12141220
match self.place {
1215-
Place::Local { frame, local, offset } => {
1221+
Place::Local { local, offset, locals_addr } => {
1222+
debug_assert_eq!(locals_addr, self.ecx.frame().locals_addr());
12161223
let mut allocs = Vec::new();
12171224
write!(fmt, "{local:?}")?;
12181225
if let Some(offset) = offset {
12191226
write!(fmt, "+{:#x}", offset.bytes())?;
12201227
}
1221-
if frame != self.ecx.frame_idx() {
1222-
write!(fmt, " ({} frames up)", self.ecx.frame_idx() - frame)?;
1223-
}
12241228
write!(fmt, ":")?;
12251229

1226-
match self.ecx.stack()[frame].locals[local].value {
1230+
match self.ecx.frame().locals[local].value {
12271231
LocalValue::Dead => write!(fmt, " is dead")?,
12281232
LocalValue::Live(Operand::Immediate(Immediate::Uninit)) => {
12291233
write!(fmt, " is uninitialized")?

compiler/rustc_const_eval/src/interpret/operand.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -661,9 +661,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
661661
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
662662
match place.as_mplace_or_local() {
663663
Left(mplace) => Ok(mplace.into()),
664-
Right((frame, local, offset)) => {
664+
Right((local, offset, locals_addr)) => {
665665
debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`.
666-
let base = self.local_to_op(&self.stack()[frame], local, None)?;
666+
debug_assert_eq!(locals_addr, self.frame().locals_addr());
667+
let base = self.local_to_op(&self.frame(), local, None)?;
667668
Ok(match offset {
668669
Some(offset) => base.offset(offset, place.layout, self)?,
669670
None => {

compiler/rustc_const_eval/src/interpret/place.rs

+33-26
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,13 @@ pub(super) enum Place<Prov: Provenance = CtfeProvenance> {
187187
/// where in the local this place is located; if it is `None`, no projection has been applied.
188188
/// Such projections are meaningful even if the offset is 0, since they can change layouts.
189189
/// (Without that optimization, we'd just always be a `MemPlace`.)
190-
/// Note that this only stores the frame index, not the thread this frame belongs to -- that is
191-
/// implicit. This means a `Place` must never be moved across interpreter thread boundaries!
190+
/// `Local` places always refer to the current stack frame, so they are unstable under
191+
/// function calls/returns and switching betweens stacks of different threads!
192+
/// We carry around the address of the `locals` buffer of the correct stack frame as a sanity
193+
/// chec to be able to catch some cases of using a dangling `Place`.
192194
///
193195
/// This variant shall not be used for unsized types -- those must always live in memory.
194-
Local { frame: usize, local: mir::Local, offset: Option<Size> },
196+
Local { local: mir::Local, offset: Option<Size>, locals_addr: usize },
195197
}
196198

197199
/// An evaluated place, together with its type.
@@ -233,10 +235,10 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
233235
#[inline(always)]
234236
pub fn as_mplace_or_local(
235237
&self,
236-
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>)> {
238+
) -> Either<MPlaceTy<'tcx, Prov>, (mir::Local, Option<Size>, usize)> {
237239
match self.place {
238240
Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout }),
239-
Place::Local { frame, local, offset } => Right((frame, local, offset)),
241+
Place::Local { local, offset, locals_addr } => Right((local, offset, locals_addr)),
240242
}
241243
}
242244

@@ -279,7 +281,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
279281
) -> InterpResult<'tcx, Self> {
280282
Ok(match self.as_mplace_or_local() {
281283
Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(),
282-
Right((frame, local, old_offset)) => {
284+
Right((local, old_offset, locals_addr)) => {
283285
debug_assert!(layout.is_sized(), "unsized locals should live in memory");
284286
assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway...
285287
// `Place::Local` are always in-bounds of their surrounding local, so we can just
@@ -292,7 +294,10 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
292294
.offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?,
293295
);
294296

295-
PlaceTy { place: Place::Local { frame, local, offset: Some(new_offset) }, layout }
297+
PlaceTy {
298+
place: Place::Local { local, offset: Some(new_offset), locals_addr },
299+
layout,
300+
}
296301
}
297302
})
298303
}
@@ -331,7 +336,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
331336
pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
332337
fn as_mplace_or_local(
333338
&self,
334-
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, TyAndLayout<'tcx>)>;
339+
) -> Either<MPlaceTy<'tcx, Prov>, (mir::Local, Option<Size>, usize, TyAndLayout<'tcx>)>;
335340

336341
fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
337342
&self,
@@ -343,9 +348,9 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
343348
#[inline(always)]
344349
fn as_mplace_or_local(
345350
&self,
346-
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, TyAndLayout<'tcx>)> {
351+
) -> Either<MPlaceTy<'tcx, Prov>, (mir::Local, Option<Size>, usize, TyAndLayout<'tcx>)> {
347352
self.as_mplace_or_local()
348-
.map_right(|(frame, local, offset)| (frame, local, offset, self.layout))
353+
.map_right(|(local, offset, locals_addr)| (local, offset, locals_addr, self.layout))
349354
}
350355

351356
#[inline(always)]
@@ -361,7 +366,7 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
361366
#[inline(always)]
362367
fn as_mplace_or_local(
363368
&self,
364-
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>, TyAndLayout<'tcx>)> {
369+
) -> Either<MPlaceTy<'tcx, Prov>, (mir::Local, Option<Size>, usize, TyAndLayout<'tcx>)> {
365370
Left(self.clone())
366371
}
367372

@@ -512,7 +517,7 @@ where
512517
let layout = self.layout_of_local(frame_ref, local, None)?;
513518
let place = if layout.is_sized() {
514519
// We can just always use the `Local` for sized values.
515-
Place::Local { frame, local, offset: None }
520+
Place::Local { local, offset: None, locals_addr: frame_ref.locals_addr() }
516521
} else {
517522
// Unsized `Local` isn't okay (we cannot store the metadata).
518523
match frame_ref.locals[local].access()? {
@@ -611,23 +616,24 @@ where
611616
// See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
612617
// but not factored as a separate function.
613618
let mplace = match dest.as_mplace_or_local() {
614-
Right((frame, local, offset, layout)) => {
619+
Right((local, offset, locals_addr, layout)) => {
615620
if offset.is_some() {
616621
// This has been projected to a part of this local. We could have complicated
617622
// logic to still keep this local as an `Operand`... but it's much easier to
618623
// just fall back to the indirect path.
619624
dest.force_mplace(self)?
620625
} else {
621-
M::before_access_local_mut(self, frame, local)?;
622-
match self.stack_mut()[frame].locals[local].access_mut()? {
626+
debug_assert_eq!(locals_addr, self.frame().locals_addr());
627+
M::before_access_local_mut(self, self.frame_idx(), local)?;
628+
match self.frame_mut().locals[local].access_mut()? {
623629
Operand::Immediate(local_val) => {
624630
// Local can be updated in-place.
625631
*local_val = src;
626632
// Double-check that the value we are storing and the local fit to each other.
627633
// (*After* doing the update for borrow checker reasons.)
628634
if cfg!(debug_assertions) {
629635
let local_layout =
630-
self.layout_of_local(&self.stack()[frame], local, None)?;
636+
self.layout_of_local(&self.frame(), local, None)?;
631637
match (src, local_layout.abi) {
632638
(Immediate::Scalar(scalar), Abi::Scalar(s)) => {
633639
assert_eq!(scalar.size(), s.size(self))
@@ -725,16 +731,17 @@ where
725731
) -> InterpResult<'tcx> {
726732
let mplace = match dest.as_mplace_or_local() {
727733
Left(mplace) => mplace,
728-
Right((frame, local, offset, layout)) => {
734+
Right((local, offset, locals_addr, layout)) => {
729735
if offset.is_some() {
730736
// This has been projected to a part of this local. We could have complicated
731737
// logic to still keep this local as an `Operand`... but it's much easier to
732738
// just fall back to the indirect path.
733739
// FIXME: share the logic with `write_immediate_no_validate`.
734740
dest.force_mplace(self)?
735741
} else {
736-
M::before_access_local_mut(self, frame, local)?;
737-
match self.stack_mut()[frame].locals[local].access_mut()? {
742+
debug_assert_eq!(locals_addr, self.frame().locals_addr());
743+
M::before_access_local_mut(self, self.frame_idx(), local)?;
744+
match self.frame_mut().locals[local].access_mut()? {
738745
Operand::Immediate(local) => {
739746
*local = Immediate::Uninit;
740747
return Ok(());
@@ -912,17 +919,17 @@ where
912919
place: &PlaceTy<'tcx, M::Provenance>,
913920
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
914921
let mplace = match place.place {
915-
Place::Local { frame, local, offset } => {
916-
M::before_access_local_mut(self, frame, local)?;
917-
let whole_local = match self.stack_mut()[frame].locals[local].access_mut()? {
922+
Place::Local { local, offset, locals_addr } => {
923+
debug_assert_eq!(locals_addr, self.frame().locals_addr());
924+
M::before_access_local_mut(self, self.frame_idx(), local)?;
925+
let whole_local = match self.frame_mut().locals[local].access_mut()? {
918926
&mut Operand::Immediate(local_val) => {
919927
// We need to make an allocation.
920928

921929
// We need the layout of the local. We can NOT use the layout we got,
922930
// that might e.g., be an inner field of a struct with `Scalar` layout,
923931
// that has different alignment than the outer field.
924-
let local_layout =
925-
self.layout_of_local(&self.stack()[frame], local, None)?;
932+
let local_layout = self.layout_of_local(&self.frame(), local, None)?;
926933
assert!(local_layout.is_sized(), "unsized locals cannot be immediate");
927934
let mplace = self.allocate(local_layout, MemoryKind::Stack)?;
928935
// Preserve old value. (As an optimization, we can skip this if it was uninit.)
@@ -936,11 +943,11 @@ where
936943
mplace.mplace,
937944
)?;
938945
}
939-
M::after_local_allocated(self, frame, local, &mplace)?;
946+
M::after_local_allocated(self, self.frame_idx(), local, &mplace)?;
940947
// Now we can call `access_mut` again, asserting it goes well, and actually
941948
// overwrite things. This points to the entire allocation, not just the part
942949
// the place refers to, i.e. we do this before we apply `offset`.
943-
*self.stack_mut()[frame].locals[local].access_mut().unwrap() =
950+
*self.frame_mut().locals[local].access_mut().unwrap() =
944951
Operand::Indirect(mplace.mplace);
945952
mplace.mplace
946953
}

compiler/rustc_const_eval/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Rust MIR: a lowered representation of Rust.
1414
#![feature(generic_nonzero)]
1515
#![feature(let_chains)]
1616
#![feature(slice_ptr_get)]
17+
#![feature(strict_provenance)]
1718
#![feature(never_type)]
1819
#![feature(trait_alias)]
1920
#![feature(try_blocks)]

0 commit comments

Comments
 (0)