about summary refs log tree commit diff
diff options
context:
space:
mode:
authorWaffle <waffle.lapkin@gmail.com>2021-03-04 12:19:52 +0300
committerWaffle <waffle.lapkin@gmail.com>2021-03-04 17:10:57 +0300
commit1f031d95ded44337848f53e07aae05087ccf15f1 (patch)
tree444453926e7276caff801e46e4696ab226246706
parent84e9608596f14eba78b8ff0371f747f7513b523a (diff)
downloadrust-1f031d95ded44337848f53e07aae05087ccf15f1.tar.gz
rust-1f031d95ded44337848f53e07aae05087ccf15f1.zip
Add regression test for `Vec::extend_from_within` leak
-rw-r--r--library/alloc/src/vec/mod.rs8
-rw-r--r--library/alloc/tests/vec.rs42
2 files changed, 47 insertions, 3 deletions
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index 369fa460863..49758f672a8 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -1944,14 +1944,16 @@ impl<T, A: Allocator> Vec<T, A> {
     pub fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit<T>]) {
         // SAFETY:
         // - len is ignored and so never changed
-        let (init, spare, _) = unsafe{ self.split_at_spare_mut_with_len() };
+        let (init, spare, _) = unsafe { self.split_at_spare_mut_with_len() };
         (init, spare)
     }
 
     /// Safety: changing returned .2 (&mut usize) is considered the same as calling `.set_len(_)`.
     ///
     /// This method is used to have unique access to all vec parts at once in `extend_from_within`.
-    unsafe fn split_at_spare_mut_with_len(&mut self) -> (&mut [T], &mut [MaybeUninit<T>], &mut usize) {
+    unsafe fn split_at_spare_mut_with_len(
+        &mut self,
+    ) -> (&mut [T], &mut [MaybeUninit<T>], &mut usize) {
         let Range { start: ptr, end: spare_ptr } = self.as_mut_ptr_range();
         let spare_ptr = spare_ptr.cast::<MaybeUninit<T>>();
         let spare_len = self.buf.capacity() - self.len;
@@ -1965,7 +1967,7 @@ impl<T, A: Allocator> Vec<T, A> {
 
             (initialized, spare, &mut self.len)
         }
-    } 
+    }
 }
 
 impl<T: Clone, A: Allocator> Vec<T, A> {
diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs
index fab45028585..1ba2315ca73 100644
--- a/library/alloc/tests/vec.rs
+++ b/library/alloc/tests/vec.rs
@@ -7,6 +7,7 @@ use std::mem::{size_of, swap};
 use std::ops::Bound::*;
 use std::panic::{catch_unwind, AssertUnwindSafe};
 use std::rc::Rc;
+use std::sync::atomic::{AtomicU32, Ordering};
 use std::vec::{Drain, IntoIter};
 
 struct DropCounter<'a> {
@@ -2100,3 +2101,44 @@ fn test_extend_from_within() {
 
     assert_eq!(v, ["a", "b", "c", "b", "c", "a", "b"]);
 }
+
+// Regression test for issue #82533
+#[test]
+fn test_extend_from_within_panicing_clone() {
+    struct Panic<'dc> {
+        drop_count: &'dc AtomicU32,
+        aaaaa: bool,
+    }
+
+    impl Clone for Panic<'_> {
+        fn clone(&self) -> Self {
+            if self.aaaaa {
+                panic!("panic! at the clone");
+            }
+
+            Self { ..*self }
+        }
+    }
+
+    impl Drop for Panic<'_> {
+        fn drop(&mut self) {
+            self.drop_count.fetch_add(1, Ordering::SeqCst);
+        }
+    }
+
+    let count = core::sync::atomic::AtomicU32::new(0);
+    let mut vec = vec![
+        Panic { drop_count: &count, aaaaa: false },
+        Panic { drop_count: &count, aaaaa: true },
+        Panic { drop_count: &count, aaaaa: false },
+    ];
+
+    // This should clone&append one Panic{..} at the end, and then panic while
+    // cloning second Panic{..}. This means that `Panic::drop` should be called
+    // 4 times (3 for items already in vector, 1 for just appended).
+    //
+    // Previously just appended item was leaked, making drop_count = 3, instead of 4.
+    std::panic::catch_unwind(move || vec.extend_from_within(..)).unwrap_err();
+
+    assert_eq!(count.load(Ordering::SeqCst), 4);
+}