Skip to content

Commit fa61678

Browse files
committed
Fix leaking in inplace collection when destructor panics
1 parent 36e530c commit fa61678

File tree

3 files changed

+22
-3
lines changed

3 files changed

+22
-3
lines changed

library/alloc/src/vec/in_place_collect.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccessNoCoerce};
138138
use core::mem::{self, ManuallyDrop};
139139
use core::ptr::{self};
140140

141-
use super::{InPlaceDrop, SpecFromIter, SpecFromIterNested, Vec};
141+
use super::{InPlaceDrop, InPlaceDstBufDrop, SpecFromIter, SpecFromIterNested, Vec};
142142

143143
/// Specialization marker for collecting an iterator pipeline into a Vec while reusing the
144144
/// source allocation, i.e. executing the pipeline in place.
@@ -193,12 +193,16 @@ where
193193

194194
// Drop any remaining values at the tail of the source but prevent drop of the allocation
195195
// itself once IntoIter goes out of scope.
196-
// If the drop panics then we also leak any elements collected into dst_buf.
196+
// If the drop panics then we also try to drop the destination buffer and its elements.
197+
// This is safe because `forget_allocation_drop_remaining` forgets the allocation *before*
198+
// trying to drop the remaining elements.
197199
//
198200
// Note: This access to the source wouldn't be allowed by the TrustedRandomIteratorNoCoerce
199201
// contract (used by SpecInPlaceCollect below). But see the "O(1) collect" section in the
200202
// module documenttation why this is ok anyway.
203+
let dst_guard = InPlaceDstBufDrop { ptr: dst_buf, len, cap };
201204
src.forget_allocation_drop_remaining();
205+
mem::forget(dst_guard);
202206

203207
let vec = unsafe { Vec::from_raw_parts(dst_buf, len, cap) };
204208

library/alloc/src/vec/in_place_drop.rs

+15
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,18 @@ impl<T> Drop for InPlaceDrop<T> {
2222
}
2323
}
2424
}
25+
26+
// A helper struct for in-place collection that drops the destination allocation and elements,
27+
// to avoid leaking them if some other destructor panics.
28+
pub(super) struct InPlaceDstBufDrop<T> {
29+
pub(super) ptr: *mut T,
30+
pub(super) len: usize,
31+
pub(super) cap: usize,
32+
}
33+
34+
impl<T> Drop for InPlaceDstBufDrop<T> {
35+
#[inline]
36+
fn drop(&mut self) {
37+
unsafe { super::Vec::from_raw_parts(self.ptr, self.len, self.cap) };
38+
}
39+
}

library/alloc/src/vec/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ use self::set_len_on_drop::SetLenOnDrop;
125125
mod set_len_on_drop;
126126

127127
#[cfg(not(no_global_oom_handling))]
128-
use self::in_place_drop::InPlaceDrop;
128+
use self::in_place_drop::{InPlaceDrop, InPlaceDstBufDrop};
129129

130130
#[cfg(not(no_global_oom_handling))]
131131
mod in_place_drop;

0 commit comments

Comments
 (0)