Skip to content

Commit a859ca5

Browse files
Fix binary_heap::DrainSorted drop leak on panics
1 parent c0e02ad commit a859ca5

File tree

2 files changed

+47
-2
lines changed

2 files changed

+47
-2
lines changed

src/liballoc/collections/binary_heap.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@
147147

148148
use core::fmt;
149149
use core::iter::{FromIterator, FusedIterator, TrustedLen};
150-
use core::mem::{size_of, swap, ManuallyDrop};
150+
use core::mem::{self, size_of, swap, ManuallyDrop};
151151
use core::ops::{Deref, DerefMut};
152152
use core::ptr;
153153

@@ -1239,7 +1239,19 @@ pub struct DrainSorted<'a, T: Ord> {
12391239
impl<'a, T: Ord> Drop for DrainSorted<'a, T> {
12401240
/// Removes heap elements in heap order.
12411241
fn drop(&mut self) {
1242-
while let Some(_) = self.inner.pop() {}
1242+
struct DropGuard<'r, 'a, T: Ord>(&'r mut DrainSorted<'a, T>);
1243+
1244+
impl<'r, 'a, T: Ord> Drop for DropGuard<'r, 'a, T> {
1245+
fn drop(&mut self) {
1246+
while let Some(_) = self.0.inner.pop() {}
1247+
}
1248+
}
1249+
1250+
while let Some(item) = self.inner.pop() {
1251+
let guard = DropGuard(self);
1252+
drop(item);
1253+
mem::forget(guard);
1254+
}
12431255
}
12441256
}
12451257

src/liballoc/tests/binary_heap.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use std::collections::binary_heap::{Drain, PeekMut};
22
use std::collections::BinaryHeap;
33
use std::iter::TrustedLen;
4+
use std::panic::{catch_unwind, AssertUnwindSafe};
5+
use std::sync::atomic::{AtomicU32, Ordering};
46

57
#[test]
68
fn test_iterator() {
@@ -275,6 +277,37 @@ fn test_drain_sorted() {
275277
assert!(q.is_empty());
276278
}
277279

280+
#[test]
281+
fn test_drain_sorted_leak() {
282+
static DROPS: AtomicU32 = AtomicU32::new(0);
283+
284+
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
285+
struct D(u32, bool);
286+
287+
impl Drop for D {
288+
fn drop(&mut self) {
289+
DROPS.fetch_add(1, Ordering::SeqCst);
290+
291+
if self.1 {
292+
panic!("panic in `drop`");
293+
}
294+
}
295+
}
296+
297+
let mut q = BinaryHeap::from(vec![
298+
D(0, false),
299+
D(1, false),
300+
D(2, false),
301+
D(3, true),
302+
D(4, false),
303+
D(5, false),
304+
]);
305+
306+
catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).ok();
307+
308+
assert_eq!(DROPS.load(Ordering::SeqCst), 6);
309+
}
310+
278311
#[test]
279312
fn test_extend_ref() {
280313
let mut a = BinaryHeap::new();

0 commit comments

Comments
 (0)