diff options
| author | Jonas Schievink <jonasschievink@gmail.com> | 2019-12-14 00:55:06 +0100 |
|---|---|---|
| committer | Jonas Schievink <jonasschievink@gmail.com> | 2020-01-19 20:24:36 +0100 |
| commit | 0ae16b47ffee866ee49f909ea213a28d8068cf56 (patch) | |
| tree | bbc88dd073366bc4f1cab9f88f3aec0ce4c2430e /src/liballoc | |
| parent | 163ed23f0081d7283ccaef39141bc29879260663 (diff) | |
| download | rust-0ae16b47ffee866ee49f909ea213a28d8068cf56.tar.gz rust-0ae16b47ffee866ee49f909ea213a28d8068cf56.zip | |
Avoid leak in DrainFilter when a drop panics
Diffstat (limited to 'src/liballoc')
| -rw-r--r-- | src/liballoc/collections/linked_list.rs | 16 | ||||
| -rw-r--r-- | src/liballoc/tests/linked_list.rs | 36 |
2 files changed, 50 insertions, 2 deletions
diff --git a/src/liballoc/collections/linked_list.rs b/src/liballoc/collections/linked_list.rs index b88ca8a0fb0..463ec67d668 100644 --- a/src/liballoc/collections/linked_list.rs +++ b/src/liballoc/collections/linked_list.rs @@ -1565,7 +1565,21 @@ where F: FnMut(&mut T) -> bool, { fn drop(&mut self) { - self.for_each(drop); + struct DropGuard<'r, 'a, T, F>(&'r mut DrainFilter<'a, T, F>) + where F: FnMut(&mut T) -> bool; + + impl<'r, 'a, T, F> Drop for DropGuard<'r, 'a, T, F> + where F: FnMut(&mut T) -> bool { + fn drop(&mut self) { + self.0.for_each(drop); + } + } + + while let Some(item) = self.next() { + let guard = DropGuard(self); + drop(item); + mem::forget(guard); + } } } diff --git a/src/liballoc/tests/linked_list.rs b/src/liballoc/tests/linked_list.rs index b7736515b26..aa3f582a165 100644 --- a/src/liballoc/tests/linked_list.rs +++ b/src/liballoc/tests/linked_list.rs @@ -1,5 +1,5 @@ use std::collections::LinkedList; -use std::panic::catch_unwind; +use std::panic::{catch_unwind, AssertUnwindSafe}; #[test] fn test_basic() { @@ -532,6 +532,40 @@ fn drain_filter_complex() { } #[test] +fn drain_filter_drop_panic_leak() { + static mut DROPS: i32 = 0; + + struct D(bool); + + impl Drop for D { + fn drop(&mut self) { + unsafe { + DROPS += 1; + } + + if self.0 { + panic!("panic in `drop`"); + } + } + } + + let mut q = LinkedList::new(); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_front(D(false)); + q.push_front(D(true)); + q.push_front(D(false)); + + catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).ok(); + + assert_eq!(unsafe { DROPS }, 8); + assert!(q.is_empty()); +} + +#[test] fn test_drop() { static mut DROPS: i32 = 0; struct Elem; |
