diff options
| author | Jonas Schievink <jonasschievink@gmail.com> | 2019-12-13 17:54:26 +0100 |
|---|---|---|
| committer | Jonas Schievink <jonasschievink@gmail.com> | 2020-01-19 20:23:41 +0100 |
| commit | 5d04790dd2e73f3faf08d528e3675e131585ec01 (patch) | |
| tree | 5aa4271596ae881762bf2d636979512a622a9b9a /src/liballoc/vec.rs | |
| parent | 3e5eb2634cbb356b626e028a4be1305d4a44a023 (diff) | |
| download | rust-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.rs | 43 |
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); } } |
