Skip to content

Commit 97e010f

Browse files
committed
barriers prevent deallocation
1 parent 194710e commit 97e010f

File tree

2 files changed

+70
-25
lines changed

2 files changed

+70
-25
lines changed

src/stacked_borrows.rs

Lines changed: 57 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ pub enum RefKind {
9090
Raw,
9191
}
9292

93+
/// What kind of access is being performed?
94+
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
95+
pub enum AccessKind {
96+
Read,
97+
Write,
98+
Dealloc,
99+
}
100+
93101
/// Extra global state in the memory, available to the memory access hooks
94102
#[derive(Debug)]
95103
pub struct BarrierTracking {
@@ -221,13 +229,13 @@ impl<'tcx> Stack {
221229
fn access(
222230
&mut self,
223231
bor: Borrow,
224-
is_write: bool,
232+
kind: AccessKind,
225233
barrier_tracking: &BarrierTracking,
226234
) -> EvalResult<'tcx> {
227235
// Check if we can match the frozen "item".
228236
// Not possible on writes!
229237
if self.is_frozen() {
230-
if !is_write {
238+
if kind == AccessKind::Read {
231239
// When we are frozen, we just accept all reads. No harm in this.
232240
// The deref already checked that `Uniq` items are in the stack, and that
233241
// the location is frozen if it should be.
@@ -247,26 +255,41 @@ impl<'tcx> Stack {
247255
)))
248256
}
249257
(BorStackItem::Uniq(itm_t), Borrow::Uniq(bor_t)) if itm_t == bor_t => {
250-
// Found matching unique item.
251-
return Ok(())
258+
// Found matching unique item. Continue after the match.
252259
}
253-
(BorStackItem::Shr, _) if !is_write => {
260+
(BorStackItem::Shr, _) if kind == AccessKind::Read => {
254261
// When reading, everything can use a shared item!
255262
// We do not want to do this when writing: Writing to an `&mut`
256263
// should reaffirm its exclusivity (i.e., make sure it is
257-
// on top of the stack).
258-
return Ok(())
264+
// on top of the stack). Continue after the match.
259265
}
260266
(BorStackItem::Shr, Borrow::Shr(_)) => {
261-
// Found matching shared item.
262-
return Ok(())
267+
// Found matching shared item. Continue after the match.
263268
}
264269
_ => {
265-
// Pop this. This ensures U2.
270+
// Pop this, go on. This ensures U2.
266271
let itm = self.borrows.pop().unwrap();
267272
trace!("access: Popping {:?}", itm);
273+
continue
274+
}
275+
}
276+
// If we got here, we found a matching item. Congratulations!
277+
// However, we are not done yet: If this access is deallocating, we must make sure
278+
// there are no active barriers remaining on the stack.
279+
if kind == AccessKind::Dealloc {
280+
for &itm in self.borrows.iter().rev() {
281+
match itm {
282+
BorStackItem::FnBarrier(call) if barrier_tracking.is_active(call) => {
283+
return err!(MachineError(format!(
284+
"Deallocating with active barrier ({})", call
285+
)))
286+
}
287+
_ => {},
288+
}
268289
}
269290
}
291+
// NOW we are done.
292+
return Ok(())
270293
}
271294
// If we got here, we did not find our item.
272295
err!(MachineError(format!(
@@ -352,18 +375,16 @@ impl<'tcx> Stacks {
352375
&self,
353376
ptr: Pointer<Borrow>,
354377
size: Size,
355-
is_write: bool,
356-
barrier_tracking: &BarrierTracking,
378+
kind: AccessKind,
357379
) -> EvalResult<'tcx> {
358-
trace!("{} access of tag {:?}: {:?}, size {}",
359-
if is_write { "read" } else { "write" },
360-
ptr.tag, ptr, size.bytes());
380+
trace!("{:?} access of tag {:?}: {:?}, size {}", kind, ptr.tag, ptr, size.bytes());
361381
// Even reads can have a side-effect, by invalidating other references.
362382
// This is fundamentally necessary since `&mut` asserts that there
363383
// are no accesses through other references, not even reads.
384+
let barrier_tracking = self.barrier_tracking.borrow();
364385
let mut stacks = self.stacks.borrow_mut();
365386
for stack in stacks.iter_mut(ptr.offset, size) {
366-
stack.access(ptr.tag, is_write, barrier_tracking)?;
387+
stack.access(ptr.tag, kind, &*barrier_tracking)?;
367388
}
368389
Ok(())
369390
}
@@ -377,16 +398,24 @@ impl<'tcx> Stacks {
377398
mut barrier: Option<CallId>,
378399
new_bor: Borrow,
379400
new_kind: RefKind,
380-
barrier_tracking: &BarrierTracking,
381401
) -> EvalResult<'tcx> {
382402
assert_eq!(new_bor.is_unique(), new_kind == RefKind::Unique);
383403
trace!("reborrow for tag {:?} to {:?} as {:?}: {:?}, size {}",
384404
ptr.tag, new_bor, new_kind, ptr, size.bytes());
385405
if new_kind == RefKind::Raw {
386406
// No barrier for raw, including `&UnsafeCell`. They can rightfully
387407
// alias with `&mut`.
408+
// FIXME: This means that the `dereferencable` attribute on non-frozen shared
409+
// references is incorrect! They are dereferencable when the function is
410+
// called, but might become non-dereferencable during the coruse of execution.
411+
// Also see [1], [2].
412+
//
413+
// [1]: <https://internals.rust-lang.org/t/
414+
// is-it-possible-to-be-memory-safe-with-deallocated-self/8457/8>,
415+
// [2]: <https://lists.llvm.org/pipermail/llvm-dev/2018-July/124555.html>
388416
barrier = None;
389417
}
418+
let barrier_tracking = self.barrier_tracking.borrow();
390419
let mut stacks = self.stacks.borrow_mut();
391420
for stack in stacks.iter_mut(ptr.offset, size) {
392421
// Access source `ptr`, create new ref.
@@ -410,7 +439,12 @@ impl<'tcx> Stacks {
410439
continue;
411440
}
412441
// We need to do some actual work.
413-
stack.access(ptr.tag, new_kind == RefKind::Unique, barrier_tracking)?;
442+
let access_kind = if new_kind == RefKind::Unique {
443+
AccessKind::Write
444+
} else {
445+
AccessKind::Read
446+
};
447+
stack.access(ptr.tag, access_kind, &*barrier_tracking)?;
414448
if let Some(call) = barrier {
415449
stack.barrier(call);
416450
}
@@ -440,7 +474,7 @@ impl AllocationExtra<Borrow, MemoryState> for Stacks {
440474
ptr: Pointer<Borrow>,
441475
size: Size,
442476
) -> EvalResult<'tcx> {
443-
alloc.extra.access(ptr, size, /*is_write*/false, &*alloc.extra.barrier_tracking.borrow())
477+
alloc.extra.access(ptr, size, AccessKind::Read)
444478
}
445479

