Skip to content

Commit e9352e3

Browse files
committed
allocator::adapt::DangleZst: fix ZST reallocs by mass reimplementation.
• Why was I trying to copy data to/from ZSTs? • Why did I try to free dangling pointers corresponding to ZSTs? • Why were I calling `realloc_uninit` to implement `thin::Realloc::realloc_zeroed`? • No seriously, what the fuck? Additionally: • Additional test coverage (including for `AllocZst`) • `impl thin::SizeOf{,Debug} for allocator::adapt::DangleZst` relates to #32
1 parent 259ddec commit e9352e3

File tree

3 files changed

+100
-38
lines changed

3 files changed

+100
-38
lines changed

src/allocator/adapt/dangle_zst.rs

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use core::ptr::NonNull;
1414

1515
impl<A: Meta> DangleZst<A> {
1616
// TODO: replace with NonNull::new when that stabilizes as a const fn
17-
const DANGLE : NonNull<MaybeUninit<u8>> = unsafe { NonNull::new_unchecked(A::MAX_ALIGN.as_usize() as _) };
17+
const DANGLE : NonNull<MaybeUninit<u8>> = match crate::util::nn::from_usize(A::MAX_ALIGN.as_usize()) { Some(nn) => nn, None => panic!("could not convert A::MAX_ALIGN to a NonNull") };
1818
}
1919

2020
impl<A> core::ops::Deref for DangleZst<A> { fn deref(&self) -> &Self::Target { &self.0 } type Target = A; }
@@ -71,7 +71,21 @@ unsafe impl<A: thin::Realloc> thin::Realloc for DangleZst<A> {
7171
unsafe fn realloc_zeroed(&self, ptr: NonNull<MaybeUninit<u8>>, new_size: usize) -> Result<NonNull<MaybeUninit<u8>>, Self::Error> {
7272
if ptr == Self::DANGLE { return self.0.alloc_zeroed(new_size).map(|a| a.cast()) }
7373
if new_size == 0 { unsafe { self.0.free(ptr) }; return Ok(Self::DANGLE) }
74-
unsafe { self.0.realloc_uninit(ptr, new_size) }
74+
unsafe { self.0.realloc_zeroed(ptr, new_size) }
75+
}
76+
}
77+
78+
unsafe impl<A: thin::SizeOf> thin::SizeOf for DangleZst<A> {
79+
unsafe fn size_of(&self, ptr: NonNull<MaybeUninit<u8>>) -> usize {
80+
if ptr == Self::DANGLE { return 0 }
81+
unsafe { self.0.size_of(ptr) }
82+
}
83+
}
84+
85+
unsafe impl<A: thin::SizeOfDebug> thin::SizeOfDebug for DangleZst<A> {
86+
unsafe fn size_of_debug(&self, ptr: NonNull<MaybeUninit<u8>>) -> Option<usize> {
87+
if ptr == Self::DANGLE { return Some(0) }
88+
unsafe { self.0.size_of_debug(ptr) }
7589
}
7690
}
7791

