Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 5d04790

Browse files
Avoid leak in vec::Drain when item drop panics
1 parent 3e5eb26 commit 5d04790

File tree

2 files changed

+69
-13
lines changed

2 files changed

+69
-13
lines changed

src/liballoc/tests/vec.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::borrow::Cow;
22
use std::collections::TryReserveError::*;
33
use std::mem::size_of;
4+
use std::panic::{catch_unwind, AssertUnwindSafe};
45
use std::vec::{Drain, IntoIter};
56
use std::{isize, usize};
67

@@ -585,6 +586,44 @@ fn test_drain_inclusive_out_of_bounds() {
585586
v.drain(5..=5);
586587
}
587588

589+
#[test]
590+
fn test_drain_leak() {
591+
static mut DROPS: i32 = 0;
592+
593+
#[derive(Debug, PartialEq)]
594+
struct D(u32, bool);
595+
596+
impl Drop for D {
597+
fn drop(&mut self) {
598+
unsafe {
599+
DROPS += 1;
600+
}
601+
602+
if self.1 {
603+
panic!("panic in `drop`");
604+
}
605+
}
606+
}
607+
608+
let mut v = vec![
609+
D(0, false),
610+
D(1, false),
611+
D(2, false),
612+
D(3, false),
613+
D(4, true),
614+
D(5, false),
615+
D(6, false),
616+
];
617+
618+
catch_unwind(AssertUnwindSafe(|| {
619+
v.drain(2..=5);
620+
}))
621+
.ok();
622+
623+
assert_eq!(unsafe { DROPS }, 4);
624+
assert_eq!(v, vec![D(0, false), D(1, false), D(6, false),]);
625+
}
626+
588627
#[test]
589628
fn test_splice() {
590629
let mut v = vec![1, 2, 3, 4, 5];

src/liballoc/vec.rs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2700,23 +2700,40 @@ impl<T> DoubleEndedIterator for Drain<'_, T> {
27002700
#[stable(feature = "drain", since = "1.6.0")]
27012701
impl<T> Drop for Drain<'_, T> {
27022702
fn drop(&mut self) {
2703-
// exhaust self first
2704-
self.for_each(drop);
2703+
/// Continues dropping the remaining elements when a destructor unwinds.
2704+
struct DropGuard<'r, 'a, T>(&'r mut Drain<'a, T>);
27052705

2706-
if self.tail_len > 0 {
2707-
unsafe {
2708-
let source_vec = self.vec.as_mut();
2709-
// memmove back untouched tail, update to new length
2710-
let start = source_vec.len();
2711-
let tail = self.tail_start;
2712-
if tail != start {
2713-
let src = source_vec.as_ptr().add(tail);
2714-
let dst = source_vec.as_mut_ptr().add(start);
2715-
ptr::copy(src, dst, self.tail_len);
2706+
impl<'r, 'a, T> Drop for DropGuard<'r, 'a, T> {
2707+
fn drop(&mut self) {
2708+
// Continue the same loop we do below. This only runs when a destructor has
2709+
// panicked. If another one panics this will abort.
2710+
self.0.for_each(drop);
2711+
2712+
if self.0.tail_len > 0 {
2713+
unsafe {
2714+
let source_vec = self.0.vec.as_mut();
2715+
// memmove back untouched tail, update to new length
2716+
let start = source_vec.len();
2717+
let tail = self.0.tail_start;
2718+
if tail != start {
2719+
let src = source_vec.as_ptr().add(tail);
2720+
let dst = source_vec.as_mut_ptr().add(start);
2721+
ptr::copy(src, dst, self.0.tail_len);
2722+
}
2723+
source_vec.set_len(start + self.0.tail_len);
2724+
}
27162725
}
2717-
source_vec.set_len(start + self.tail_len);
27182726
}
27192727
}
2728+
2729+
// exhaust self first
2730+
while let Some(item) = self.next() {
2731+
let guard = DropGuard(self);
2732+
drop(item);
2733+
mem::forget(guard);
2734+
}
2735+
2736+
DropGuard(self);
27202737
}
27212738
}
27222739

0 commit comments

Comments
 (0)