From 39f7b35327cd4747da1a20a187fbaf220ee4a09c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 28 Oct 2020 12:04:39 +0100 Subject: [PATCH 1/5] Stacked Borrows: print affected memory location on errors --- src/range_map.rs | 30 ++++++++++++---------- src/stacked_borrows.rs | 56 ++++++++++++++++++++---------------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/range_map.rs b/src/range_map.rs index 16ad5fd7c2..607c830530 100644 --- a/src/range_map.rs +++ b/src/range_map.rs @@ -61,7 +61,9 @@ impl RangeMap { /// Provides read-only iteration over everything in the given range. This does /// *not* split items if they overlap with the edges. Do not use this to mutate /// through interior mutability. - pub fn iter<'a>(&'a self, offset: Size, len: Size) -> impl Iterator + 'a { + /// + /// The iterator also provides the offset of the given element. + pub fn iter<'a>(&'a self, offset: Size, len: Size) -> impl Iterator + 'a { let offset = offset.bytes(); let len = len.bytes(); // Compute a slice starting with the elements we care about. @@ -75,7 +77,7 @@ impl RangeMap { }; // The first offset that is not included any more. let end = offset + len; - slice.iter().take_while(move |elem| elem.range.start < end).map(|elem| &elem.data) + slice.iter().take_while(move |elem| elem.range.start < end).map(|elem| (Size::from_bytes(elem.range.start), &elem.data)) } pub fn iter_mut_all<'a>(&'a mut self) -> impl Iterator + 'a { @@ -112,11 +114,13 @@ impl RangeMap { /// this will split entries in the map that are only partially hit by the given range, /// to make sure that when they are mutated, the effect is constrained to the given range. /// Moreover, this will opportunistically merge neighbouring equal blocks. + /// + /// The iterator also provides the offset of the given element. pub fn iter_mut<'a>( &'a mut self, offset: Size, len: Size, - ) -> impl Iterator + 'a + ) -> impl Iterator + 'a where T: Clone + PartialEq, { @@ -197,7 +201,7 @@ impl RangeMap { // Now we yield the slice. `end` is inclusive. &mut self.v[first_idx..=end_idx] }; - slice.iter_mut().map(|elem| &mut elem.data) + slice.iter_mut().map(|elem| (Size::from_bytes(elem.range.start), &mut elem.data)) } } @@ -209,7 +213,7 @@ mod tests { fn to_vec(map: &RangeMap, offset: u64, len: u64) -> Vec { (offset..offset + len) .into_iter() - .map(|i| map.iter(Size::from_bytes(i), Size::from_bytes(1)).next().map(|&t| t).unwrap()) + .map(|i| map.iter(Size::from_bytes(i), Size::from_bytes(1)).next().map(|(_, &t)| t).unwrap()) .collect() } @@ -217,7 +221,7 @@ mod tests { fn basic_insert() { let mut map = RangeMap::::new(Size::from_bytes(20), -1); // Insert. - for x in map.iter_mut(Size::from_bytes(10), Size::from_bytes(1)) { + for (_, x) in map.iter_mut(Size::from_bytes(10), Size::from_bytes(1)) { *x = 42; } // Check. @@ -225,10 +229,10 @@ mod tests { assert_eq!(map.v.len(), 3); // Insert with size 0. - for x in map.iter_mut(Size::from_bytes(10), Size::from_bytes(0)) { + for (_, x) in map.iter_mut(Size::from_bytes(10), Size::from_bytes(0)) { *x = 19; } - for x in map.iter_mut(Size::from_bytes(11), Size::from_bytes(0)) { + for (_, x) in map.iter_mut(Size::from_bytes(11), Size::from_bytes(0)) { *x = 19; } assert_eq!(to_vec(&map, 10, 2), vec![42, -1]); @@ -238,16 +242,16 @@ mod tests { #[test] fn gaps() { let mut map = RangeMap::::new(Size::from_bytes(20), -1); - for x in map.iter_mut(Size::from_bytes(11), Size::from_bytes(1)) { + for (_, x) in map.iter_mut(Size::from_bytes(11), Size::from_bytes(1)) { *x = 42; } - for x in map.iter_mut(Size::from_bytes(15), Size::from_bytes(1)) { + for (_, x) in map.iter_mut(Size::from_bytes(15), Size::from_bytes(1)) { *x = 43; } assert_eq!(map.v.len(), 5); assert_eq!(to_vec(&map, 10, 10), vec![-1, 42, -1, -1, -1, 43, -1, -1, -1, -1]); - for x in map.iter_mut(Size::from_bytes(10), Size::from_bytes(10)) { + for (_, x) in map.iter_mut(Size::from_bytes(10), Size::from_bytes(10)) { if *x < 42 { *x = 23; } @@ -256,14 +260,14 @@ mod tests { assert_eq!(to_vec(&map, 10, 10), vec![23, 42, 23, 23, 23, 43, 23, 23, 23, 23]); assert_eq!(to_vec(&map, 13, 5), vec![23, 23, 43, 23, 23]); - for x in map.iter_mut(Size::from_bytes(15), Size::from_bytes(5)) { + for (_, x) in map.iter_mut(Size::from_bytes(15), Size::from_bytes(5)) { *x = 19; } assert_eq!(map.v.len(), 6); assert_eq!(to_vec(&map, 10, 10), vec![23, 42, 23, 23, 23, 19, 19, 19, 19, 19]); // Should be seeing two blocks with 19. assert_eq!( - map.iter(Size::from_bytes(15), Size::from_bytes(2)).map(|&t| t).collect::>(), + map.iter(Size::from_bytes(15), Size::from_bytes(2)).map(|(_, &t)| t).collect::>(), vec![19, 19] ); diff --git a/src/stacked_borrows.rs b/src/stacked_borrows.rs index 257208056d..cf5b31597d 100644 --- a/src/stacked_borrows.rs +++ b/src/stacked_borrows.rs @@ -309,14 +309,14 @@ impl<'tcx> Stack { /// Test if a memory `access` using pointer tagged `tag` is granted. /// If yes, return the index of the item that granted it. - fn access(&mut self, access: AccessKind, tag: Tag, global: &GlobalState) -> InterpResult<'tcx> { + fn access(&mut self, access: AccessKind, ptr: Pointer, global: &GlobalState) -> InterpResult<'tcx> { // Two main steps: Find granting item, remove incompatible items above. // Step 1: Find granting item. - let granting_idx = self.find_granting(access, tag).ok_or_else(|| { + let granting_idx = self.find_granting(access, ptr.tag).ok_or_else(|| { err_sb_ub(format!( - "no item granting {} to tag {:?} found in borrow stack.", - access, tag + "no item granting {} to tag {:?} at {} found in borrow stack.", + access, ptr.tag, ptr.erase_tag(), )) })?; @@ -328,7 +328,7 @@ impl<'tcx> Stack { let first_incompatible_idx = self.find_first_write_incompatible(granting_idx); for item in self.borrows.drain(first_incompatible_idx..).rev() { trace!("access: popping item {:?}", item); - Stack::check_protector(&item, Some(tag), global)?; + Stack::check_protector(&item, Some(ptr.tag), global)?; } } else { // On a read, *disable* all `Unique` above the granting item. This ensures U2 for read accesses. @@ -343,7 +343,7 @@ impl<'tcx> Stack { let item = &mut self.borrows[idx]; if item.perm == Permission::Unique { trace!("access: disabling item {:?}", item); - Stack::check_protector(item, Some(tag), global)?; + Stack::check_protector(item, Some(ptr.tag), global)?; item.perm = Permission::Disabled; } } @@ -355,12 +355,12 @@ impl<'tcx> Stack { /// Deallocate a location: Like a write access, but also there must be no /// active protectors at all because we will remove all items. - fn dealloc(&mut self, tag: Tag, global: &GlobalState) -> InterpResult<'tcx> { + fn dealloc(&mut self, ptr: Pointer, global: &GlobalState) -> InterpResult<'tcx> { // Step 1: Find granting item. - self.find_granting(AccessKind::Write, tag).ok_or_else(|| { + self.find_granting(AccessKind::Write, ptr.tag).ok_or_else(|| { err_sb_ub(format!( - "no item granting write access for deallocation to tag {:?} found in borrow stack", - tag, + "no item granting write access for deallocation to tag {:?} at {} found in borrow stack", + ptr.tag, ptr.erase_tag(), )) })?; @@ -372,20 +372,20 @@ impl<'tcx> Stack { Ok(()) } - /// Derived a new pointer from one with the given tag. + /// Derive a new pointer from one with the given tag. /// `weak` controls whether this operation is weak or strong: weak granting does not act as /// an access, and they add the new item directly on top of the one it is derived /// from instead of all the way at the top of the stack. - fn grant(&mut self, derived_from: Tag, new: Item, global: &GlobalState) -> InterpResult<'tcx> { + fn grant(&mut self, derived_from: Pointer, new: Item, global: &GlobalState) -> InterpResult<'tcx> { // Figure out which access `perm` corresponds to. let access = if new.perm.grants(AccessKind::Write) { AccessKind::Write } else { AccessKind::Read }; // Now we figure out which item grants our parent (`derived_from`) this kind of access. // We use that to determine where to put the new item. - let granting_idx = self.find_granting(access, derived_from) + let granting_idx = self.find_granting(access, derived_from.tag) .ok_or_else(|| err_sb_ub(format!( - "trying to reborrow for {:?}, but parent tag {:?} does not have an appropriate item in the borrow stack", - new.perm, derived_from, + "trying to reborrow for {:?} at {}, but parent tag {:?} does not have an appropriate item in the borrow stack", + new.perm, derived_from.erase_tag(), derived_from.tag, )))?; // Compute where to put the new item. @@ -443,12 +443,14 @@ impl<'tcx> Stacks { &self, ptr: Pointer, size: Size, - f: impl Fn(&mut Stack, &GlobalState) -> InterpResult<'tcx>, + f: impl Fn(Pointer, &mut Stack, &GlobalState) -> InterpResult<'tcx>, ) -> InterpResult<'tcx> { let global = self.global.borrow(); let mut stacks = self.stacks.borrow_mut(); - for stack in stacks.iter_mut(ptr.offset, size) { - f(stack, &*global)?; + for (offset, stack) in stacks.iter_mut(ptr.offset, size) { + let mut cur_ptr = ptr; + cur_ptr.offset = offset; + f(cur_ptr, stack, &*global)?; } Ok(()) } @@ -487,19 +489,13 @@ impl Stacks { #[inline(always)] pub fn memory_read<'tcx>(&self, ptr: Pointer, size: Size) -> InterpResult<'tcx> { trace!("read access with tag {:?}: {:?}, size {}", ptr.tag, ptr.erase_tag(), size.bytes()); - self.for_each(ptr, size, |stack, global| { - stack.access(AccessKind::Read, ptr.tag, global)?; - Ok(()) - }) + self.for_each(ptr, size, |ptr, stack, global| stack.access(AccessKind::Read, ptr, global)) } #[inline(always)] pub fn memory_written<'tcx>(&mut self, ptr: Pointer, size: Size) -> InterpResult<'tcx> { trace!("write access with tag {:?}: {:?}, size {}", ptr.tag, ptr.erase_tag(), size.bytes()); - self.for_each(ptr, size, |stack, global| { - stack.access(AccessKind::Write, ptr.tag, global)?; - Ok(()) - }) + self.for_each(ptr, size, |ptr, stack, global| stack.access(AccessKind::Write, ptr, global)) } #[inline(always)] @@ -509,7 +505,7 @@ impl Stacks { size: Size, ) -> InterpResult<'tcx> { trace!("deallocation with tag {:?}: {:?}, size {}", ptr.tag, ptr.erase_tag(), size.bytes()); - self.for_each(ptr, size, |stack, global| stack.dealloc(ptr.tag, global)) + self.for_each(ptr, size, |ptr, stack, global| stack.dealloc(ptr, global)) } } @@ -561,14 +557,14 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Permission::SharedReadWrite }; let item = Item { perm, tag: new_tag, protector }; - stacked_borrows.for_each(cur_ptr, size, |stack, global| { - stack.grant(cur_ptr.tag, item, global) + stacked_borrows.for_each(cur_ptr, size, |cur_ptr, stack, global| { + stack.grant(cur_ptr, item, global) }) }); } }; let item = Item { perm, tag: new_tag, protector }; - stacked_borrows.for_each(ptr, size, |stack, global| stack.grant(ptr.tag, item, global)) + stacked_borrows.for_each(ptr, size, |ptr, stack, global| stack.grant(ptr, item, global)) } /// Retags an indidual pointer, returning the retagged version. From 194451345dc6b7d269a5ded6fde49883cb862d75 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 28 Oct 2020 12:23:35 +0100 Subject: [PATCH 2/5] add an option to track raw pointer tags in Stacked Borrows --- README.md | 10 +++++++--- src/bin/miri.rs | 3 +++ src/eval.rs | 14 ++++---------- src/machine.rs | 23 +++++++++++------------ src/stacked_borrows.rs | 32 ++++++++++++++++++-------------- 5 files changed, 43 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index b638c3bca1..0aa22a81ad 100644 --- a/README.md +++ b/README.md @@ -232,13 +232,17 @@ environment variable: * `-Zmiri-track-alloc-id=` shows a backtrace when the given allocation is being allocated or freed. This helps in debugging memory leaks and use after free bugs. +* `-Zmiri-track-call-id=` shows a backtrace when the given call id is + assigned to a stack frame. This helps in debugging UB related to Stacked + Borrows "protectors". * `-Zmiri-track-pointer-tag=` shows a backtrace when the given pointer tag is popped from a borrow stack (which is where the tag becomes invalid and any future use of it will error). This helps you in finding out why UB is happening and where in your code would be a good place to look for it. -* `-Zmiri-track-call-id=` shows a backtrace when the given call id is - assigned to a stack frame. This helps in debugging UB related to Stacked - Borrows "protectors". +* `-Zmiri-track-raw-pointers` makes Stacked Borrows track a pointer tag even for + raw pointers. This can make valid code fail to pass the checks (when + integer-pointer casts are involved), but also can help identify latent + aliasing issues in code that Miri accepts by default. Some native rustc `-Z` flags are also very relevant for Miri: diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 5769590ad0..ef1429a350 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -207,6 +207,9 @@ fn main() { "-Zmiri-ignore-leaks" => { miri_config.ignore_leaks = true; } + "-Zmiri-track-raw-pointers" => { + miri_config.track_raw = true; + } "--" => { after_dashdash = true; } diff --git a/src/eval.rs b/src/eval.rs index e36a0019cd..54d06feec3 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -3,8 +3,6 @@ use std::convert::TryFrom; use std::ffi::OsStr; -use rand::rngs::StdRng; -use rand::SeedableRng; use log::info; use rustc_hir::def_id::DefId; @@ -48,6 +46,8 @@ pub struct MiriConfig { pub tracked_call_id: Option, /// The allocation id to report about. pub tracked_alloc_id: Option, + /// Whether to track raw pointers in stacked borrows. + pub track_raw: bool, } impl Default for MiriConfig { @@ -64,6 +64,7 @@ impl Default for MiriConfig { tracked_pointer_tag: None, tracked_call_id: None, tracked_alloc_id: None, + track_raw: false, } } } @@ -84,14 +85,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( rustc_span::source_map::DUMMY_SP, param_env, Evaluator::new(config.communicate, config.validate, layout_cx), - MemoryExtra::new( - StdRng::seed_from_u64(config.seed.unwrap_or(0)), - config.stacked_borrows, - config.tracked_pointer_tag, - config.tracked_call_id, - config.tracked_alloc_id, - config.check_alignment, - ), + MemoryExtra::new(&config), ); // Complete initialization. EnvVars::init(&mut ecx, config.excluded_env_vars)?; diff --git a/src/machine.rs b/src/machine.rs index 544f4667e8..e9f9298e56 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -10,6 +10,7 @@ use std::fmt; use log::trace; use rand::rngs::StdRng; +use rand::SeedableRng; use rustc_data_structures::fx::FxHashMap; use rustc_middle::{ @@ -132,16 +133,14 @@ pub struct MemoryExtra { } impl MemoryExtra { - pub fn new( - rng: StdRng, - stacked_borrows: bool, - tracked_pointer_tag: Option, - tracked_call_id: Option, - tracked_alloc_id: Option, - check_alignment: AlignmentCheck, - ) -> Self { - let stacked_borrows = if stacked_borrows { - Some(Rc::new(RefCell::new(stacked_borrows::GlobalState::new(tracked_pointer_tag, tracked_call_id)))) + pub fn new(config: &MiriConfig) -> Self { + let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0)); + let stacked_borrows = if config.stacked_borrows { + Some(Rc::new(RefCell::new(stacked_borrows::GlobalState::new( + config.tracked_pointer_tag, + config.tracked_call_id, + config.track_raw, + )))) } else { None }; @@ -150,8 +149,8 @@ impl MemoryExtra { intptrcast: Default::default(), extern_statics: FxHashMap::default(), rng: RefCell::new(rng), - tracked_alloc_id, - check_alignment, + tracked_alloc_id: config.tracked_alloc_id, + check_alignment: config.check_alignment, } } diff --git a/src/stacked_borrows.rs b/src/stacked_borrows.rs index cf5b31597d..616950eb0a 100644 --- a/src/stacked_borrows.rs +++ b/src/stacked_borrows.rs @@ -108,6 +108,8 @@ pub struct GlobalState { tracked_pointer_tag: Option, /// The call id to trace tracked_call_id: Option, + /// Whether to track raw pointers. + track_raw: bool, } /// Memory extra state gives us interior mutable access to the global state. pub type MemoryExtra = Rc>; @@ -155,7 +157,7 @@ impl fmt::Display for RefKind { /// Utilities for initialization and ID generation impl GlobalState { - pub fn new(tracked_pointer_tag: Option, tracked_call_id: Option) -> Self { + pub fn new(tracked_pointer_tag: Option, tracked_call_id: Option, track_raw: bool) -> Self { GlobalState { next_ptr_id: NonZeroU64::new(1).unwrap(), base_ptr_ids: FxHashMap::default(), @@ -163,6 +165,7 @@ impl GlobalState { active_calls: FxHashSet::default(), tracked_pointer_tag, tracked_call_id, + track_raw, } } @@ -479,9 +482,12 @@ impl Stacks { // The base pointer is not unique, so the base permission is `SharedReadWrite`. MemoryKind::Machine(MiriMemoryKind::Global | MiriMemoryKind::ExternStatic | MiriMemoryKind::Tls | MiriMemoryKind::Env) => (extra.borrow_mut().global_base_ptr(id), Permission::SharedReadWrite), - // Everything else we handle entirely untagged for now. - // FIXME: experiment with more precise tracking. - _ => (Tag::Untagged, Permission::SharedReadWrite), + // Everything else we handle like raw pointers for now. + _ => { + let mut extra = extra.borrow_mut(); + let tag = if extra.track_raw { Tag::Tagged(extra.new_ptr()) } else { Tag::Untagged }; + (tag, Permission::SharedReadWrite) + } }; (Stacks::new(size, perm, tag, extra), tag) } @@ -593,16 +599,14 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } // Compute new borrow. - let new_tag = match kind { - // Give up tracking for raw pointers. - // FIXME: Experiment with more precise tracking. Blocked on `&raw` - // because `Rc::into_raw` currently creates intermediate references, - // breaking `Rc::from_raw`. - RefKind::Raw { .. } => Tag::Untagged, - // All other pointesr are properly tracked. - _ => Tag::Tagged( - this.memory.extra.stacked_borrows.as_ref().unwrap().borrow_mut().new_ptr(), - ), + let new_tag = { + let mut mem_extra = this.memory.extra.stacked_borrows.as_ref().unwrap().borrow_mut(); + match kind { + // Give up tracking for raw pointers. + RefKind::Raw { .. } if !mem_extra.track_raw => Tag::Untagged, + // All other pointers are properly tracked. + _ => Tag::Tagged(mem_extra.new_ptr()), + } }; // Reborrow. From 19e78a65d95d03b6d7ac670075231837cc00edf4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 28 Oct 2020 12:35:05 +0100 Subject: [PATCH 3/5] run some tests with raw pointer tracking --- tests/run-pass/format.rs | 2 ++ tests/run-pass/slices.rs | 1 + tests/run-pass/vec.rs | 1 + tests/run-pass/vecdeque.rs | 1 + 4 files changed, 5 insertions(+) diff --git a/tests/run-pass/format.rs b/tests/run-pass/format.rs index 053cce3613..3893efcb26 100644 --- a/tests/run-pass/format.rs +++ b/tests/run-pass/format.rs @@ -1,3 +1,5 @@ +// compile-flags: -Zmiri-track-raw-pointers + fn main() { println!("Hello {}", 13); println!("{:0(dummy: &mut T, iter: impl Iterator) { diff --git a/tests/run-pass/vecdeque.rs b/tests/run-pass/vecdeque.rs index 34f32ee1d9..54aeb89ec8 100644 --- a/tests/run-pass/vecdeque.rs +++ b/tests/run-pass/vecdeque.rs @@ -1,3 +1,4 @@ +// compile-flags: -Zmiri-track-raw-pointers use std::collections::VecDeque; fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator) { From 70af7aed88456b8c478e06d1d28b0d8523a46b38 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 28 Oct 2020 12:51:25 +0100 Subject: [PATCH 4/5] expand flag docs --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0aa22a81ad..0c280edec0 100644 --- a/README.md +++ b/README.md @@ -240,9 +240,12 @@ environment variable: future use of it will error). This helps you in finding out why UB is happening and where in your code would be a good place to look for it. * `-Zmiri-track-raw-pointers` makes Stacked Borrows track a pointer tag even for - raw pointers. This can make valid code fail to pass the checks (when - integer-pointer casts are involved), but also can help identify latent - aliasing issues in code that Miri accepts by default. + raw pointers. This can make valid code fail to pass the checks, but also can + help identify latent aliasing issues in code that Miri accepts by default. You + can recognize false positives by "" occurring in the message -- this + indicates a pointer that was cast from an integer, so Miri was unable to track + this pointer. Make sure to use a non-Windows target with this flag, as the + Windows runtime makes use of integer-pointer casts. Some native rustc `-Z` flags are also very relevant for Miri: From bf54607ba03cf12a015e7027be6d5ffdf08cc3ca Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 28 Oct 2020 13:50:27 +0100 Subject: [PATCH 5/5] test raw pointer tracking; we cannot track raw pointers on Windows --- tests/compile-fail/stacked_borrows/raw_tracking.rs | 13 +++++++++++++ tests/run-pass/format.rs | 2 -- tests/run-pass/slices.rs | 1 - tests/run-pass/vec.rs | 1 + tests/run-pass/vecdeque.rs | 1 + 5 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 tests/compile-fail/stacked_borrows/raw_tracking.rs diff --git a/tests/compile-fail/stacked_borrows/raw_tracking.rs b/tests/compile-fail/stacked_borrows/raw_tracking.rs new file mode 100644 index 0000000000..b9ddee328f --- /dev/null +++ b/tests/compile-fail/stacked_borrows/raw_tracking.rs @@ -0,0 +1,13 @@ +// compile-flags: -Zmiri-track-raw-pointers +// ignore-windows (FIXME: tracking raw pointers does not work on Windows) +//! This demonstrates a provenance problem that requires tracking of raw pointers to be detected. + +fn main() { + let mut l = 13; + let raw1 = &mut l as *mut _; + let raw2 = &mut l as *mut _; // invalidates raw1 + // Without raw pointer tracking, Stacked Borrows cannot distinguish raw1 and raw2, and thus + // fails to realize that raw1 should not be used any more. + unsafe { *raw1 = 13; } //~ ERROR no item granting write access to tag + unsafe { *raw2 = 13; } +} diff --git a/tests/run-pass/format.rs b/tests/run-pass/format.rs index 3893efcb26..053cce3613 100644 --- a/tests/run-pass/format.rs +++ b/tests/run-pass/format.rs @@ -1,5 +1,3 @@ -// compile-flags: -Zmiri-track-raw-pointers - fn main() { println!("Hello {}", 13); println!("{:0(dummy: &mut T, iter: impl Iterator) { diff --git a/tests/run-pass/vecdeque.rs b/tests/run-pass/vecdeque.rs index 54aeb89ec8..55b47f622f 100644 --- a/tests/run-pass/vecdeque.rs +++ b/tests/run-pass/vecdeque.rs @@ -1,4 +1,5 @@ // compile-flags: -Zmiri-track-raw-pointers +// ignore-windows (FIXME: tracking raw pointers does not work on Windows) use std::collections::VecDeque; fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator) {