Skip to content

Commit 4e88b73

Browse files
committed
Auto merge of #55270 - RalfJung:stacked-borrows-ng, r=oli-obk
miri engine: Stacked Borrows NG For more refined tracking in miri, we do return untagged pointers from the memory abstraction after allocations and let the caller decide how to tag these. Also refactor the `tag_(de)reference` hooks so they can be more easily called in the ref-to-place and place-to-ref methods, and reorder things in validation: validation calls ref-to-place which (when running in miri) triggers some checks, so we want to run it rather late and catch other problems first. We also do not need to redundantly check the ref to be allocated any more, the checks miri does anyway imply thath. r? @oli-obk
2 parents bcb05a0 + 95b19bb commit 4e88b73

File tree

9 files changed

+174
-130
lines changed

9 files changed

+174
-130
lines changed

src/librustc_mir/const_eval.rs

+9-20
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use rustc::hir::{self, def_id::DefId};
2020
use rustc::hir::def::Def;
2121
use rustc::mir::interpret::{ConstEvalErr, ErrorHandled};
2222
use rustc::mir;
23-
use rustc::ty::{self, Ty, TyCtxt, Instance, query::TyCtxtAt};
24-
use rustc::ty::layout::{self, Size, LayoutOf, TyLayout};
23+
use rustc::ty::{self, TyCtxt, Instance, query::TyCtxtAt};
24+
use rustc::ty::layout::{self, LayoutOf, TyLayout};
2525
use rustc::ty::subst::Subst;
2626
use rustc::traits::Reveal;
2727
use rustc_data_structures::indexed_vec::IndexVec;
@@ -32,7 +32,7 @@ use syntax::ast::Mutability;
3232
use syntax::source_map::{Span, DUMMY_SP};
3333

3434
use interpret::{self,
35-
PlaceTy, MemPlace, OpTy, Operand, Value, Pointer, Scalar, ConstValue,
35+
PlaceTy, MemPlace, OpTy, Operand, Value, Scalar, ConstValue, Pointer,
3636
EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup,
3737
Allocation, AllocId, MemoryKind,
3838
snapshot, RefTracking,
@@ -426,7 +426,7 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
426426
}
427427

428428
#[inline(always)]
429-
fn static_with_default_tag(
429+
fn adjust_static_allocation(
430430
alloc: &'_ Allocation
431431
) -> Cow<'_, Allocation<Self::PointerTag>> {
432432
// We do not use a tag so we can just cheaply forward the reference
@@ -467,23 +467,12 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
467467
}
468468

469469
#[inline(always)]
470-
fn tag_reference(
470+
fn tag_new_allocation(
471471
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
472-
_ptr: Pointer<Self::PointerTag>,
473-
_pointee_ty: Ty<'tcx>,
474-
_pointee_size: Size,
475-
_borrow_kind: Option<mir::BorrowKind>,
476-
) -> EvalResult<'tcx, Self::PointerTag> {
477-
Ok(())
478-
}
479-
480-
#[inline(always)]
481-
fn tag_dereference(
482-
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
483-
_ptr: Pointer<Self::PointerTag>,
484-
_ptr_ty: Ty<'tcx>,
485-
) -> EvalResult<'tcx, Self::PointerTag> {
486-
Ok(())
472+
ptr: Pointer,
473+
_kind: MemoryKind<Self::MemoryKinds>,
474+
) -> EvalResult<'tcx, Pointer> {
475+
Ok(ptr)
487476
}
488477
}
489478

src/librustc_mir/interpret/cast.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
110110
def_id,
111111
substs,
112112
).ok_or_else(|| EvalErrorKind::TooGeneric.into());
113-
let fn_ptr = self.memory.create_fn_alloc(instance?);
113+
let fn_ptr = self.memory.create_fn_alloc(instance?).with_default_tag();
114114
self.write_scalar(Scalar::Ptr(fn_ptr.into()), dest)?;
115115
}
116116
ref other => bug!("reify fn pointer on {:?}", other),
@@ -143,7 +143,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
143143
substs,
144144
ty::ClosureKind::FnOnce,
145145
);
146-
let fn_ptr = self.memory.create_fn_alloc(instance);
146+
let fn_ptr = self.memory.create_fn_alloc(instance).with_default_tag();
147147
let val = Value::Scalar(Scalar::Ptr(fn_ptr.into()).into());
148148
self.write_value(val, dest)?;
149149
}

src/librustc_mir/interpret/eval_context.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> {
4747
pub(crate) param_env: ty::ParamEnv<'tcx>,
4848

4949
/// The virtual memory system.
50-
pub memory: Memory<'a, 'mir, 'tcx, M>,
50+
pub(crate) memory: Memory<'a, 'mir, 'tcx, M>,
5151

5252
/// The virtual call stack.
5353
pub(crate) stack: Vec<Frame<'mir, 'tcx, M::PointerTag>>,
@@ -334,7 +334,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
334334
}
335335

