Skip to content

Commit ddde70c

Browse files
committed
Auto merge of #2394 - saethlin:unique-range-ice, r=RalfJung
Fix bugs where unique_range became invalid And also expand the cache integrity checks to cover this case. I'm going to run this over all the ICEs I've gotten out of Miri recently, could be a bit. Fixes #2389
2 parents 9e37c48 + 4268918 commit ddde70c

File tree

3 files changed

+65
-10
lines changed

3 files changed

+65
-10
lines changed

src/stacked_borrows/stack.rs

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ impl<'tcx> Stack {
9292
}
9393
}
9494

95+
// Check that all Unique items fall within unique_range.
9596
for (idx, item) in self.borrows.iter().enumerate() {
9697
if item.perm() == Permission::Unique {
9798
assert!(
@@ -102,6 +103,19 @@ impl<'tcx> Stack {
102103
);
103104
}
104105
}
106+
107+
// Check that the unique_range is a valid index into the borrow stack.
108+
// This asserts that the unique_range's start <= end.
109+
let uniques = &self.borrows[self.unique_range.clone()];
110+
111+
// Check that the start of the unique_range is precise.
112+
if let Some(first_unique) = uniques.first() {
113+
assert_eq!(first_unique.perm(), Permission::Unique);
114+
}
115+
// We cannot assert that the unique range is exact on the upper end.
116+
// When we pop items within the unique range, setting the end of the range precisely
117+
// requires doing a linear search of the borrow stack, which is exactly the kind of
118+
// operation that all this caching exists to avoid.
105119
}
106120

107121
/// Find the item granting the given kind of access to the given tag, and return where
@@ -227,9 +241,14 @@ impl<'tcx> Stack {
227241
self.unique_range.end += 1;
228242
}
229243
if new.perm() == Permission::Unique {
230-
// Make sure the possibly-unique range contains the new borrow
231-
self.unique_range.start = self.unique_range.start.min(new_idx);
232-
self.unique_range.end = self.unique_range.end.max(new_idx + 1);
244+
// If this is the only Unique, set the range to contain just the new item.
245+
if self.unique_range.is_empty() {
246+
self.unique_range = new_idx..new_idx + 1;
247+
} else {
248+
// We already have other Unique items, expand the range to include the new item
249+
self.unique_range.start = self.unique_range.start.min(new_idx);
250+
self.unique_range.end = self.unique_range.end.max(new_idx + 1);
251+
}
233252
}
234253

235254
// The above insert changes the meaning of every index in the cache >= new_idx, so now
@@ -282,6 +301,10 @@ impl<'tcx> Stack {
282301
// cache when it has been cleared and not yet refilled.
283302
self.borrows.clear();
284303
self.unknown_bottom = Some(tag);
304+
#[cfg(feature = "stack-cache")]
305+
{
306+
self.unique_range = 0..0;
307+
}
285308
}
286309

287310
/// Find all `Unique` elements in this borrow stack above `granting_idx`, pass a copy of them
@@ -298,7 +321,7 @@ impl<'tcx> Stack {
298321

299322
if disable_start <= unique_range.end {
300323
let lower = unique_range.start.max(disable_start);
301-
let upper = (unique_range.end + 1).min(self.borrows.len());
324+
let upper = self.unique_range.end;
302325
for item in &mut self.borrows[lower..upper] {
303326
if item.perm() == Permission::Unique {
304327
log::trace!("access: disabling item {:?}", item);
@@ -315,14 +338,14 @@ impl<'tcx> Stack {
315338
}
316339

317340
#[cfg(feature = "stack-cache")]
318-
if disable_start < self.unique_range.start {
341+
if disable_start <= self.unique_range.start {
319342
// We disabled all Unique items
320343
self.unique_range.start = 0;
321344
self.unique_range.end = 0;
322345
} else {
323-
// Truncate the range to disable_start. This is + 2 because we are only removing
324-
// elements after disable_start, and this range does not include the end.
325-
self.unique_range.end = self.unique_range.end.min(disable_start + 1);
346+
// Truncate the range to only include items up to the index that we started disabling
347+
// at.
348+
self.unique_range.end = self.unique_range.end.min(disable_start);
326349
}
327350

328351
#[cfg(debug_assertions)]
@@ -369,12 +392,12 @@ impl<'tcx> Stack {
369392
self.cache.items[i] = base_tag;
370393
}
371394

372-
if start < self.unique_range.start.saturating_sub(1) {
395+
if start <= self.unique_range.start {
373396
// We removed all the Unique items
374397
self.unique_range = 0..0;
375398
} else {
376399
// Ensure the range doesn't extend past the new top of the stack
377-
self.unique_range.end = self.unique_range.end.min(start + 1);
400+
self.unique_range.end = self.unique_range.end.min(start);
378401
}
379402
} else {
380403
self.unique_range = 0..0;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use std::cell::Cell;
2+
3+
fn main() {
4+
unsafe {
5+
let root0 = Cell::new(42);
6+
let wildcard = &root0 as *const Cell<i32> as usize as *const Cell<i32>;
7+
// empty the stack to unknown (via SRW reborrow from wildcard)
8+
let _ref0 = &*wildcard;
9+
// Do a non-SRW reborrow from wildcard to start building up a stack again.
10+
// Now new refs start being inserted at idx 0, pushing the unique_range up.
11+
let _refn = &*&*&*&*&*(wildcard.cast::<i32>());
12+
// empty the stack again, but this time with unique_range.start sitting at some high index.
13+
let _ref0 = &*wildcard;
14+
// and do a read which tries to clear the uniques
15+
wildcard.cast::<i32>().read();
16+
}
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
warning: integer-to-pointer cast
2+
--> $DIR/issue-miri-2389.rs:LL:CC
3+
|
4+
LL | let wildcard = &root0 as *const Cell<i32> as usize as *const Cell<i32>;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
6+
|
7+
= help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`,
8+
= help: which means that Miri might miss pointer bugs in this program.
9+
= help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation.
10+
= help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead.
11+
= help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics.
12+
= help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning.
13+
= note: backtrace:
14+
= note: inside `main` at $DIR/issue-miri-2389.rs:LL:CC
15+

0 commit comments

Comments
 (0)