about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2021-05-26 13:32:06 +0200
committerGitHub <noreply@github.com>2021-05-26 13:32:06 +0200
commit27899e3887c1e7c767b51ca5ee5e88ac4a543687 (patch)
tree8d51c2ab81bc1e9b7c6753b4eaaea07cf942b8b2
parent69c78a98ee2fb1e96675beb50115495bffca9777 (diff)
parentc9595faa2850b01966c64682d2bb8b32e0955625 (diff)
downloadrust-27899e3887c1e7c767b51ca5ee5e88ac4a543687.tar.gz
rust-27899e3887c1e7c767b51ca5ee5e88ac4a543687.zip
Rollup merge of #85625 - SkiFire13:fix-85613-vec-dedup-drop-panics, r=nagisa
Prevent double drop in `Vec::dedup_by` if a destructor panics

Fixes #85613
-rw-r--r--library/alloc/src/vec/mod.rs5
-rw-r--r--library/alloc/tests/vec.rs48
2 files changed, 28 insertions, 25 deletions
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index 1c33ff555d6..105c60e7bf0 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -1619,6 +1619,8 @@ impl<T, A: Allocator> Vec<T, A> {
                 let prev_ptr = ptr.add(gap.write.wrapping_sub(1));
 
                 if same_bucket(&mut *read_ptr, &mut *prev_ptr) {
+                    // Increase `gap.read` now since the drop may panic.
+                    gap.read += 1;
                     /* We have found duplicate, drop it in-place */
                     ptr::drop_in_place(read_ptr);
                 } else {
@@ -1631,9 +1633,8 @@ impl<T, A: Allocator> Vec<T, A> {
 
                     /* We have filled that place, so go further */
                     gap.write += 1;
+                    gap.read += 1;
                 }
-
-                gap.read += 1;
             }
 
             /* Technically we could let `gap` clean up with its Drop, but
diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs
index ad69234403b..36c81b49709 100644
--- a/library/alloc/tests/vec.rs
+++ b/library/alloc/tests/vec.rs
@@ -2234,48 +2234,50 @@ fn test_vec_dedup() {
 #[test]
 fn test_vec_dedup_panicking() {
     #[derive(Debug)]
-    struct Panic {
-        drop_counter: &'static AtomicU32,
+    struct Panic<'a> {
+        drop_counter: &'a Cell<u32>,
         value: bool,
         index: usize,
     }
 
-    impl PartialEq for Panic {
+    impl<'a> PartialEq for Panic<'a> {
         fn eq(&self, other: &Self) -> bool {
             self.value == other.value
         }
     }
 
-    impl Drop for Panic {
+    impl<'a> Drop for Panic<'a> {
         fn drop(&mut self) {
-            let x = self.drop_counter.fetch_add(1, Ordering::SeqCst);
-            assert!(x != 4);
+            self.drop_counter.set(self.drop_counter.get() + 1);
+            if !std::thread::panicking() {
+                assert!(self.index != 4);
+            }
         }
     }
 
-    static DROP_COUNTER: AtomicU32 = AtomicU32::new(0);
+    let drop_counter = &Cell::new(0);
     let expected = [
-        Panic { drop_counter: &DROP_COUNTER, value: false, index: 0 },
-        Panic { drop_counter: &DROP_COUNTER, value: false, index: 5 },
-        Panic { drop_counter: &DROP_COUNTER, value: true, index: 6 },
-        Panic { drop_counter: &DROP_COUNTER, value: true, index: 7 },
+        Panic { drop_counter, value: false, index: 0 },
+        Panic { drop_counter, value: false, index: 5 },
+        Panic { drop_counter, value: true, index: 6 },
+        Panic { drop_counter, value: true, index: 7 },
     ];
     let mut vec = vec![
-        Panic { drop_counter: &DROP_COUNTER, value: false, index: 0 },
+        Panic { drop_counter, value: false, index: 0 },
         // these elements get deduplicated
-        Panic { drop_counter: &DROP_COUNTER, value: false, index: 1 },
-        Panic { drop_counter: &DROP_COUNTER, value: false, index: 2 },
-        Panic { drop_counter: &DROP_COUNTER, value: false, index: 3 },
-        Panic { drop_counter: &DROP_COUNTER, value: false, index: 4 },
-        // here it panics
-        Panic { drop_counter: &DROP_COUNTER, value: false, index: 5 },
-        Panic { drop_counter: &DROP_COUNTER, value: true, index: 6 },
-        Panic { drop_counter: &DROP_COUNTER, value: true, index: 7 },
+        Panic { drop_counter, value: false, index: 1 },
+        Panic { drop_counter, value: false, index: 2 },
+        Panic { drop_counter, value: false, index: 3 },
+        Panic { drop_counter, value: false, index: 4 },
+        // here it panics while dropping the item with index==4
+        Panic { drop_counter, value: false, index: 5 },
+        Panic { drop_counter, value: true, index: 6 },
+        Panic { drop_counter, value: true, index: 7 },
     ];
 
-    let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
-        vec.dedup();
-    }));
+    let _ = catch_unwind(AssertUnwindSafe(|| vec.dedup())).unwrap_err();
+
+    assert_eq!(drop_counter.get(), 4);
 
     let ok = vec.iter().zip(expected.iter()).all(|(x, y)| x.index == y.index);