about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-04-19 17:12:17 +0000
committerbors <bors@rust-lang.org>2019-04-19 17:12:17 +0000
commit130dc3e7dac132cf30272ccf4541b512828e2108 (patch)
tree7dbba29cf92a6a02a0d2a20939fee701cb44d6fc /src
parenta2bbf7debaab60be33bd8008a71bca69576945a0 (diff)
parent8b09d046fedf84ed869204bbe0779a0439bbf9eb (diff)
downloadrust-130dc3e7dac132cf30272ccf4541b512828e2108.tar.gz
rust-130dc3e7dac132cf30272ccf4541b512828e2108.zip
Auto merge of #60072 - RalfJung:linked-list, r=shepmaster
fix LinkedList invalidating mutable references

The test `test_insert_prev` failed in Miri due to what I consider a bug in `LinkedList`: in various places, `NonNull::as_mut` got called to modify the `prev`/`next` pointers of existing nodes. In particular, the unstable `insert_next` has to modify the `next` pointer of the node that was last handed out by the iterator; to this end it creates a mutable reference to the *entire node* that overlaps with the mutable reference to the node's content that was handed out by the iterator! Thus, the next use if said mutable reference is UB.

In code:
```rust
            loop {
                match it.next() { // mutable reference handed to us
                    None => break,
                    Some(elt) => {
                        it.insert_next(*elt + 1); // this invalidates `elt` because it creates an overlapping mutable reference
                        match it.peek_next() {
                            Some(x) => assert_eq!(*x, *elt + 2), // this use of `elt` now is a use of an invalid pointer
                            None => assert_eq!(8, *elt),
                        }
                    }
                }
            }
```

This PR fixes that by using `as_ptr` instead of `as_mut`. This avoids invalidating the mutable reference that was handed to the user.  I did this in all methods called by iterators, just to be sure.

