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

Commit b04ca13

Browse files
Fix leak in VecDeque::drain when drop panics
1 parent dc49245 commit b04ca13

File tree

2 files changed

+86
-34
lines changed

2 files changed

+86
-34
lines changed

src/liballoc/collections/vec_deque.rs

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2575,47 +2575,61 @@ unsafe impl<T: Send> Send for Drain<'_, T> {}
25752575
#[stable(feature = "drain", since = "1.6.0")]
25762576
impl<T> Drop for Drain<'_, T> {
25772577
fn drop(&mut self) {
2578-
self.for_each(drop);
2578+
struct DropGuard<'r, 'a, T>(&'r mut Drain<'a, T>);
25792579

2580-
let source_deque = unsafe { self.deque.as_mut() };
2580+
impl<'r, 'a, T> Drop for DropGuard<'r, 'a, T> {
2581+
fn drop(&mut self) {
2582+
self.0.for_each(drop);
25812583

2582-
// T = source_deque_tail; H = source_deque_head; t = drain_tail; h = drain_head
2583-
//
2584-
// T t h H
2585-
// [. . . o o x x o o . . .]
2586-
//
2587-
let orig_tail = source_deque.tail;
2588-
let drain_tail = source_deque.head;
2589-
let drain_head = self.after_tail;
2590-
let orig_head = self.after_head;
2584+
let source_deque = unsafe { self.0.deque.as_mut() };
25912585

2592-
let tail_len = count(orig_tail, drain_tail, source_deque.cap());
2593-
let head_len = count(drain_head, orig_head, source_deque.cap());
2586+
// T = source_deque_tail; H = source_deque_head; t = drain_tail; h = drain_head
2587+
//
2588+
// T t h H
2589+
// [. . . o o x x o o . . .]
2590+
//
2591+
let orig_tail = source_deque.tail;
2592+
let drain_tail = source_deque.head;
2593+
let drain_head = self.0.after_tail;
2594+
let orig_head = self.0.after_head;
25942595

2595-
// Restore the original head value
2596-
source_deque.head = orig_head;
2596+
let tail_len = count(orig_tail, drain_tail, source_deque.cap());
2597+
let head_len = count(drain_head, orig_head, source_deque.cap());
25972598

2598-
match (tail_len, head_len) {
2599-
(0, 0) => {
2600-
source_deque.head = 0;
2601-
source_deque.tail = 0;
2602-
}
2603-
(0, _) => {
2604-
source_deque.tail = drain_head;
2605-
}
2606-
(_, 0) => {
2607-
source_deque.head = drain_tail;
2608-
}
2609-
_ => unsafe {
2610-
if tail_len <= head_len {
2611-
source_deque.tail = source_deque.wrap_sub(drain_head, tail_len);
2612-
source_deque.wrap_copy(source_deque.tail, orig_tail, tail_len);
2613-
} else {
2614-
source_deque.head = source_deque.wrap_add(drain_tail, head_len);
2615-
source_deque.wrap_copy(drain_tail, drain_head, head_len);
2599+
// Restore the original head value
2600+
source_deque.head = orig_head;
2601+
2602+
match (tail_len, head_len) {
2603+
(0, 0) => {
2604+
source_deque.head = 0;
2605+
source_deque.tail = 0;
2606+
}
2607+
(0, _) => {
2608+
source_deque.tail = drain_head;
2609+
}
2610+
(_, 0) => {
2611+
source_deque.head = drain_tail;
2612+
}
2613+
_ => unsafe {
2614+
if tail_len <= head_len {
2615+
source_deque.tail = source_deque.wrap_sub(drain_head, tail_len);
2616+
source_deque.wrap_copy(source_deque.tail, orig_tail, tail_len);
2617+
} else {
2618+
source_deque.head = source_deque.wrap_add(drain_tail, head_len);
2619+
source_deque.wrap_copy(drain_tail, drain_head, head_len);
2620+
}
2621+
},
26162622
}
2617-
},
2623+
}
26182624
}
2625+
2626+
while let Some(item) = self.next() {
2627+
let guard = DropGuard(self);
2628+
drop(item);
2629+
mem::forget(guard);
2630+
}
2631+
2632+
DropGuard(self);
26192633
}
26202634
}
26212635

src/liballoc/tests/vec_deque.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,3 +1606,41 @@ fn truncate_leak() {
16061606

16071607
assert_eq!(unsafe { DROPS }, 7);
16081608
}
1609+
1610+
#[test]
1611+
fn test_drain_leak() {
1612+
static mut DROPS: i32 = 0;
1613+
1614+
#[derive(Debug, PartialEq)]
1615+
struct D(u32, bool);
1616+
1617+
impl Drop for D {
1618+
fn drop(&mut self) {
1619+
unsafe {
1620+
DROPS += 1;
1621+
}
1622+
1623+
if self.1 {
1624+
panic!("panic in `drop`");
1625+
}
1626+
}
1627+
}
1628+
1629+
let mut v = VecDeque::new();
1630+
v.push_back(D(4, false));
1631+
v.push_back(D(5, false));
1632+
v.push_back(D(6, false));
1633+
v.push_front(D(3, false));
1634+
v.push_front(D(2, true));
1635+
v.push_front(D(1, false));
1636+
v.push_front(D(0, false));
1637+
1638+
catch_unwind(AssertUnwindSafe(|| {
1639+
v.drain(1..=4);
1640+
})).ok();
1641+
1642+
assert_eq!(unsafe { DROPS }, 4);
1643+
assert_eq!(v.len(), 3);
1644+
drop(v);
1645+
assert_eq!(unsafe { DROPS }, 7);
1646+
}

0 commit comments

Comments
 (0)