about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs14
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs42
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs4
3 files changed, 40 insertions, 20 deletions
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
index 5c7f5ea46ba..bec51c7cdf2 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
@@ -181,8 +181,12 @@ impl Permission {
     pub fn is_initial(&self) -> bool {
         self.inner.is_initial()
     }
+    /// Check if `self` is the terminal state of a pointer (is `Disabled`).
+    pub fn is_disabled(&self) -> bool {
+        self.inner == Disabled
+    }
 
-    /// Default initial permission of the root of a new tree.
+    /// Default initial permission of the root of a new tree at inbounds positions.
     /// Must *only* be used for the root, this is not in general an "initial" permission!
     pub fn new_active() -> Self {
         Self { inner: Active }
@@ -193,11 +197,17 @@ impl Permission {
         Self { inner: Reserved { ty_is_freeze, conflicted: false } }
     }
 
-    /// Default initial permission of a reborrowed shared reference
+    /// Default initial permission of a reborrowed shared reference.
     pub fn new_frozen() -> Self {
         Self { inner: Frozen }
     }
 
+    /// Default initial permission of  the root of a new tre at out-of-bounds positions.
+    /// Must *only* be used for the root, this is not in general an "initial" permission!
+    pub fn new_disabled() -> Self {
+        Self { inner: Disabled }
+    }
+
     /// Apply the transition to the inner PermissionPriv.
     pub fn perform_access(
         kind: AccessKind,
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
index dda1c7cca19..0fea78daa88 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
@@ -42,6 +42,7 @@ pub(super) struct LocationState {
     /// protected, that is *not* the case for uninitialized locations. Instead we just have a latent
     /// "future initial permission" of `Disabled`, causing UB only if an access is ever actually
     /// performed.
+    /// Note that the tree root is also always initialized, as if the allocation was a write access.
     initialized: bool,
     /// This pointer's current permission / future initial permission.
     permission: Permission,
@@ -55,17 +56,18 @@ pub(super) struct LocationState {
 }
 
 impl LocationState {
-    /// Default initial state has never been accessed and has been subjected to no
-    /// foreign access.
-    fn new(permission: Permission) -> Self {
+    /// Constructs a new initial state. It has neither been accessed, nor been subjected
+    /// to any foreign access yet.
+    /// The permission is not allowed to be `Active`.
+    fn new_uninit(permission: Permission) -> Self {
+        assert!(permission.is_initial() || permission.is_disabled());
         Self { permission, initialized: false, latest_foreign_access: None }
     }
 
-    /// Record that this location was accessed through a child pointer by
-    /// marking it as initialized
-    fn with_access(mut self) -> Self {
-        self.initialized = true;
-        self
+    /// Constructs a new initial state. It has not yet been subjected
+    /// to any foreign access. However, it is already marked as having been accessed.
+    fn new_init(permission: Permission) -> Self {
+        Self { permission, initialized: true, latest_foreign_access: None }
     }
 
     /// Check if the location has been initialized, i.e. if it has
@@ -238,8 +240,10 @@ pub(super) struct Node {
     /// If the pointer was reborrowed, it has children.
     // FIXME: bench to compare this to FxHashSet and to other SmallVec sizes
     pub children: SmallVec<[UniIndex; 4]>,
-    /// Either `Reserved` or `Frozen`, the permission this tag will be lazily initialized
-    /// to on the first access.
+    /// Either `Reserved`,  `Frozen`, or `Disabled`, it is the permission this tag will
+    /// lazily be initialized to on the first access.
+    /// It is only ever `Disabled` for a tree root, since the root is initialized to `Active` by
+    /// its own separate mechanism.
     default_initial_perm: Permission,
     /// Some extra information useful only for debugging purposes
     pub debug_info: NodeDebugInfo,
@@ -444,12 +448,14 @@ impl<'tree> TreeVisitor<'tree> {
 impl Tree {
     /// Create a new tree, with only a root pointer.
     pub fn new(root_tag: BorTag, size: Size, span: Span) -> Self {
-        let root_perm = Permission::new_active();
+        // The root has `Disabled` as the default permission,
+        // so that any access out of bounds is invalid.
+        let root_default_perm = Permission::new_disabled();
         let mut tag_mapping = UniKeyMap::default();
         let root_idx = tag_mapping.insert(root_tag);
         let nodes = {
             let mut nodes = UniValMap::<Node>::default();
-            let mut debug_info = NodeDebugInfo::new(root_tag, root_perm, span);
+            let mut debug_info = NodeDebugInfo::new(root_tag, root_default_perm, span);
             // name the root so that all allocations contain one named pointer
             debug_info.add_name("root of the allocation");
             nodes.insert(
@@ -458,7 +464,7 @@ impl Tree {
                     tag: root_tag,
                     parent: None,
                     children: SmallVec::default(),
-                    default_initial_perm: root_perm,
+                    default_initial_perm: root_default_perm,
                     debug_info,
                 },
             );
@@ -466,7 +472,11 @@ impl Tree {
         };
         let rperms = {
             let mut perms = UniValMap::default();
-            perms.insert(root_idx, LocationState::new(root_perm).with_access());
+            // We manually set it to `Active` on all in-bounds positions.
+            // We also ensure that it is initalized, so that no `Active` but
+            // not yet initialized nodes exist. Essentially, we pretend there
+            // was a write that initialized these to `Active`.
+            perms.insert(root_idx, LocationState::new_init(Permission::new_active()));
             RangeMap::new(size, perms)
         };
         Self { root: root_idx, nodes, rperms, tag_mapping }
@@ -500,7 +510,7 @@ impl<'tcx> Tree {
         // Register new_tag as a child of parent_tag
         self.nodes.get_mut(parent_idx).unwrap().children.push(idx);
         // Initialize perms
-        let perm = LocationState::new(default_initial_perm).with_access();
+        let perm = LocationState::new_init(default_initial_perm);
         for (_perms_range, perms) in self.rperms.iter_mut(reborrow_range.start, reborrow_range.size)
         {
             perms.insert(idx, perm);
@@ -599,7 +609,7 @@ impl<'tcx> Tree {
          -> Result<ContinueTraversal, TransitionError> {
             let NodeAppArgs { node, mut perm, rel_pos } = args;
 
-            let old_state = perm.or_insert(LocationState::new(node.default_initial_perm));
+            let old_state = perm.or_insert(LocationState::new_uninit(node.default_initial_perm));
 
             match old_state.skip_if_known_noop(access_kind, rel_pos) {
                 ContinueTraversal::SkipChildren => return Ok(ContinueTraversal::SkipChildren),
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs
index 35f3b53afdb..f568850d8db 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs
@@ -516,11 +516,11 @@ mod spurious_read {
         let source = LocStateProtPair {
             xy_rel: RelPosXY::MutuallyForeign,
             x: LocStateProt {
-                state: LocationState::new(Permission::new_frozen()).with_access(),
+                state: LocationState::new_init(Permission::new_frozen()),
                 prot: true,
             },
             y: LocStateProt {
-                state: LocationState::new(Permission::new_reserved(false)),
+                state: LocationState::new_uninit(Permission::new_reserved(false)),
                 prot: true,
             },
         };