Skip to content

Commit 50ee7da

Browse files
committed
Fix leak on panic in insert_many.
Fixes #208.
1 parent 6c76c41 commit 50ee7da

File tree

1 file changed

+34
-14
lines changed

1 file changed

+34
-14
lines changed

lib.rs

+34-14
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ use core::hint::unreachable_unchecked;
8181
use core::iter::{repeat, FromIterator, FusedIterator, IntoIterator};
8282
use core::mem;
8383
use core::mem::MaybeUninit;
84-
use core::ops::{self, RangeBounds};
84+
use core::ops::{self, Range, RangeBounds};
8585
use core::ptr::{self, NonNull};
8686
use core::slice::{self, SliceIndex};
8787

@@ -865,8 +865,6 @@ impl<A: Array> SmallVec<A> {
865865

866866
/// Insert multiple elements at position `index`, shifting all following elements toward the
867867
/// back.
868-
///
869-
/// Note: when the iterator panics, this can leak memory.
870868
pub fn insert_many<I: IntoIterator<Item = A::Item>>(&mut self, index: usize, iterable: I) {
871869
let iter = iterable.into_iter();
872870
if index == self.len() {
@@ -887,7 +885,12 @@ impl<A: Array> SmallVec<A> {
887885
ptr::copy(ptr, ptr.add(lower_size_bound), old_len - index);
888886

889887
// In case the iterator panics, don't double-drop the items we just copied above.
890-
self.set_len(index);
888+
self.set_len(0);
889+
let mut guard = DropOnPanic {
890+
ptr: self.as_mut_ptr(),
891+
skip: index..lower_size_bound,
892+
len: old_len + lower_size_bound,
893+
};
891894

892895
let mut num_added = 0;
893896
for element in iter {
@@ -898,10 +901,17 @@ impl<A: Array> SmallVec<A> {
898901
ptr = self.as_mut_ptr().add(index);
899902
cur = ptr.add(num_added);
900903
ptr::copy(cur, cur.add(1), old_len - index);
904+
905+
guard.ptr = self.as_mut_ptr();
906+
guard.len += 1;
907+
guard.skip.end += 1;
901908
}
902909
ptr::write(cur, element);
910+
guard.skip.start += 1;
903911
num_added += 1;
904912
}
913+
mem::forget(guard);
914+
905915
if num_added < lower_size_bound {
906916
// Iterator provided fewer elements than the hint
907917
ptr::copy(
@@ -913,6 +923,24 @@ impl<A: Array> SmallVec<A> {
913923

914924
self.set_len(old_len + num_added);
915925
}
926+
927+
struct DropOnPanic<T> {
928+
ptr: *mut T,
929+
len: usize,
930+
skip: Range<usize>,
931+
}
932+
933+
impl<T> Drop for DropOnPanic<T> {
934+
fn drop(&mut self) {
935+
for i in 0..self.len {
936+
if !self.skip.contains(&i) {
937+
unsafe {
938+
ptr::drop_in_place(self.ptr.add(i));
939+
}
940+
}
941+
}
942+
}
943+
}
916944
}
917945

918946
/// Convert a SmallVec to a Vec, without reallocating if the SmallVec has already spilled onto
@@ -2094,27 +2122,19 @@ mod tests {
20942122
}
20952123
}
20962124

2097-
// These boxes are leaked on purpose by panicking `insert_many`,
2098-
// so we clean them up manually to appease Miri's leak checker.
2099-
let mut box1 = Box::new(false);
2100-
let mut box2 = Box::new(false);
2101-
21022125
let mut vec: SmallVec<[PanicOnDoubleDrop; 0]> = vec![
21032126
PanicOnDoubleDrop {
2104-
dropped: unsafe { Box::from_raw(&mut *box1) },
2127+
dropped: Box::new(false),
21052128
},
21062129
PanicOnDoubleDrop {
2107-
dropped: unsafe { Box::from_raw(&mut *box2) },
2130+
dropped: Box::new(false),
21082131
},
21092132
]
21102133
.into();
21112134
let result = ::std::panic::catch_unwind(move || {
21122135
vec.insert_many(0, BadIter);
21132136
});
21142137
assert!(result.is_err());
2115-
2116-
drop(box1);
2117-
drop(box2);
21182138
}
21192139

21202140
#[test]

0 commit comments

Comments
 (0)