Skip to content

Commit 74635a5

Browse files
committed
re-do large parts of stacked borrows, now with proper support for partiall frozen data
1 parent d0b79cf commit 74635a5

27 files changed

+434
-335
lines changed

cargo-miri-test/run-test.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
def test_cargo_miri():
1111
print("==> Testing `cargo miri` <==")
1212
## Call `cargo miri`, capture all output
13-
# FIXME: Disabling validation, still investigating whether there is UB here
1413
p = subprocess.Popen(
15-
["cargo", "miri", "-q", "--", "-Zmiri-disable-validation"],
14+
["cargo", "miri", "-q"],
1615
stdout=subprocess.PIPE,
1716
stderr=subprocess.PIPE
1817
)

src/helpers.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ impl<Tag> ScalarExt for ScalarMaybeUndef<Tag> {
3232

3333
pub trait EvalContextExt<'tcx> {
3434
fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>>;
35+
36+
/// Visit the memory covered by `place` that is frozen -- i.e., NOT
37+
/// what is inside an `UnsafeCell`.
38+
fn visit_frozen(
39+
&self,
40+
place: MPlaceTy<'tcx, Borrow>,
41+
size: Size,
42+
action: impl FnMut(Pointer<Borrow>, Size) -> EvalResult<'tcx>,
43+
) -> EvalResult<'tcx>;
3544
}
3645

3746

