Skip to content

Commit 64c7467

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 + b6e4ed4 commit 64c7467

File tree

3 files changed

+218
-33
lines changed

3 files changed

+218
-33
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

+114-33
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::{
96-
alloc::{alloc, handle_alloc_error},
99+
alloc::{alloc, dealloc, 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,31 @@ 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 + SliceDst>(ptr::NonNull<S>, Layout);
202+
203+
impl<S: ?Sized + SliceDst> RawBox<S> {
204+
unsafe fn new(len: usize) -> Self {
205+
let layout = S::layout_for(len);
206+
RawBox(alloc_slice_dst(len), layout)
207+
}
208+
209+
unsafe fn finalize(self) -> Box<S> {
210+
let this = ManuallyDrop::new(self);
211+
Box::from_raw(this.0.as_ptr())
212+
}
213+
}
214+
215+
impl<S: ?Sized + SliceDst> Drop for RawBox<S> {
216+
fn drop(&mut self) {
217+
unsafe {
218+
dealloc(self.0.as_ptr().cast(), self.1);
219+
}
220+
}
221+
}
222+
223+
let ptr = RawBox::new(len);
224+
init(ptr.0);
225+
ptr.finalize()
201226
}
202227
}
203228

@@ -221,7 +246,7 @@ unsafe impl<Header, Item> SliceDst for SliceWithHeader<Header, Item> {
221246
Self::layout(len).0
222247
}
223248

224-
fn retype(ptr: NonNull<[()]>) -> NonNull<Self> {
249+
fn retype(ptr: ptr::NonNull<[()]>) -> ptr::NonNull<Self> {
225250
unsafe { ptr::NonNull::new_unchecked(ptr.as_ptr() as *mut _) }
226251
}
227252
}
@@ -246,30 +271,91 @@ impl<Header, Item> SliceWithHeader<Header, Item> {
246271
I: IntoIterator<Item = Item>,
247272
I::IntoIter: ExactSizeIterator,
248273
{
249-
let mut items = items.into_iter();
274+
let items = items.into_iter();
250275
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);
276+
277+
struct InProgress<Header, Item> {
278+
raw: ptr::NonNull<SliceWithHeader<Header, Item>>,
279+
written: usize,
280+
layout: Layout,
281+
length_offset: usize,
282+
header_offset: usize,
283+
slice_offset: usize,
284+
}
285+
286+
impl<Header, Item> Drop for InProgress<Header, Item> {
287+
fn drop(&mut self) {
288+
unsafe {
289+
ptr::drop_in_place(slice_from_raw_parts(
290+
self.raw().add(self.slice_offset).cast::<Item>(),
291+
self.written,
292+
));
293+
}
294+
}
295+
}
296+
297+
impl<Header, Item> InProgress<Header, Item> {
298+
fn init(
299+
len: usize,
300+
header: Header,
301+
mut items: impl Iterator<Item = Item> + ExactSizeIterator,
302+
) -> impl FnOnce(ptr::NonNull<SliceWithHeader<Header, Item>>) {
303+
move |ptr| {
304+
let mut this = Self::new(len, ptr);
305+
306+
unsafe {
307+
for _ in 0..len {
308+
let item = items
309+
.next()
310+
.expect("ExactSizeIterator over-reported length");
311+
this.push(item);
312+
}
313+
314+
assert!(
315+
items.next().is_none(),
316+
"ExactSizeIterator under-reported length"
317+
);
318+
319+
this.finish(len, header)
320+
}
321+
}
322+
}
323+
324+
fn raw(&self) -> *mut u8 {
325+
self.raw.as_ptr().cast()
326+
}
327+
328+
fn new(len: usize, raw: ptr::NonNull<SliceWithHeader<Header, Item>>) -> Self {
329+
let (layout, [length_offset, header_offset, slice_offset]) =
330+
SliceWithHeader::<Header, Item>::layout(len);
331+
InProgress {
332+
raw,
333+
written: 0,
334+
layout,
335+
length_offset,
336+
header_offset,
337+
slice_offset,
265338
}
266-
assert!(
267-
items.next().is_none(),
268-
"ExactSizeIterator under-reported length"
269-
);
270-
assert_eq!(layout, Layout::for_value(ptr.as_ref()));
271-
})
339+
}
340+
341+
unsafe fn push(&mut self, item: Item) {
342+
self.raw()
343+
.add(self.slice_offset)
344+
.cast::<Item>()
345+
.add(self.written)
346+
.write(item);
347+
self.written += 1;
348+
}
349+
350+
unsafe fn finish(self, len: usize, header: Header) {
351+
let this = ManuallyDrop::new(self);
352+
ptr::write(this.raw().add(this.length_offset).cast(), len);
353+
ptr::write(this.raw().add(this.header_offset).cast(), header);
354+
debug_assert_eq!(this.layout, Layout::for_value(this.raw.as_ref()))
355+
}
272356
}
357+
358+
unsafe { A::new_slice_dst(len, InProgress::init(len, header, items)) }
273359
}
274360
}
275361

@@ -286,11 +372,6 @@ where
286372
#[cfg(feature = "erasable")]
287373
unsafe impl<Header, Item> Erasable for SliceWithHeader<Header, Item> {
288374
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-
294375
let len: usize = ptr::read(this.as_ptr().cast());
295376
let raw = ptr::NonNull::new_unchecked(slice_from_raw_parts(this.as_ptr().cast(), len));
296377
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)