about summary refs log tree commit diff
path: root/src/liballoc/vec.rs
diff options
context:
space:
mode:
authorJonas Schievink <jonasschievink@gmail.com>2019-12-13 17:54:26 +0100
committerJonas Schievink <jonasschievink@gmail.com>2020-01-19 20:23:41 +0100
commit5d04790dd2e73f3faf08d528e3675e131585ec01 (patch)
tree5aa4271596ae881762bf2d636979512a622a9b9a /src/liballoc/vec.rs
parent3e5eb2634cbb356b626e028a4be1305d4a44a023 (diff)
downloadrust-5d04790dd2e73f3faf08d528e3675e131585ec01.tar.gz
rust-5d04790dd2e73f3faf08d528e3675e131585ec01.zip
Avoid leak in `vec::Drain` when item drop panics
Diffstat (limited to 'src/liballoc/vec.rs')
-rw-r--r--src/liballoc/vec.rs43
1 files changed, 30 insertions, 13 deletions
diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs
index e9cbf627846..ba71e42090c 100644
--- a/src/liballoc/vec.rs
+++ b/src/liballoc/vec.rs
@@ -2700,23 +2700,40 @@ impl<T> DoubleEndedIterator for Drain<'_, T> {
 #[stable(feature = "drain", since = "1.6.0")]
 impl<T> Drop for Drain<'_, T> {
     fn drop(&mut self) {
-        // exhaust self first
-        self.for_each(drop);
+        /// Continues dropping the remaining elements when a destructor unwinds.
+        struct DropGuard<'r, 'a, T>(&'r mut Drain<'a, T>);
 
-        if self.tail_len > 0 {
-            unsafe {
-                let source_vec = self.vec.as_mut();
-                // memmove back untouched tail, update to new length
-                let start = source_vec.len();
-                let tail = self.tail_start;
-                if tail != start {
-                    let src = source_vec.as_ptr().add(tail);
-                    let dst = source_vec.as_mut_ptr().add(start);
-                    ptr::copy(src, dst, self.tail_len);
+        impl<'r, 'a, T> Drop for DropGuard<'r, 'a, T> {
+            fn drop(&mut self) {
+                // Continue the same loop we do below. This only runs when a destructor has
+                // panicked. If another one panics this will abort.
+                self.0.for_each(drop);
+
+                if self.0.tail_len > 0 {
+                    unsafe {
+                        let source_vec = self.0.vec.as_mut();
+                        // memmove back untouched tail, update to new length
+                        let start = source_vec.len();
+                        let tail = self.0.tail_start;
+                        if tail != start {
+                            let src = source_vec.as_ptr().add(tail);
+                            let dst = source_vec.as_mut_ptr().add(start);
+                            ptr::copy(src, dst, self.0.tail_len);
+                        }
+                        source_vec.set_len(start + self.0.tail_len);
+                    }
                 }
-                source_vec.set_len(start + self.tail_len);
             }
         }
+
+        // exhaust self first
+        while let Some(item) = self.next() {
+            let guard = DropGuard(self);
+            drop(item);
+            mem::forget(guard);
+        }
+
+        DropGuard(self);
     }
 }