@@ -114,42 +128,44 @@ unsafe impl<A: fat::Free> fat::Free for DangleZst<A> {
114128

115129
unsafe impl<A: fat::Realloc> fat::Realloc for DangleZst<A> {
116130
unsafe fn realloc_uninit(&self, ptr: NonNull<MaybeUninit<u8>>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<MaybeUninit<u8>>, Self::Error> {
117-
if old_layout.size() > 0 && new_layout.size() > 0 {
118-
unsafe { self.0.realloc_uninit(ptr, old_layout, new_layout) }
119-
} else {
120-
if cfg!(debug_assertions) && old_layout.size() == 0 {
121-
if ptr != Self::DANGLE { bug::ub::invalid_ptr_for_allocator(ptr) }
122-
if old_layout.align() > A::MAX_ALIGN.as_usize() { bug::ub::invalid_free_align_for_allocator(old_layout.align()) }
123-
}
124-
let alloc = self.alloc_uninit(new_layout)?;
125-
let n = old_layout.size().min(new_layout.size());
126-
{
127-
let src : & [MaybeUninit<u8>] = unsafe { core::slice::from_raw_parts (ptr .as_ptr(), n) };
128-
let dst : &mut [MaybeUninit<u8>] = unsafe { core::slice::from_raw_parts_mut(alloc.as_ptr(), n) };
129-
dst.copy_from_slice(src);
130-
}
131-
unsafe { self.free(ptr, old_layout) };
132-
Ok(alloc)
131+
let old_zst = old_layout.size() == 0;
132+
let new_zst = new_layout.size() == 0;
133+
134+
if old_zst && cfg!(debug_assertions) {
135+
if ptr != Self::DANGLE { bug::ub::invalid_ptr_for_allocator(ptr) }
136+
if old_layout.align() > A::MAX_ALIGN.as_usize() { bug::ub::invalid_free_align_for_allocator(old_layout.align()) }
137+
}
138+
139+
if new_zst && new_layout.align() > A::MAX_ALIGN.as_usize() {
140+
return Err(ExcessiveAlignmentRequestedError{ requested: new_layout.into(), supported: A::MAX_ALIGN }.into())
141+
}
142+
143+
match (old_zst, new_zst) {
144+
(false, false) => unsafe { self.0.realloc_uninit(ptr, old_layout, new_layout) },
145+
(false, true ) => unsafe { self.0.free(ptr, old_layout); Ok(Self::DANGLE.cast()) },
146+
(true, false) => { self.0.alloc_uninit(new_layout) },
147+
(true, true ) => { Ok(Self::DANGLE.cast()) },
133148
}
134149
}
135150

136151
unsafe fn realloc_zeroed(&self, ptr: NonNull<MaybeUninit<u8>>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<MaybeUninit<u8>>, Self::Error> {
137-
if old_layout.size() > 0 && new_layout.size() > 0 {
138-
unsafe { self.0.realloc_zeroed(ptr, old_layout, new_layout) }
139-
} else {
140-
if cfg!(debug_assertions) && old_layout.size() == 0 {
141-
if ptr != Self::DANGLE { bug::ub::invalid_ptr_for_allocator(ptr) }
142-
if old_layout.align() > A::MAX_ALIGN.as_usize() { bug::ub::invalid_free_align_for_allocator(old_layout.align()) }
143-
}
144-
let alloc = self.alloc_zeroed(new_layout)?.cast();
145-
let n = old_layout.size().min(new_layout.size());
146-
{
147-
let src : & [MaybeUninit<u8>] = unsafe { core::slice::from_raw_parts (ptr .as_ptr(), n) };
148-
let dst : &mut [MaybeUninit<u8>] = unsafe { core::slice::from_raw_parts_mut(alloc.as_ptr(), n) };
149-
dst.copy_from_slice(src);
150-
}
151-
unsafe { self.free(ptr, old_layout) };
152-
Ok(alloc)
152+
let old_zst = old_layout.size() == 0;
153+
let new_zst = new_layout.size() == 0;
154+
155+
if old_zst && cfg!(debug_assertions) {
156+
if ptr != Self::DANGLE { bug::ub::invalid_ptr_for_allocator(ptr) }
157+
if old_layout.align() > A::MAX_ALIGN.as_usize() { bug::ub::invalid_free_align_for_allocator(old_layout.align()) }
158+
}
159+
160+
if new_zst && new_layout.align() > A::MAX_ALIGN.as_usize() {
161+
return Err(ExcessiveAlignmentRequestedError{ requested: new_layout.into(), supported: A::MAX_ALIGN }.into())
162+
}
163+
164+
match (old_zst, new_zst) {
165+
(false, false) => unsafe { self.0.realloc_zeroed(ptr, old_layout, new_layout) },
166+
(false, true ) => unsafe { self.0.free(ptr, old_layout); Ok(Self::DANGLE.cast()) },
167+
(true, false) => { Ok(self.0.alloc_zeroed(new_layout)?.cast()) },
168+
(true, true ) => { Ok(Self::DANGLE.cast()) },
153169
}
154170
}
155171
}

src/allocator/c/malloc.rs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,6 @@ unsafe impl thin::SizeOfDebug for Malloc {
222222
#[test] fn thin_zeroed() { thin::test::zeroed_alloc(Malloc) }
223223
#[test] fn thin_zeroed_realloc() { thin::test::zeroed_realloc(Malloc) }
224224
#[test] fn thin_zst_support() { thin::test::zst_supported_conservative(Malloc) }
225-
#[test] fn thin_zst_support_dangle() { thin::test::zst_supported_conservative(crate::allocator::adapt::DangleZst(Malloc)) }
226-
#[test] fn thin_zst_support_alloc() { thin::test::zst_supported_conservative(crate::allocator::adapt::AllocZst(Malloc)) }
227225

228226
#[test] fn fat_alignment() { fat::test::alignment(Malloc) }
229227
#[test] fn fat_edge_case_sizes() { fat::test::edge_case_sizes(Malloc) }
@@ -232,5 +230,49 @@ unsafe impl thin::SizeOfDebug for Malloc {
232230
#[test] fn fat_zeroed() { fat::test::zeroed_alloc(Malloc) }
233231
#[test] fn fat_zeroed_realloc() { fat::test::zeroed_realloc(Malloc) }
234232
#[test] fn fat_zst_support() { fat::test::zst_supported_conservative(Malloc) }
235-
#[test] fn fat_zst_support_dangle() { fat::test::zst_supported_conservative(crate::allocator::adapt::DangleZst(Malloc)) }
236-
#[test] fn fat_zst_support_alloc() { fat::test::zst_supported_conservative(crate::allocator::adapt::AllocZst(Malloc)) }
233+
234+
#[cfg(test)] mod adapt_alloc_zst {
235+
use crate::allocator::adapt::AllocZst;
236+
use super::{thin, fat, Malloc, MALLOC_ZERO_INITS};
237+
238+
#[test] fn thin_alignment() { thin::test::alignment(AllocZst(Malloc)) }
239+
#[test] fn thin_edge_case_sizes() { thin::test::edge_case_sizes(AllocZst(Malloc)) }
240+
#[test] fn thin_nullable() { thin::test::nullable(AllocZst(Malloc)) }
241+
#[test] fn thin_size() { thin::test::size_exact_alloc_except_zsts(AllocZst(Malloc)) }
242+
#[test] fn thin_uninit() { if !MALLOC_ZERO_INITS { unsafe { thin::test::uninit_alloc_unsound(AllocZst(Malloc)) } } }
243+
#[test] fn thin_uninit_realloc() { thin::test::uninit_realloc(AllocZst(Malloc)) }
244+
#[test] fn thin_zeroed() { thin::test::zeroed_alloc(AllocZst(Malloc)) }
245+
#[test] fn thin_zeroed_realloc() { thin::test::zeroed_realloc(AllocZst(Malloc)) }
246+
#[test] fn thin_zst_support() { thin::test::zst_supported_conservative(AllocZst(Malloc)) }
247+
248+
#[test] fn fat_alignment() { fat::test::alignment(AllocZst(Malloc)) }
249+
#[test] fn fat_edge_case_sizes() { fat::test::edge_case_sizes(AllocZst(Malloc)) }
250+
#[test] fn fat_uninit() { if !MALLOC_ZERO_INITS { unsafe { fat::test::uninit_alloc_unsound(AllocZst(Malloc)) } } }
251+
#[test] fn fat_uninit_realloc() { fat::test::uninit_realloc(AllocZst(Malloc)) }
252+
#[test] fn fat_zeroed() { fat::test::zeroed_alloc(AllocZst(Malloc)) }
253+
#[test] fn fat_zeroed_realloc() { fat::test::zeroed_realloc(AllocZst(Malloc)) }
254+
#[test] fn fat_zst_support() { fat::test::zst_supported_conservative(AllocZst(Malloc)) }
255+
}
256+
257+
#[cfg(test)] mod adapt_dangle_zst {
258+
use crate::allocator::adapt::DangleZst;
259+
use super::{thin, fat, Malloc, MALLOC_ZERO_INITS};
260+
261+
#[test] fn thin_alignment() { thin::test::alignment(DangleZst(Malloc)) }
262+
#[test] fn thin_edge_case_sizes() { thin::test::edge_case_sizes(DangleZst(Malloc)) }
263+
#[test] fn thin_nullable() { thin::test::nullable(DangleZst(Malloc)) }
264+
#[test] fn thin_size() { thin::test::size_exact_alloc(DangleZst(Malloc)) }
265+
#[test] fn thin_uninit() { if !MALLOC_ZERO_INITS { unsafe { thin::test::uninit_alloc_unsound(DangleZst(Malloc)) } } }
266+
#[test] fn thin_uninit_realloc() { thin::test::uninit_realloc(DangleZst(Malloc)) }
267+
#[test] fn thin_zeroed() { thin::test::zeroed_alloc(DangleZst(Malloc)) }
268+
#[test] fn thin_zeroed_realloc() { thin::test::zeroed_realloc(DangleZst(Malloc)) }
269+
#[test] fn thin_zst_support() { thin::test::zst_supported_conservative(DangleZst(Malloc)) }
270+
271+
#[test] fn fat_alignment() { fat::test::alignment(DangleZst(Malloc)) }
272+
#[test] fn fat_edge_case_sizes() { fat::test::edge_case_sizes(DangleZst(Malloc)) }
273+
#[test] fn fat_uninit() { if !MALLOC_ZERO_INITS { unsafe { fat::test::uninit_alloc_unsound(DangleZst(Malloc)) } } }
274+
#[test] fn fat_uninit_realloc() { fat::test::uninit_realloc(DangleZst(Malloc)) }
275+
#[test] fn fat_zeroed() { fat::test::zeroed_alloc(DangleZst(Malloc)) }
276+
#[test] fn fat_zeroed_realloc() { fat::test::zeroed_realloc(DangleZst(Malloc)) }
277+
#[test] fn fat_zst_support() { fat::test::zst_supported_conservative(DangleZst(Malloc)) }
278+
}

src/util/nn.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,7 @@ pub /*const*/ fn slice_assume_init<T>(data: NonNull<[MaybeUninit<T>]>) -> NonNul
2323
pub fn dangling<T>(layout: Layout) -> NonNull<T> {
2424
NonNull::new(layout.align() as _).unwrap_or(NonNull::dangling())
2525
}
26+
27+
// TODO: replace with NonNull::new when that stabilizes as a const fn
28+
pub const fn from_usize<T>(value: usize) -> Option<NonNull<T>> { from_ptr(value as *mut T) }
29+
pub const fn from_ptr<T>(ptr: *mut T) -> Option<NonNull<T>> { unsafe { core::mem::transmute(ptr) } }

0 commit comments

Comments
 (0)