446480
#[inline(always)]
@@ -449,7 +483,7 @@ impl AllocationExtra<Borrow, MemoryState> for Stacks {
449483
ptr: Pointer<Borrow>,
450484
size: Size,
451485
) -> EvalResult<'tcx> {
452-
alloc.extra.access(ptr, size, /*is_write*/true, &*alloc.extra.barrier_tracking.borrow())
486+
alloc.extra.access(ptr, size, AccessKind::Write)
453487
}
454488

455489
#[inline(always)]
@@ -458,9 +492,7 @@ impl AllocationExtra<Borrow, MemoryState> for Stacks {
458492
ptr: Pointer<Borrow>,
459493
size: Size,
460494
) -> EvalResult<'tcx> {
461-
// This is like mutating
462-
alloc.extra.access(ptr, size, /*is_write*/true, &*alloc.extra.barrier_tracking.borrow())
463-
// FIXME: Error out of there are any barriers?
495+
alloc.extra.access(ptr, size, AccessKind::Dealloc)
464496
}
465497
}
466498

@@ -627,12 +659,12 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
627659
// Reference that cares about freezing. We need a frozen-sensitive reborrow.
628660
self.visit_freeze_sensitive(place, size, |cur_ptr, size, frozen| {
629661
let kind = if frozen { RefKind::Frozen } else { RefKind::Raw };
630-
alloc.extra.reborrow(cur_ptr, size, barrier, new_bor, kind, &*self.memory().extra.borrow())
662+
alloc.extra.reborrow(cur_ptr, size, barrier, new_bor, kind)
631663
})?;
632664
} else {
633665
// Just treat this as one big chunk.
634666
let kind = if new_bor.is_unique() { RefKind::Unique } else { RefKind::Raw };
635-
alloc.extra.reborrow(ptr, size, barrier, new_bor, kind, &*self.memory().extra.borrow())?;
667+
alloc.extra.reborrow(ptr, size, barrier, new_bor, kind)?;
636668
}
637669
Ok(new_ptr)
638670
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// error-pattern: Deallocating with active barrier
2+
3+
fn inner(x: &mut i32, f: fn(&mut i32)) {
4+
// `f` may mutate, but it may not deallocate!
5+
f(x)
6+
}
7+
8+
fn main() {
9+
inner(Box::leak(Box::new(0)), |x| {
10+
let raw = x as *mut _;
11+
drop(unsafe { Box::from_raw(raw) });
12+
});
13+
}

0 commit comments

Comments
 (0)