336336
pub fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value<M::PointerTag>> {
337-
let ptr = self.memory.allocate_static_bytes(s.as_bytes());
337+
let ptr = self.memory.allocate_static_bytes(s.as_bytes()).with_default_tag();
338338
Ok(Value::new_slice(Scalar::Ptr(ptr), s.len() as u64, self.tcx.tcx))
339339
}
340340

src/librustc_mir/interpret/machine.rs

+35-18
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
use std::borrow::{Borrow, Cow};
1616
use std::hash::Hash;
1717

18-
use rustc::hir::def_id::DefId;
18+
use rustc::hir::{self, def_id::DefId};
1919
use rustc::mir;
2020
use rustc::ty::{self, Ty, layout::{Size, TyLayout}, query::TyCtxtAt};
2121

2222
use super::{
2323
Allocation, AllocId, EvalResult, Scalar,
24-
EvalContext, PlaceTy, OpTy, Pointer, MemoryKind,
24+
EvalContext, PlaceTy, OpTy, Pointer, MemPlace, MemoryKind,
2525
};
2626

2727
/// Classifying memory accesses
@@ -81,6 +81,7 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
8181

8282
/// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows"
8383
/// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
84+
/// The `default()` is used for pointers to consts, statics, vtables and functions.
8485
type PointerTag: ::std::fmt::Debug + Default + Copy + Eq + Hash + 'static;
8586

8687
/// Extra data stored in every allocation.
@@ -151,13 +152,13 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
151152
) -> EvalResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag, Self::AllocExtra>>>;
152153

153154
/// Called to turn an allocation obtained from the `tcx` into one that has
154-
/// the appropriate tags on each pointer.
155+
/// the right type for this machine.
155156
///
156157
/// This should avoid copying if no work has to be done! If this returns an owned
157-
/// allocation (because a copy had to be done to add the tags), machine memory will
158+
/// allocation (because a copy had to be done to add tags or metadata), machine memory will
158159
/// cache the result. (This relies on `AllocMap::get_or` being able to add the
159160
/// owned allocation to the map even when the map is shared.)
160-
fn static_with_default_tag(
161+
fn adjust_static_allocation(
161162
alloc: &'_ Allocation
162163
) -> Cow<'_, Allocation<Self::PointerTag, Self::AllocExtra>>;
163164

@@ -204,24 +205,40 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
204205
Ok(())
205206
}
206207

208+
/// Add the tag for a newly allocated pointer.
209+
fn tag_new_allocation(
210+
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
211+
ptr: Pointer,
212+
kind: MemoryKind<Self::MemoryKinds>,
213+
) -> EvalResult<'tcx, Pointer<Self::PointerTag>>;
214+
207215
/// Executed when evaluating the `&` operator: Creating a new reference.
208-
/// This has the chance to adjust the tag.
209-
/// `borrow_kind` can be `None` in case a raw ptr is being created.
216+
/// This has the chance to adjust the tag. It should not change anything else!
217+
/// `mutability` can be `None` in case a raw ptr is being created.
218+
#[inline]
210219
fn tag_reference(
211-
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
212-
ptr: Pointer<Self::PointerTag>,
213-
pointee_ty: Ty<'tcx>,
214-
pointee_size: Size,
215-
borrow_kind: Option<mir::BorrowKind>,
216-
) -> EvalResult<'tcx, Self::PointerTag>;
220+
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
221+
place: MemPlace<Self::PointerTag>,
222+
_ty: Ty<'tcx>,
223+
_size: Size,
224+
_mutability: Option<hir::Mutability>,
225+
) -> EvalResult<'tcx, MemPlace<Self::PointerTag>> {
226+
Ok(place)
227+
}
217228

218229
/// Executed when evaluating the `*` operator: Following a reference.
219-
/// This has the change to adjust the tag.
230+
/// This has the change to adjust the tag. It should not change anything else!
231+
/// `mutability` can be `None` in case a raw ptr is being dereferenced.
232+
#[inline]
220233
fn tag_dereference(
221-
ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
222-
ptr: Pointer<Self::PointerTag>,
223-
ptr_ty: Ty<'tcx>,
224-
) -> EvalResult<'tcx, Self::PointerTag>;
234+
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
235+
place: MemPlace<Self::PointerTag>,
236+
_ty: Ty<'tcx>,
237+
_size: Size,
238+
_mutability: Option<hir::Mutability>,
239+
) -> EvalResult<'tcx, MemPlace<Self::PointerTag>> {
240+
Ok(place)
241+
}
225242

