Skip to content

Commit 9111999

Browse files
committed
Clean up leak on bad ExactSizeIter impl
1 parent d52989a commit 9111999

File tree

1 file changed

+88
-29
lines changed

1 file changed

+88
-29
lines changed

crates/slice-dst/src/lib.rs

+88-29
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,18 @@
8888

8989
extern crate alloc;
9090

91-
use core::ptr::NonNull;
91+
#[cfg(has_ptr_slice_from_raw_parts)]
92+
use core::ptr::slice_from_raw_parts_mut as slice_from_raw_parts;
93+
#[cfg(not(has_ptr_slice_from_raw_parts))]
94+
use core::slice::from_raw_parts_mut as slice_from_raw_parts;
9295
#[cfg(feature = "erasable")]
9396
use erasable::{Erasable, ErasedPtr};
9497
use {
9598
alloc::{
9699
alloc::{alloc, handle_alloc_error},
97100
boxed::Box,
98101
},
99-
core::{alloc::Layout, ptr, slice},
102+
core::{alloc::Layout, mem::ManuallyDrop, ptr, slice},
100103
};
101104

102105
/// A custom slice-based dynamically sized type.
@@ -221,7 +224,7 @@ unsafe impl<Header, Item> SliceDst for SliceWithHeader<Header, Item> {
221224
Self::layout(len).0
222225
}
223226

224-
fn retype(ptr: NonNull<[()]>) -> NonNull<Self> {
227+
fn retype(ptr: ptr::NonNull<[()]>) -> ptr::NonNull<Self> {
225228
unsafe { ptr::NonNull::new_unchecked(ptr.as_ptr() as *mut _) }
226229
}
227230
}
@@ -246,30 +249,91 @@ impl<Header, Item> SliceWithHeader<Header, Item> {
246249
I: IntoIterator<Item = Item>,
247250
I::IntoIter: ExactSizeIterator,
248251
{
249-
let mut items = items.into_iter();
252+
let items = items.into_iter();
250253
let len = items.len();
251-
let (layout, [length_offset, header_offset, slice_offset]) = Self::layout(len);
252-
253-
unsafe {
254-
A::new_slice_dst(len, |ptr| {
255-
let raw = ptr.as_ptr().cast::<u8>();
256-
ptr::write(raw.add(length_offset).cast(), len);
257-
ptr::write(raw.add(header_offset).cast(), header);
258-
let mut slice_ptr = raw.add(slice_offset).cast::<Item>();
259-
for _ in 0..len {
260-
let item = items
261-
.next()
262-
.expect("ExactSizeIterator over-reported length");
263-
ptr::write(slice_ptr, item);
264-
slice_ptr = slice_ptr.offset(1);
254+
255+
struct InProgress<Header, Item> {
256+
raw: ptr::NonNull<SliceWithHeader<Header, Item>>,
257+
written: usize,
258+
layout: Layout,
259+
length_offset: usize,
260+
header_offset: usize,
261+
slice_offset: usize,
262+
}
263+
264+
impl<Header, Item> Drop for InProgress<Header, Item> {
265+
fn drop(&mut self) {
266+
unsafe {
267+
ptr::drop_in_place(slice_from_raw_parts(
268+
self.raw().add(self.slice_offset).cast::<Item>(),
269+
self.written,
270+
));
265271
}
266-
assert!(
267-
items.next().is_none(),
268-
"ExactSizeIterator under-reported length"
269-
);
270-
assert_eq!(layout, Layout::for_value(ptr.as_ref()));
271-
})
272+
}
272273
}
274+
275+
impl<Header, Item> InProgress<Header, Item> {
276+
fn init(
277+
len: usize,
278+
header: Header,
279+
mut items: impl Iterator<Item = Item> + ExactSizeIterator,
280+
) -> impl FnOnce(ptr::NonNull<SliceWithHeader<Header, Item>>) {
281+
move |ptr| {
282+
let mut this = Self::new(len, ptr);
283+
284+
unsafe {
285+
for _ in 0..len {
286+
let item = items
287+
.next()
288+
.expect("ExactSizeIterator over-reported length");
289+
this.push(item);
290+
}
291+
292+
assert!(
293+
items.next().is_none(),
294+
"ExactSizeIterator under-reported length"
295+
);
296+
297+
this.finish(len, header)
298+
}
299+
}
300+
}
301+
302+
fn raw(&self) -> *mut u8 {
303+
self.raw.as_ptr().cast()
304+
}
305+
306+
fn new(len: usize, raw: ptr::NonNull<SliceWithHeader<Header, Item>>) -> Self {
307+
let (layout, [length_offset, header_offset, slice_offset]) =
308+
SliceWithHeader::<Header, Item>::layout(len);
309+
InProgress {
310+
raw,
311+
written: 0,
312+
layout,
313+
length_offset,
314+
header_offset,
315+
slice_offset,
316+
}
317+
}
318+
319+
unsafe fn push(&mut self, item: Item) {
320+
self.raw()
321+
.add(self.slice_offset)
322+
.cast::<Item>()
323+
.add(self.written)
324+
.write(item);
325+
self.written += 1;
326+
}
327+
328+
unsafe fn finish(self, len: usize, header: Header) {
329+
let this = ManuallyDrop::new(self);
330+
ptr::write(this.raw().add(this.length_offset).cast(), len);
331+
ptr::write(this.raw().add(this.header_offset).cast(), header);
332+
debug_assert_eq!(this.layout, Layout::for_value(this.raw.as_ref()))
333+
}
334+
}
335+
336+
unsafe { A::new_slice_dst(len, InProgress::init(len, header, items)) }
273337
}
274338
}
275339

@@ -286,11 +350,6 @@ where
286350
#[cfg(feature = "erasable")]
287351
unsafe impl<Header, Item> Erasable for SliceWithHeader<Header, Item> {
288352
unsafe fn unerase(this: ErasedPtr) -> ptr::NonNull<Self> {
289-
#[cfg(not(has_ptr_slice_from_raw_parts))]
290-
let slice_from_raw_parts = slice::from_raw_parts_mut::<()>;
291-
#[cfg(has_ptr_slice_from_raw_parts)]
292-
let slice_from_raw_parts = ptr::slice_from_raw_parts_mut::<()>;
293-
294353
let len: usize = ptr::read(this.as_ptr().cast());
295354
let raw = ptr::NonNull::new_unchecked(slice_from_raw_parts(this.as_ptr().cast(), len));
296355
Self::retype(raw)

0 commit comments

Comments
 (0)