Cc @Gankro
Diffstat (limited to 'src')
-rw-r--r--src/liballoc/collections/linked_list.rs48
1 files changed, 37 insertions, 11 deletions
diff --git a/src/liballoc/collections/linked_list.rs b/src/liballoc/collections/linked_list.rs
index cb390aa419e..40a82d6feaa 100644
--- a/src/liballoc/collections/linked_list.rs
+++ b/src/liballoc/collections/linked_list.rs
@@ -86,6 +86,9 @@ impl<T> Clone for Iter<'_, T> {
 /// [`LinkedList`]: struct.LinkedList.html
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct IterMut<'a, T: 'a> {
+    // We do *not* exclusively own the entire list here, references to node's `element`
+    // have been handed out by the iterator!  So be careful when using this; the methods
+    // called must be aware that there can be aliasing pointers to `element`.
     list: &'a mut LinkedList<T>,
     head: Option<NonNull<Node<T>>>,
     tail: Option<NonNull<Node<T>>>,
@@ -143,6 +146,8 @@ impl<T> LinkedList<T> {
     /// Adds the given node to the front of the list.
     #[inline]
     fn push_front_node(&mut self, mut node: Box<Node<T>>) {
+        // This method takes care not to create mutable references to whole nodes,
+        // to maintain validity of aliasing pointers into `element`.
         unsafe {
             node.next = self.head;
             node.prev = None;
@@ -150,7 +155,8 @@ impl<T> LinkedList<T> {
 
             match self.head {
                 None => self.tail = node,
-                Some(mut head) => head.as_mut().prev = node,
+                // Not creating new mutable (unique!) references overlapping `element`.
+                Some(head) => (*head.as_ptr()).prev = node,
             }
 
             self.head = node;
@@ -161,13 +167,16 @@ impl<T> LinkedList<T> {
     /// Removes and returns the node at the front of the list.
     #[inline]
     fn pop_front_node(&mut self) -> Option<Box<Node<T>>> {
+        // This method takes care not to create mutable references to whole nodes,
+        // to maintain validity of aliasing pointers into `element`.
         self.head.map(|node| unsafe {
             let node = Box::from_raw(node.as_ptr());
             self.head = node.next;
 
             match self.head {
                 None => self.tail = None,
-                Some(mut head) => head.as_mut().prev = None,
+                // Not creating new mutable (unique!) references overlapping `element`.
+                Some(head) => (*head.as_ptr()).prev = None,
             }
 
             self.len -= 1;
@@ -178,6 +187,8 @@ impl<T> LinkedList<T> {
     /// Adds the given node to the back of the list.
     #[inline]
     fn push_back_node(&mut self, mut node: Box<Node<T>>) {
+        // This method takes care not to create mutable references to whole nodes,
+        // to maintain validity of aliasing pointers into `element`.
         unsafe {
             node.next = None;
             node.prev = self.tail;
@@ -185,7 +196,8 @@ impl<T> LinkedList<T> {
 
             match self.tail {
                 None => self.head = node,
-                Some(mut tail) => tail.as_mut().next = node,
+                // Not creating new mutable (unique!) references overlapping `element`.
+                Some(tail) => (*tail.as_ptr()).next = node,
             }
 
             self.tail = node;
@@ -196,13 +208,16 @@ impl<T> LinkedList<T> {
     /// Removes and returns the node at the back of the list.
     #[inline]
     fn pop_back_node(&mut self) -> Option<Box<Node<T>>> {
+        // This method takes care not to create mutable references to whole nodes,
+        // to maintain validity of aliasing pointers into `element`.
         self.tail.map(|node| unsafe {
             let node = Box::from_raw(node.as_ptr());
             self.tail = node.prev;
 
             match self.tail {
                 None => self.head = None,
-                Some(mut tail) => tail.as_mut().next = None,
+                // Not creating new mutable (unique!) references overlapping `element`.
+                Some(tail) => (*tail.as_ptr()).next = None,
             }
 
             self.len -= 1;
@@ -213,18 +228,22 @@ impl<T> LinkedList<T> {
     /// Unlinks the specified node from the current list.
     ///
     /// Warning: this will not check that the provided node belongs to the current list.
+    ///
+    /// This method takes care not to create mutable references to `element`, to
+    /// maintain validity of aliasing pointers.
     #[inline]
     unsafe fn unlink_node(&mut self, mut node: NonNull<Node<T>>) {
-        let node = node.as_mut();
+        let node = node.as_mut(); // this one is ours now, we can create an &mut.
 
+        // Not creating new mutable (unique!) references overlapping `element`.
         match node.prev {
-            Some(mut prev) => prev.as_mut().next = node.next.clone(),
+            Some(prev) => (*prev.as_ptr()).next = node.next.clone(),
             // this node is the head node
             None => self.head = node.next.clone(),
         };
 
         match node.next {
-            Some(mut next) => next.as_mut().prev = node.prev.clone(),
+            Some(next) => (*next.as_ptr()).prev = node.prev.clone(),
             // this node is the tail node
             None => self.tail = node.prev.clone(),
         };
@@ -297,6 +316,8 @@ impl<T> LinkedList<T> {
         match self.tail {
             None => mem::swap(self, other),
             Some(mut tail) => {
+                // `as_mut` is okay here because we have exclusive access to the entirety
+                // of both lists.
                 if let Some(mut other_head) = other.head.take() {
                     unsafe {
                         tail.as_mut().next = Some(other_head);
@@ -916,9 +937,11 @@ impl<T> IterMut<'_, T> {
                issue = "27794")]
     pub fn insert_next(&mut self, element: T) {
         match self.head {
+            // `push_back` is okay with aliasing `element` references
             None => self.list.push_back(element),
-            Some(mut head) => unsafe {
-                let mut prev = match head.as_ref().prev {
+            Some(head) => unsafe {
+                let prev = match head.as_ref().prev {
+                    // `push_front` is okay with aliasing nodes
                     None => return self.list.push_front(element),
                     Some(prev) => prev,
                 };
@@ -929,8 +952,10 @@ impl<T> IterMut<'_, T> {
                     element,
                 }));
 
-                prev.as_mut().next = node;
-                head.as_mut().prev = node;
+                // Not creating references to entire nodes to not invalidate the
+                // reference to `element` we handed to the user.
+                (*prev.as_ptr()).next = node;
+                (*head.as_ptr()).prev = node;
 
                 self.list.len += 1;
             },
@@ -994,6 +1019,7 @@ impl<T, F> Iterator for DrainFilter<'_, T, F>
                 self.idx += 1;
 
                 if (self.pred)(&mut node.as_mut().element) {
+                    // `unlink_node` is okay with aliasing `element` references.
                     self.list.unlink_node(node);
                     return Some(Box::from_raw(node.as_ptr()).element);
                 }