226243
/// Execute a validation operation
227244
#[inline]

src/librustc_mir/interpret/memory.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
117117
}
118118
}
119119

120-
pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> Pointer<M::PointerTag> {
121-
Pointer::from(self.tcx.alloc_map.lock().create_fn_alloc(instance)).with_default_tag()
120+
pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> Pointer {
121+
Pointer::from(self.tcx.alloc_map.lock().create_fn_alloc(instance))
122122
}
123123

124-
pub fn allocate_static_bytes(&mut self, bytes: &[u8]) -> Pointer<M::PointerTag> {
125-
Pointer::from(self.tcx.allocate_bytes(bytes)).with_default_tag()
124+
pub fn allocate_static_bytes(&mut self, bytes: &[u8]) -> Pointer {
125+
Pointer::from(self.tcx.allocate_bytes(bytes))
126126
}
127127

128128
pub fn allocate_with(
@@ -140,9 +140,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
140140
size: Size,
141141
align: Align,
142142
kind: MemoryKind<M::MemoryKinds>,
143-
) -> EvalResult<'tcx, Pointer<M::PointerTag>> {
144-
let ptr = Pointer::from(self.allocate_with(Allocation::undef(size, align), kind)?);
145-
Ok(ptr.with_default_tag())
143+
) -> EvalResult<'tcx, Pointer> {
144+
Ok(Pointer::from(self.allocate_with(Allocation::undef(size, align), kind)?))
146145
}
147146

148147
pub fn reallocate(
@@ -153,17 +152,18 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
153152
new_size: Size,
154153
new_align: Align,
155154
kind: MemoryKind<M::MemoryKinds>,
156-
) -> EvalResult<'tcx, Pointer<M::PointerTag>> {
155+
) -> EvalResult<'tcx, Pointer> {
157156
if ptr.offset.bytes() != 0 {
158157
return err!(ReallocateNonBasePtr);
159158
}
160159

161-
// For simplicities' sake, we implement reallocate as "alloc, copy, dealloc"
160+
// For simplicities' sake, we implement reallocate as "alloc, copy, dealloc".
161+
// This happens so rarely, the perf advantage is outweighed by the maintenance cost.
162162
let new_ptr = self.allocate(new_size, new_align, kind)?;
163163
self.copy(
164164
ptr.into(),
165165
old_align,
166-
new_ptr.into(),
166+
new_ptr.with_default_tag().into(),
167167
new_align,
168168
old_size.min(new_size),
169169
/*nonoverlapping*/ true,
@@ -347,7 +347,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
347347
Some(AllocType::Memory(mem)) => {
348348
// We got tcx memory. Let the machine figure out whether and how to
349349
// turn that into memory with the right pointer tag.
350-
return Ok(M::static_with_default_tag(mem))
350+
return Ok(M::adjust_static_allocation(mem))
351351
}
352352
Some(AllocType::Function(..)) => {
353353
return err!(DerefFunctionPointer)
@@ -381,7 +381,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
381381
if let ConstValue::ByRef(_, allocation, _) = const_val.val {
382382
// We got tcx memory. Let the machine figure out whether and how to
383383
// turn that into memory with the right pointer tag.
384-
M::static_with_default_tag(allocation)
384+
M::adjust_static_allocation(allocation)
385385
} else {
386386
bug!("Matching on non-ByRef static")
387387
}

src/librustc_mir/interpret/operand.rs

+10
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,16 @@ impl<'tcx, Tag> Value<Tag> {
217217
Value::ScalarPair(ptr, _) => ptr.not_undef(),
218218
}
219219
}
220+
221+
/// Convert the value into its metadata.
222+
/// Throws away the first half of a ScalarPair!
223+
#[inline]
224+
pub fn to_meta(self) -> EvalResult<'tcx, Option<Scalar<Tag>>> {
225+
Ok(match self {
226+
Value::Scalar(_) => None,
227+
Value::ScalarPair(_, meta) => Some(meta.not_undef()?),
228+
})
229+
}
220230
}
221231

222232
// ScalarPair needs a type to interpret, so we often have a value and a type together

src/librustc_mir/interpret/place.rs

+41-27
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use std::convert::TryFrom;
1616
use std::hash::Hash;
1717

18+
use rustc::hir;
1819
use rustc::mir;
1920
use rustc::ty::{self, Ty};
2021
use rustc::ty::layout::{self, Size, Align, LayoutOf, TyLayout, HasDataLayout};
@@ -270,24 +271,28 @@ where
270271
&self,
271272
val: ValTy<'tcx, M::PointerTag>,
272273
) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
273-
let ptr = match val.to_scalar_ptr()? {
274-
Scalar::Ptr(ptr) if M::ENABLE_PTR_TRACKING_HOOKS => {
275-
// Machine might want to track the `*` operator
276-
let tag = M::tag_dereference(self, ptr, val.layout.ty)?;
277-
Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag))
278-
}
279-
other => other,
280-
};
281-
282274
let pointee_type = val.layout.ty.builtin_deref(true).unwrap().ty;
283275
let layout = self.layout_of(pointee_type)?;
284-
let align = layout.align;
285276

