Skip to content

Commit ec487bf

Browse files
committed
Auto merge of #82760 - WaffleLapkin:unleak_extend_from_within, r=kennytm
Fix leak in Vec::extend_from_within Fixes #82533
2 parents f42888c + 1f031d9 commit ec487bf

File tree

2 files changed

+70
-15
lines changed

2 files changed

+70
-15
lines changed

library/alloc/src/vec/mod.rs

+28-15
Original file line numberDiff line numberDiff line change
@@ -1942,6 +1942,18 @@ impl<T, A: Allocator> Vec<T, A> {
19421942
#[unstable(feature = "vec_split_at_spare", issue = "81944")]
19431943
#[inline]
19441944
pub fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit<T>]) {
1945+
// SAFETY:
1946+
// - len is ignored and so never changed
1947+
let (init, spare, _) = unsafe { self.split_at_spare_mut_with_len() };
1948+
(init, spare)
1949+
}
1950+
1951+
/// Safety: changing returned .2 (&mut usize) is considered the same as calling `.set_len(_)`.
1952+
///
1953+
/// This method is used to have unique access to all vec parts at once in `extend_from_within`.
1954+
unsafe fn split_at_spare_mut_with_len(
1955+
&mut self,
1956+
) -> (&mut [T], &mut [MaybeUninit<T>], &mut usize) {
19451957
let Range { start: ptr, end: spare_ptr } = self.as_mut_ptr_range();
19461958
let spare_ptr = spare_ptr.cast::<MaybeUninit<T>>();
19471959
let spare_len = self.buf.capacity() - self.len;
@@ -1953,7 +1965,7 @@ impl<T, A: Allocator> Vec<T, A> {
19531965
let initialized = slice::from_raw_parts_mut(ptr, self.len);
19541966
let spare = slice::from_raw_parts_mut(spare_ptr, spare_len);
19551967

1956-
(initialized, spare)
1968+
(initialized, spare, &mut self.len)
19571969
}
19581970
}
19591971
}
@@ -2165,22 +2177,23 @@ trait ExtendFromWithinSpec {
21652177

21662178
impl<T: Clone, A: Allocator> ExtendFromWithinSpec for Vec<T, A> {
21672179
default unsafe fn spec_extend_from_within(&mut self, src: Range<usize>) {
2168-
let initialized = {
2169-
let (this, spare) = self.split_at_spare_mut();
2170-
2171-
// SAFETY:
2172-
// - caller guaratees that src is a valid index
2173-
let to_clone = unsafe { this.get_unchecked(src) };
2174-
2175-
to_clone.iter().cloned().zip(spare.iter_mut()).map(|(e, s)| s.write(e)).count()
2176-
};
2180+
// SAFETY:
2181+
// - len is increased only after initializing elements
2182+
let (this, spare, len) = unsafe { self.split_at_spare_mut_with_len() };
21772183

21782184
// SAFETY:
2179-
// - elements were just initialized
2180-
unsafe {
2181-
let new_len = self.len() + initialized;
2182-
self.set_len(new_len);
2183-
}
2185+
// - caller guaratees that src is a valid index
2186+
let to_clone = unsafe { this.get_unchecked(src) };
2187+
2188+
to_clone
2189+
.iter()
2190+
.cloned()
2191+
.zip(spare.iter_mut())
2192+
.map(|(src, dst)| dst.write(src))
2193+
// Note:
2194+
// - Element was just initialized with `MaybeUninit::write`, so it's ok to increace len
2195+
// - len is increased after each element to prevent leaks (see issue #82533)
2196+
.for_each(|_| *len += 1);
21842197
}
21852198
}
21862199

library/alloc/tests/vec.rs

+42
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::mem::{size_of, swap};
77
use std::ops::Bound::*;
88
use std::panic::{catch_unwind, AssertUnwindSafe};
99
use std::rc::Rc;
10+
use std::sync::atomic::{AtomicU32, Ordering};
1011
use std::vec::{Drain, IntoIter};
1112

1213
struct DropCounter<'a> {
@@ -2100,3 +2101,44 @@ fn test_extend_from_within() {
21002101

21012102
assert_eq!(v, ["a", "b", "c", "b", "c", "a", "b"]);
21022103
}
2104+
2105+
// Regression test for issue #82533
2106+
#[test]
2107+
fn test_extend_from_within_panicing_clone() {
2108+
struct Panic<'dc> {
2109+
drop_count: &'dc AtomicU32,
2110+
aaaaa: bool,
2111+
}
2112+
2113+
impl Clone for Panic<'_> {
2114+
fn clone(&self) -> Self {
2115+
if self.aaaaa {
2116+
panic!("panic! at the clone");
2117+
}
2118+
2119+
Self { ..*self }
2120+
}
2121+
}
2122+
2123+
impl Drop for Panic<'_> {
2124+
fn drop(&mut self) {
2125+
self.drop_count.fetch_add(1, Ordering::SeqCst);
2126+
}
2127+
}
2128+
2129+
let count = core::sync::atomic::AtomicU32::new(0);
2130+
let mut vec = vec![
2131+
Panic { drop_count: &count, aaaaa: false },
2132+
Panic { drop_count: &count, aaaaa: true },
2133+
Panic { drop_count: &count, aaaaa: false },
2134+
];
2135+
2136+
// This should clone&append one Panic{..} at the end, and then panic while
2137+
// cloning second Panic{..}. This means that `Panic::drop` should be called
2138+
// 4 times (3 for items already in vector, 1 for just appended).
2139+
//
2140+
// Previously just appended item was leaked, making drop_count = 3, instead of 4.
2141+
std::panic::catch_unwind(move || vec.extend_from_within(..)).unwrap_err();
2142+
2143+
assert_eq!(count.load(Ordering::SeqCst), 4);
2144+
}

0 commit comments

Comments
 (0)