On 12.09.24 00:52, Danilo Krummrich wrote: > +/// # Invariants > +/// > +/// One of the following `krealloc`, `vrealloc`, `kvrealloc`. > +struct ReallocFunc( > + unsafe extern "C" fn(*const core::ffi::c_void, usize, u32) -> *mut core::ffi::c_void, > +); > + > +impl ReallocFunc { > + // INVARIANT: `krealloc` satisfies the type invariants. > + const KREALLOC: Self = Self(bindings::krealloc); > + > + /// # Safety > + /// > + /// This method has the same safety requirements as [`Allocator::realloc`]. > + /// > + /// # Guarantees > + /// > + /// This method has the same guarantees as `Allocator::realloc`. Additionally > + /// - it accepts any pointer to a valid memory allocation allocated by this function. > + /// - memory allocated by this function remains valid until it is passed to this function. > + unsafe fn call( > + &self, > + ptr: Option<NonNull<u8>>, > + layout: Layout, > + flags: Flags, > + ) -> Result<NonNull<[u8]>, AllocError> { > + let size = aligned_size(layout); > + let ptr = match ptr { > + Some(ptr) => ptr.as_ptr(), > + None => ptr::null(), > + }; > + > + // SAFETY: > + // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc` and thus only requires that > + // `ptr` is NULL or valid. > + // - `ptr` is either NULL or valid by the safety requirements of this function. > + // > + // GUARANTEE: > + // - `self.0` is one of `krealloc`, `vrealloc`, `kvrealloc`. > + // - Those functions provide the guarantees of this function. > + let raw_ptr = unsafe { > + // If `size == 0` and `ptr != NULL` the memory behind the pointer is freed. > + self.0(ptr.cast(), size, flags.0).cast() > + }; > + > + let ptr = if size == 0 { > + NonNull::dangling() > + } else { > + NonNull::new(raw_ptr).ok_or(AllocError)? > + }; > + > + Ok(NonNull::slice_from_raw_parts(ptr, size)) > + } > +} I remember asking you to split this into a different commit. I think you argued that it would be better to keep it in the same commit when bisecting. I don't think that applies in this case, are there any other disadvantages? --- Cheers, Benno > + > +// SAFETY: `realloc` delegates to `ReallocFunc::call`, which guarantees that > +// - memory remains valid until it is explicitly freed, > +// - passing a pointer to a valid memory allocation is OK, > +// - `realloc` satisfies the guarantees, since `ReallocFunc::call` has the same. > +unsafe impl Allocator for Kmalloc { > + #[inline] > + unsafe fn realloc( > + ptr: Option<NonNull<u8>>, > + layout: Layout, > + flags: Flags, > + ) -> Result<NonNull<[u8]>, AllocError> { > + // SAFETY: `ReallocFunc::call` has the same safety requirements as `Allocator::realloc`. > + unsafe { ReallocFunc::KREALLOC.call(ptr, layout, flags) } > + } > +} > + > unsafe impl GlobalAlloc for Kmalloc { > unsafe fn alloc(&self, layout: Layout) -> *mut u8 { > // SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety > -- > 2.46.0 >