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

Commit 3e5eb26

Browse files
Fix VecDeque::truncate leak on drop panic
1 parent a859ca5 commit 3e5eb26

File tree

2 files changed

+50
-2
lines changed

2 files changed

+50
-2
lines changed

src/liballoc/collections/vec_deque.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,18 @@ impl<T> VecDeque<T> {
866866
/// ```
867867
#[stable(feature = "deque_extras", since = "1.16.0")]
868868
pub fn truncate(&mut self, len: usize) {
869+
/// Runs the destructor for all items in the slice when it gets dropped (normally or
870+
/// during unwinding).
871+
struct Dropper<'a, T>(&'a mut [T]);
872+
873+
impl<'a, T> Drop for Dropper<'a, T> {
874+
fn drop(&mut self) {
875+
unsafe {
876+
ptr::drop_in_place(self.0);
877+
}
878+
}
879+
}
880+
869881
// Safe because:
870882
//
871883
// * Any slice passed to `drop_in_place` is valid; the second case has
@@ -888,8 +900,11 @@ impl<T> VecDeque<T> {
888900
let drop_back = back as *mut _;
889901
let drop_front = front.get_unchecked_mut(len..) as *mut _;
890902
self.head = self.wrap_sub(self.head, num_dropped);
903+
904+
// Make sure the second half is dropped even when a destructor
905+
// in the first one panics.
906+
let _back_dropper = Dropper(&mut *drop_back);
891907
ptr::drop_in_place(drop_front);
892-
ptr::drop_in_place(drop_back);
893908
}
894909
}
895910
}

src/liballoc/tests/vec_deque.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::collections::TryReserveError::*;
22
use std::collections::{vec_deque::Drain, VecDeque};
33
use std::fmt::Debug;
44
use std::mem::size_of;
5-
use std::panic::catch_unwind;
5+
use std::panic::{AssertUnwindSafe, catch_unwind};
66
use std::{isize, usize};
77

88
use crate::hash;
@@ -1573,3 +1573,36 @@ fn test_try_rfold_moves_iter() {
15731573
assert_eq!(iter.try_rfold(0_i8, |acc, &x| acc.checked_add(x)), None);
15741574
assert_eq!(iter.next_back(), Some(&70));
15751575
}
1576+
1577+
#[test]
1578+
fn truncate_leak() {
1579+
static mut DROPS: i32 = 0;
1580+
1581+
struct D(bool);
1582+
1583+
impl Drop for D {
1584+
fn drop(&mut self) {
1585+
unsafe {
1586+
DROPS += 1;
1587+
}
1588+
1589+
if self.0 {
1590+
panic!("panic in `drop`");
1591+
}
1592+
}
1593+
}
1594+
1595+
let mut q = VecDeque::new();
1596+
q.push_back(D(false));
1597+
q.push_back(D(false));
1598+
q.push_back(D(false));
1599+
q.push_back(D(false));
1600+
q.push_back(D(false));
1601+
q.push_front(D(true));
1602+
q.push_front(D(false));
1603+
q.push_front(D(false));
1604+
1605+
catch_unwind(AssertUnwindSafe(|| q.truncate(1))).ok();
1606+
1607+
assert_eq!(unsafe { DROPS }, 7);
1608+
}

0 commit comments

Comments
 (0)