Skip to content

Commit 5bde40c

Browse files
authored
Merge pull request #557 from RalfJung/fix-mutability-gap
fix mutability gap: do not allow shared mutation when creating frozen reference
2 parents 21fd5fd + d11a676 commit 5bde40c

File tree

8 files changed

+84
-32
lines changed

8 files changed

+84
-32
lines changed

rust-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
nightly-2018-11-30
1+
nightly-2018-12-03

src/stacked_borrows.rs

+20-15
Original file line numberDiff line numberDiff line change
@@ -303,14 +303,28 @@ impl<'tcx> Stack {
303303
/// is met: We cannot push `Uniq` onto frozen stacks.
304304
/// `kind` indicates which kind of reference is being created.
305305
fn create(&mut self, bor: Borrow, kind: RefKind) {
306-
if self.frozen_since.is_some() {
307-
// A frozen location? Possible if we create a barrier, then push again.
308-
assert!(bor.is_shared(), "We should never try creating a unique borrow for a frozen stack");
309-
trace!("create: Not doing anything on frozen location");
306+
// When creating a frozen reference, freeze. This ensures F1.
307+
// We also do *not* push anything else to the stack, making sure that no nother kind
308+
// of access (like writing through raw pointers) is permitted.
309+
if kind == RefKind::Frozen {
310+
let bor_t = match bor {
311+
Borrow::Shr(Some(t)) => t,
312+
_ => bug!("Creating illegal borrow {:?} for frozen ref", bor),
313+
};
314+
// It is possible that we already are frozen (e.g. if we just pushed a barrier,
315+
// the redundancy check would not have kicked in).
316+
match self.frozen_since {
317+
Some(loc_t) => assert!(loc_t <= bor_t, "Trying to freeze location for longer than it was already frozen"),
318+
None => {
319+
trace!("create: Freezing");
320+
self.frozen_since = Some(bor_t);
321+
}
322+
}
310323
return;
311324
}
312-
// First, push. We do this even if we will later freeze, because we
313-
// will allow mutation of shared data at the expense of unfreezing.
325+
assert!(self.frozen_since.is_none(), "Trying to create non-frozen reference to frozen location");
326+
327+
// Push new item to the stack.
314328
let itm = match bor {
315329
Borrow::Uniq(t) => BorStackItem::Uniq(t),
316330
Borrow::Shr(_) => BorStackItem::Shr,
@@ -325,15 +339,6 @@ impl<'tcx> Stack {
325339
trace!("create: Pushing {:?}", itm);
326340
self.borrows.push(itm);
327341
}
328-
// Then, maybe freeze. This is part 2 of ensuring F1.
329-
if kind == RefKind::Frozen {
330-
let bor_t = match bor {
331-
Borrow::Shr(Some(t)) => t,
332-
_ => bug!("Creating illegal borrow {:?} for frozen ref", bor),
333-
};
334-
trace!("create: Freezing");
335-
self.frozen_since = Some(bor_t);
336-
}
337342
}
338343

339344
/// Add a barrier

tests/compile-fail-fullmir/stacked_borrows/illegal_write3.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ fn main() {
33
// Make sure raw ptr with raw tag cannot mutate frozen location without breaking the shared ref.
44
let r#ref = &target; // freeze
55
let ptr = r#ref as *const _ as *mut _; // raw ptr, with raw tag
6-
unsafe { *ptr = 42; }
7-
let _val = *r#ref; //~ ERROR is not frozen
6+
unsafe { *ptr = 42; } //~ ERROR does not exist on the stack
7+
let _val = *r#ref;
88
}

tests/compile-fail-fullmir/stacked_borrows/pass_invalid_shr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ fn foo(_: &i32) {}
33

44
fn main() {
55
let x = &mut 42;
6-
let xraw = &*x as *const _ as *mut _;
6+
let xraw = x as *mut _;
77
let xref = unsafe { &*xraw };
88
unsafe { *xraw = 42 }; // unfreeze
99
foo(xref); //~ ERROR is not frozen
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
fn foo(x: &mut i32) -> i32 {
2+
*x = 5;
3+
unknown_code(&*x);
4+
*x // must return 5
5+
}
6+
7+
fn main() {
8+
println!("{}", foo(&mut 0));
9+
}
10+
11+
// If we replace the `*const` by `&`, my current dev version of miri
12+
// *does* find the problem, but not for a good reason: It finds it because
13+
// of barriers, and we shouldn't rely on unknown code using barriers.
14+
fn unknown_code(x: *const i32) {
15+
unsafe { *(x as *mut i32) = 7; } //~ ERROR barrier
16+
}

tests/run-pass-fullmir/vecdeque.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// FIXME: Validation disabled until https://github.com/rust-lang/rust/pull/56161 lands
2+
// compile-flags: -Zmiri-disable-validation
3+
14
use std::collections::VecDeque;
25

36
fn main() {

tests/run-pass/refcell.rs

+23
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ fn aliasing_mut_and_shr() {
3939
*aliasing += 4;
4040
let _shr = &*rc;
4141
*aliasing += 4;
42+
// also turning this into a frozen ref now must work
43+
let aliasing = &*aliasing;
44+
let _val = *aliasing;
45+
let _escape_to_raw = rc as *const _; // this must NOT unfreeze
46+
let _val = *aliasing;
47+
let _shr = &*rc; // this must NOT unfreeze
48+
let _val = *aliasing;
4249
}
4350

4451
let rc = RefCell::new(23);
@@ -48,7 +55,23 @@ fn aliasing_mut_and_shr() {
4855
assert_eq!(*rc.borrow(), 23+12);
4956
}
5057

58+
fn aliasing_frz_and_shr() {
59+
fn inner(rc: &RefCell<i32>, aliasing: &i32) {
60+
let _val = *aliasing;
61+
let _escape_to_raw = rc as *const _; // this must NOT unfreeze
62+
let _val = *aliasing;
63+
let _shr = &*rc; // this must NOT unfreeze
64+
let _val = *aliasing;
65+
}
66+
67+
let rc = RefCell::new(23);
68+
let bshr = rc.borrow();
69+
inner(&rc, &*bshr);
70+
assert_eq!(*rc.borrow(), 23);
71+
}
72+
5173
fn main() {
5274
lots_of_funny_borrows();
5375
aliasing_mut_and_shr();
76+
aliasing_frz_and_shr();
5477
}

tests/run-pass/stacked-borrows.rs

+18-13
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ fn main() {
44
read_does_not_invalidate1();
55
read_does_not_invalidate2();
66
ref_raw_int_raw();
7-
mut_shr_raw();
87
mut_raw_then_mut_shr();
8+
mut_shr_then_mut_raw();
99
mut_raw_mut();
1010
partially_invalidate_mut();
11+
drop_after_sharing();
1112
}
1213

1314
// Deref a raw ptr to access a field of a large struct, where the field
@@ -53,18 +54,6 @@ fn ref_raw_int_raw() {
5354
assert_eq!(unsafe { *xraw }, 3);
5455
}
5556

56-
// Creating a raw from a `&mut` through an `&` works, even if we
57-
// write through that raw.
58-
fn mut_shr_raw() {
59-
let mut x = 2;
60-
{
61-
let xref = &mut x;
62-
let xraw = &*xref as *const i32 as *mut i32;
63-
unsafe { *xraw = 4; }
64-
}
65-
assert_eq!(x, 4);
66-
}
67-
6857
// Escape a mut to raw, then share the same mut and use the share, then the raw.
6958
// That should work.
7059
fn mut_raw_then_mut_shr() {
@@ -77,6 +66,16 @@ fn mut_raw_then_mut_shr() {
7766
assert_eq!(x, 4);
7867
}
7968

69+
// Create first a shared reference and then a raw pointer from a `&mut`
70+
// should permit mutation through that raw pointer.
71+
fn mut_shr_then_mut_raw() {
72+
let xref = &mut 2;
73+
let _xshr = &*xref;
74+
let xraw = xref as *mut _;
75+
unsafe { *xraw = 3; }
76+
assert_eq!(*xref, 3);
77+
}
78+
8079
// Ensure that if we derive from a mut a raw, and then from that a mut,
8180
// and then read through the original mut, that does not invalidate the raw.
8281
// This shows that the read-exception for `&mut` applies even if the `Shr` item
@@ -107,3 +106,9 @@ fn partially_invalidate_mut() {
107106
*shard += 1; // so we can still use `shard`.
108107
assert_eq!(*data, (1, 1));
109108
}
109+
110+
// Make sure that we can handle the situation where a loaction is frozen when being dropped.
111+
fn drop_after_sharing() {
112+
let x = String::from("hello!");
113+
let _len = x.len();
114+
}

0 commit comments

Comments
 (0)