Re: [PATCH v7 04/26] rust: alloc: implement `Allocator` for `Kmalloc`

Benno Lossin
2024-09-26T13:01:53Z

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
>