Skip to content

Commit fa48435

Browse files
bors[bot]CAD97
andauthored
Merge #44
44: Patch some leak-on-panics r=CAD97 a=CAD97 Co-authored-by: CAD97 <[email protected]>
2 parents 5142ad5 + b26e32e commit fa48435

File tree

3 files changed

+204
-32
lines changed

3 files changed

+204
-32
lines changed

bors.toml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ delete-merged-branches = true
33

44
status = [
55
"Clippy",
6+
"Miri",
67
"MSRV Tests",
78
"Rustfmt",
89
"Tests",

crates/slice-dst/src/lib.rs

+100-32
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.
@@ -195,9 +198,18 @@ unsafe impl<S: ?Sized + SliceDst> AllocSliceDst<S> for Box<S> {
195198
where
196199
I: FnOnce(ptr::NonNull<S>),
197200
{
198-
let ptr = alloc_slice_dst(len);
199-
init(ptr);
200-
Box::from_raw(ptr.as_ptr())
201+
struct RawBox<S: ?Sized>(ptr::NonNull<S>);
202+
203+
impl<S: ?Sized> RawBox<S> {
204+
unsafe fn finalize(self) -> Box<S> {
205+
let this = ManuallyDrop::new(self);
206+
Box::from_raw(this.0.as_ptr())
207+
}
208+
}
209+
210+
let ptr = RawBox(alloc_slice_dst(len));
211+
init(ptr.0);
212+
ptr.finalize()
201213
}
202214
}
203215

@@ -221,7 +233,7 @@ unsafe impl<Header, Item> SliceDst for SliceWithHeader<Header, Item> {
221233
Self::layout(len).0
222234
}
223235

224-
fn retype(ptr: NonNull<[()]>) -> NonNull<Self> {
236+
fn retype(ptr: ptr::NonNull<[()]>) -> ptr::NonNull<Self> {
225237
unsafe { ptr::NonNull::new_unchecked(ptr.as_ptr() as *mut _) }
226238
}
227239
}
@@ -246,30 +258,91 @@ impl<Header, Item> SliceWithHeader<Header, Item> {
246258
I: IntoIterator<Item = Item>,
247259
I::IntoIter: ExactSizeIterator,
248260
{
249-
let mut items = items.into_iter();
261+
let items = items.into_iter();
250262
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);
263+
264+
struct InProgress<Header, Item> {
265+
raw: ptr::NonNull<SliceWithHeader<Header, Item>>,
266+
written: usize,
267+
layout: Layout,
268+
length_offset: usize,
269+
header_offset: usize,
270+
slice_offset: usize,
271+
}
272+
273+
impl<Header, Item> Drop for InProgress<Header, Item> {
274+
fn drop(&mut self) {
275+
unsafe {
276+
ptr::drop_in_place(slice_from_raw_parts(
277+
self.raw().add(self.slice_offset).cast::<Item>(),
278+
self.written,
279+
));
280+
}
281+
}
282+
}
283+
284+
impl<Header, Item> InProgress<Header, Item> {
285+
fn init(
286+
len: usize,
287+
header: Header,
288+
mut items: impl Iterator<Item = Item> + ExactSizeIterator,
289+
) -> impl FnOnce(ptr::NonNull<SliceWithHeader<Header, Item>>) {
290+
move |ptr| {
291+
let mut this = Self::new(len, ptr);
292+
293+
unsafe {
294+
for _ in 0..len {
295+
let item = items
296+
.next()
297+
.expect("ExactSizeIterator over-reported length");
298+
this.push(item);
299+
}
300+
301+
assert!(
302+
items.next().is_none(),
303+
"ExactSizeIterator under-reported length"
304+
);
305+
306+
this.finish(len, header)
307+
}
308+
}
309+
}
310+
311+
fn raw(&self) -> *mut u8 {
312+
self.raw.as_ptr().cast()
313+
}
314+
315+
fn new(len: usize, raw: ptr::NonNull<SliceWithHeader<Header, Item>>) -> Self {
316+
let (layout, [length_offset, header_offset, slice_offset]) =
317+
SliceWithHeader::<Header, Item>::layout(len);
318+
InProgress {
319+
raw,
320+
written: 0,
321+
layout,
322+
length_offset,
323+
header_offset,
324+
slice_offset,
265325
}
266-
assert!(
267-
items.next().is_none(),
268-
"ExactSizeIterator under-reported length"
269-
);
270-
assert_eq!(layout, Layout::for_value(ptr.as_ref()));
271-
})
326+
}
327+
328+
unsafe fn push(&mut self, item: Item) {
329+
self.raw()
330+
.add(self.slice_offset)
331+
.cast::<Item>()
332+
.add(self.written)
333+
.write(item);
334+
self.written += 1;
335+
}
336+
337+
unsafe fn finish(self, len: usize, header: Header) {
338+
let this = ManuallyDrop::new(self);
339+
ptr::write(this.raw().add(this.length_offset).cast(), len);
340+
ptr::write(this.raw().add(this.header_offset).cast(), header);
341+
debug_assert_eq!(this.layout, Layout::for_value(this.raw.as_ref()))
342+
}
272343
}
344+
345+
unsafe { A::new_slice_dst(len, InProgress::init(len, header, items)) }
273346
}
274347
}
275348