286-
let mplace = match *val {
287-
Value::Scalar(_) =>
288-
MemPlace { ptr, align, meta: None },
289-
Value::ScalarPair(_, meta) =>
290-
MemPlace { ptr, align, meta: Some(meta.not_undef()?) },
277+
let align = layout.align;
278+
let meta = val.to_meta()?;
279+
let ptr = val.to_scalar_ptr()?;
280+
let mplace = MemPlace { ptr, align, meta };
281+
// Pointer tag tracking might want to adjust the tag.
282+
let mplace = if M::ENABLE_PTR_TRACKING_HOOKS {
283+
let (size, _) = self.size_and_align_of(meta, layout)?
284+
// for extern types, just cover what we can
285+
.unwrap_or_else(|| layout.size_and_align());
286+
let mutbl = match val.layout.ty.sty {
287+
// `builtin_deref` considers boxes immutable, that's useless for our purposes
288+
ty::Ref(_, _, mutbl) => Some(mutbl),
289+
ty::Adt(def, _) if def.is_box() => Some(hir::MutMutable),
290+
ty::RawPtr(_) => None,
291+
_ => bug!("Unexpected pointer type {}", val.layout.ty.sty),
292+
};
293+
M::tag_dereference(self, mplace, pointee_type, size, mutbl)?
294+
} else {
295+
mplace
291296
};
292297
Ok(MPlaceTy { mplace, layout })
293298
}
@@ -299,19 +304,25 @@ where
299304
place: MPlaceTy<'tcx, M::PointerTag>,
300305
borrow_kind: Option<mir::BorrowKind>,
301306
) -> EvalResult<'tcx, Value<M::PointerTag>> {
302-
let ptr = match place.ptr {
303-
Scalar::Ptr(ptr) if M::ENABLE_PTR_TRACKING_HOOKS => {
304-
// Machine might want to track the `&` operator
305-
let (size, _) = self.size_and_align_of_mplace(place)?
306-
.expect("create_ref cannot determine size");
307-
let tag = M::tag_reference(self, ptr, place.layout.ty, size, borrow_kind)?;
308-
Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag))
309-
},
310-
other => other,
307+
// Pointer tag tracking might want to adjust the tag
308+
let place = if M::ENABLE_PTR_TRACKING_HOOKS {
309+
let (size, _) = self.size_and_align_of_mplace(place)?
310+
// for extern types, just cover what we can
311+
.unwrap_or_else(|| place.layout.size_and_align());
312+
let mutbl = match borrow_kind {
313+
Some(mir::BorrowKind::Mut { .. }) |
314+
Some(mir::BorrowKind::Unique) =>
315+
Some(hir::MutMutable),
316+
Some(_) => Some(hir::MutImmutable),
317+
None => None,
318+
};
319+
M::tag_reference(self, *place, place.layout.ty, size, mutbl)?
320+
} else {
321+
*place
311322
};
312323
Ok(match place.meta {
313-
None => Value::Scalar(ptr.into()),
314-
Some(meta) => Value::ScalarPair(ptr.into(), meta.into()),
324+
None => Value::Scalar(place.ptr.into()),
325+
Some(meta) => Value::ScalarPair(place.ptr.into(), meta.into()),
315326
})
316327
}
317328

@@ -845,6 +856,8 @@ where
845856
}
846857

847858
/// Make sure that a place is in memory, and return where it is.
859+
/// If the place currently refers to a local that doesn't yet have a matching allocation,
860+
/// create such an allocation.
848861
/// This is essentially `force_to_memplace`.
849862
pub fn force_allocation(
850863
&mut self,
@@ -888,10 +901,11 @@ where
888901
) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
889902
if layout.is_unsized() {
890903
assert!(self.tcx.features().unsized_locals, "cannot alloc memory for unsized type");
891-
// FIXME: What should we do here?
904+
// FIXME: What should we do here? We should definitely also tag!
892905
Ok(MPlaceTy::dangling(layout, &self))
893906
} else {
894907
let ptr = self.memory.allocate(layout.size, layout.align, kind)?;
908+
let ptr = M::tag_new_allocation(self, ptr, kind)?;
895909
Ok(MPlaceTy::from_aligned_ptr(ptr, layout))
896910
}
897911
}

0 commit comments

Comments
 (0)