@@ -69,4 +78,126 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super:
6978
EvalErrorKind::PathNotFound(path).into()
7079
})
7180
}
81+
82+
/// Visit the memory covered by `place` that is frozen -- i.e., NOT
83+
/// what is inside an `UnsafeCell`.
84+
fn visit_frozen(
85+
&self,
86+
place: MPlaceTy<'tcx, Borrow>,
87+
size: Size,
88+
mut frozen_action: impl FnMut(Pointer<Borrow>, Size) -> EvalResult<'tcx>,
89+
) -> EvalResult<'tcx> {
90+
trace!("visit_frozen(place={:?}, size={:?})", *place, size);
91+
debug_assert_eq!(size,
92+
self.size_and_align_of_mplace(place)?
93+
.map(|(size, _)| size)
94+
.unwrap_or_else(|| place.layout.size)
95+
);
96+
// Store how far we proceeded into the place so far. Everything to the left of
97+
// this offset has already been handled, in the sense that the frozen parts
98+
// have had `action` called on them.
99+
let mut end_ptr = place.ptr;
100+
// Called when we detected an `UnsafeCell` at the given offset and size.
101+
// Calls `action` and advances `end_ptr`.
102+
let mut unsafe_cell_action = |unsafe_cell_offset, unsafe_cell_size| {
103+
// We assume that we are given the fields in increasing offset order,
104+
// and nothing else changes.
105+
let end_offset = end_ptr.get_ptr_offset(self);
106+
assert!(unsafe_cell_offset >= end_offset);
107+
let frozen_size = unsafe_cell_offset - end_offset;
108+
// Everything between the end_ptr and this `UnsafeCell` is frozen.
109+
if frozen_size != Size::ZERO {
110+
frozen_action(end_ptr.to_ptr()?, frozen_size)?;
111+
}
112+
// Update end end_ptr.
113+
end_ptr = end_ptr.ptr_wrapping_offset(frozen_size+unsafe_cell_size, self);
114+
// Done
115+
Ok(())
116+
};
117+
// Run a visitor
118+
{
119+
let mut visitor = UnsafeCellVisitor {
120+
ecx: self,
121+
unsafe_cell_action: |place| {
122+
trace!("unsafe_cell_action on {:?}", place.ptr);
123+
// We need a size to go on.
124+
let (unsafe_cell_size, _) = self.size_and_align_of_mplace(place)?
125+
// for extern types, just cover what we can
126+
.unwrap_or_else(|| place.layout.size_and_align());
127+
// Now handle this `UnsafeCell`.
128+
unsafe_cell_action(place.ptr.get_ptr_offset(self), unsafe_cell_size)
129+
},
130+
};
131+
visitor.visit_value(place)?;
132+
}
133+
// The part between the end_ptr and the end of the place is also frozen.
134+
// So pretend there is a 0-sized `UnsafeCell` at the end.
135+
unsafe_cell_action(place.ptr.get_ptr_offset(self) + size, Size::ZERO)?;
136+
// Done!
137+
return Ok(());
138+
139+
/// Visiting the memory covered by a `MemPlace`, being aware of
140+
/// whether we are inside an `UnsafeCell` or not.
141+
struct UnsafeCellVisitor<'ecx, 'a, 'mir, 'tcx, F>
142+
where F: FnMut(MPlaceTy<'tcx, Borrow>) -> EvalResult<'tcx>
143+
{
144+
ecx: &'ecx MiriEvalContext<'a, 'mir, 'tcx>,
145+
unsafe_cell_action: F,
146+
}
147+
148+
impl<'ecx, 'a, 'mir, 'tcx, F> ValueVisitor<'a, 'mir, 'tcx, Evaluator<'tcx>>
149+
for UnsafeCellVisitor<'ecx, 'a, 'mir, 'tcx, F>
150+
where
151+
F: FnMut(MPlaceTy<'tcx, Borrow>) -> EvalResult<'tcx>
152+
{
153+
type V = MPlaceTy<'tcx, Borrow>;
154+
155+
const WANT_FIELDS_SORTED: bool = true; // sorted? yes please!
156+
157+
#[inline(always)]
158+
fn ecx(&self) -> &MiriEvalContext<'a, 'mir, 'tcx> {
159+
&self.ecx
160+
}
161+
162+
// Hook to detect `UnsafeCell`
163+
fn visit_value(&mut self, v: MPlaceTy<'tcx, Borrow>) -> EvalResult<'tcx>
164+
{
165+
trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
166+
let is_unsafe_cell = match v.layout.ty.sty {
167+
ty::Adt(adt, _) => Some(adt.did) == self.ecx.tcx.lang_items().unsafe_cell_type(),
168+
_ => false,
169+
};
170+
if is_unsafe_cell {
171+
// We do not have to recurse further, this is an `UnsafeCell`.
172+
(self.unsafe_cell_action)(v)
173+
} else if self.ecx.type_is_freeze(v.layout.ty) {
174+
// This is `Freeze`, there cannot be an `UnsafeCell`
175+
Ok(())
176+
} else {
177+
// Proceed further
178+
self.walk_value(v)
179+
}
180+
}
181+
182+
// We have to do *something* for unions
183+
fn visit_union(&mut self, v: MPlaceTy<'tcx, Borrow>) -> EvalResult<'tcx>
184+
{
185+
// With unions, we fall back to whatever the type says, to hopefully be consistent
186+
// with LLVM IR.
187+
// FIXME Are we consistent? And is this really the behavior we want?
188+
let frozen = self.ecx.type_is_freeze(v.layout.ty);
189+
if frozen {
190+
Ok(())
191+
} else {
192+
(self.unsafe_cell_action)(v)
193+
}
194+
}
195+
196+
// We should never get to a primitive, but always short-circuit somewhere above
197+
fn visit_primitive(&mut self, _val: ImmTy<'tcx, Borrow>) -> EvalResult<'tcx>
198+
{
199+
bug!("We should always short-circit before coming to a primitive")
200+
}
201+
}
202+
}
72203
}

src/lib.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::collections::HashMap;
1717
use std::borrow::Cow;
1818
use std::env;
1919

20-
use rustc::ty::{self, Ty, TyCtxt, query::TyCtxtAt};
20+
use rustc::ty::{self, TyCtxt, query::TyCtxtAt};
2121
use rustc::ty::layout::{TyLayout, LayoutOf, Size};
2222
use rustc::hir::{self, def_id::DefId};
2323
use rustc::mir;
@@ -48,7 +48,7 @@ use crate::mono_hash_map::MonoHashMap;
4848
use crate::stacked_borrows::{EvalContextExt as StackedBorEvalContextExt};
4949

5050
// Used by priroda
51-
pub use crate::stacked_borrows::{Borrow, Stack, Stacks, Mut as MutBorrow, BorStackItem};
51+
pub use crate::stacked_borrows::{Borrow, Stack, Stacks, BorStackItem};
5252

5353
/// Insert rustc arguments at the beginning of the argument list that miri wants to be
5454
/// set per default, for maximal validation power.
@@ -476,38 +476,38 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> {
476476
#[inline(always)]
477477
fn tag_reference(
478478
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
479-
place: MemPlace<Borrow>,
480-
ty: Ty<'tcx>,
481-
size: Size,
479+
place: MPlaceTy<'tcx, Borrow>,
482480
mutability: Option<hir::Mutability>,
483-
) -> EvalResult<'tcx, MemPlace<Borrow>> {
481+
) -> EvalResult<'tcx, Scalar<Borrow>> {
482+
let (size, _) = ecx.size_and_align_of_mplace(place)?
483+
// for extern types, just cover what we can
484+
.unwrap_or_else(|| place.layout.size_and_align());
484485
if !ecx.machine.validate || size == Size::ZERO {
485486
// No tracking
486-
Ok(place)
487+
Ok(place.ptr)
487488
} else {
488489
let ptr = place.ptr.to_ptr()?;
489-
let tag = ecx.tag_reference(ptr, ty, size, mutability.into())?;
490-
let ptr = Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag));
491-
Ok(MemPlace { ptr, ..place })
490+
let tag = ecx.tag_reference(place, size, mutability.into())?;
491+
Ok(Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag)))
492492
}
493493
}
494494

495495
#[inline(always)]
496496
fn tag_dereference(
497497
ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
498-
place: MemPlace<Borrow>,
499-
ty: Ty<'tcx>,
500-
size: Size,
498+
place: MPlaceTy<'tcx, Borrow>,
501499
mutability: Option<hir::Mutability>,
502-
) -> EvalResult<'tcx, MemPlace<Borrow>> {
500+
) -> EvalResult<'tcx, Scalar<Borrow>> {
501+
let (size, _) = ecx.size_and_align_of_mplace(place)?
502+
// for extern types, just cover what we can
503+
.unwrap_or_else(|| place.layout.size_and_align());
503504
if !ecx.machine.validate || size == Size::ZERO {
504505
// No tracking
505-
Ok(place)
506+
Ok(place.ptr)
506507
} else {
507508
let ptr = place.ptr.to_ptr()?;
508-
let tag = ecx.tag_dereference(ptr, ty, size, mutability.into())?;
509-
let ptr = Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag));
510-
Ok(MemPlace { ptr, ..place })
509+
let tag = ecx.tag_dereference(place, size, mutability.into())?;
510+
Ok(Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag)))
511511
}
512512
}
513513

0 commit comments

Comments
 (0)