@@ -286,11 +359,6 @@ where
286359
#[cfg(feature = "erasable")]
287360
unsafe impl<Header, Item> Erasable for SliceWithHeader<Header, Item> {
288361
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-
294362
let len: usize = ptr::read(this.as_ptr().cast());
295363
let raw = ptr::NonNull::new_unchecked(slice_from_raw_parts(this.as_ptr().cast(), len));
296364
Self::retype(raw)

crates/slice-dst/tests/leak.rs

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use {
2+
slice_dst::*,
3+
std::{
4+
alloc::Layout,
5+
panic, ptr,
6+
rc::Rc,
7+
sync::{
8+
atomic::{AtomicUsize, Ordering::SeqCst},
9+
Arc,
10+
},
11+
},
12+
};
13+
14+
struct DropTracking<'a> {
15+
place: &'a AtomicUsize,
16+
}
17+
18+
impl<'a> DropTracking<'a> {
19+
fn new(place: &'a AtomicUsize) -> Self {
20+
place.fetch_add(1, SeqCst);
21+
DropTracking { place }
22+
}
23+
}
24+
25+
impl Drop for DropTracking<'_> {
26+
fn drop(&mut self) {
27+
self.place.fetch_sub(1, SeqCst);
28+
}
29+
}
30+
31+
#[test]
32+
#[cfg_attr(
33+
all(miri, target_os = "windows"),
34+
ignore = "miri does not support panicking on windows rust-lang/miri#1059"
35+
)]
36+
fn bad_exactsizeiterator() {
37+
struct Iter<'a> {
38+
counter: &'a AtomicUsize,
39+
len: usize,
40+
}
41+
42+
impl ExactSizeIterator for Iter<'_> {
43+
fn len(&self) -> usize {
44+
self.len
45+
}
46+
}
47+
48+
impl<'a> Iterator for Iter<'a> {
49+
type Item = DropTracking<'a>;
50+
51+
fn next(&mut self) -> Option<Self::Item> {
52+
match self.len {
53+
0 | 1 => None,
54+
_ => {
55+
self.len -= 1;
56+
Some(DropTracking::new(self.counter))
57+
}
58+
}
59+
}
60+
}
61+
62+
let mut counter = AtomicUsize::new(0);
63+
let _ = std::panic::catch_unwind(|| {
64+
let _: Box<_> = SliceWithHeader::new::<Box<_>, _>(
65+
DropTracking::new(&counter),
66+
Iter {
67+
counter: &counter,
68+
len: 5,
69+
},
70+
);
71+
});
72+
assert_eq!(*counter.get_mut(), 0);
73+
}
74+
75+
struct S(u8);
76+
77+
unsafe impl SliceDst for S {
78+
fn layout_for(_: usize) -> Layout {
79+
Layout::new::<S>()
80+
}
81+
82+
fn retype(ptr: ptr::NonNull<[()]>) -> ptr::NonNull<Self> {
83+
ptr.cast()
84+
}
85+
}
86+
87+
#[test]
88+
#[cfg_attr(
89+
all(miri, target_os = "windows"),
90+
ignore = "miri does not support panicking on windows rust-lang/miri#1059"
91+
)]
92+
fn panic_in_init() {
93+
// This relies on miri to catch leaks
94+
let _ = std::panic::catch_unwind(|| {
95+
let _: Box<S> = unsafe { AllocSliceDst::new_slice_dst(0, |_| panic!()) };
96+
});
97+
let _ = std::panic::catch_unwind(|| {
98+
let _: Arc<S> = unsafe { AllocSliceDst::new_slice_dst(0, |_| panic!()) };
99+
});
100+
let _ = std::panic::catch_unwind(|| {
101+
let _: Rc<S> = unsafe { AllocSliceDst::new_slice_dst(0, |_| panic!()) };
102+
});
103+
}

0 commit comments

Comments
 (0)