1 // SPDX-License-Identifier: GPL-2.0 2 3 //! So far the kernel's `Box` and `Vec` types can't be used by userspace test cases, since all users 4 //! of those types (e.g. `CString`) use kernel allocators for instantiation. 5 //! 6 //! In order to allow userspace test cases to make use of such types as well, implement the 7 //! `Cmalloc` allocator within the `allocator_test` module and type alias all kernel allocators to 8 //! `Cmalloc`. The `Cmalloc` allocator uses libc's `realloc()` function as allocator backend. 9 10 #![allow(missing_docs)] 11 12 use super::{flags::*, AllocError, Allocator, Flags}; 13 use core::alloc::Layout; 14 use core::cmp; 15 use core::ptr; 16 use core::ptr::NonNull; 17 18 /// The userspace allocator based on libc. 19 pub struct Cmalloc; 20 21 pub type Kmalloc = Cmalloc; 22 pub type Vmalloc = Kmalloc; 23 pub type KVmalloc = Kmalloc; 24 25 impl Cmalloc { 26 /// Returns a [`Layout`] that makes [`Kmalloc`] fulfill the requested size and alignment of 27 /// `layout`. 28 pub fn aligned_layout(layout: Layout) -> Layout { 29 // Note that `layout.size()` (after padding) is guaranteed to be a multiple of 30 // `layout.align()` which together with the slab guarantees means that `Kmalloc` will return 31 // a properly aligned object (see comments in `kmalloc()` for more information). 32 layout.pad_to_align() 33 } 34 } 35 36 extern "C" { 37 #[link_name = "aligned_alloc"] 38 fn libc_aligned_alloc(align: usize, size: usize) -> *mut crate::ffi::c_void; 39 40 #[link_name = "free"] 41 fn libc_free(ptr: *mut crate::ffi::c_void); 42 } 43 44 // SAFETY: 45 // - memory remains valid until it is explicitly freed, 46 // - passing a pointer to a valid memory allocation created by this `Allocator` is always OK, 47 // - `realloc` provides the guarantees as provided in the `# Guarantees` section. 48 unsafe impl Allocator for Cmalloc { 49 unsafe fn realloc( 50 ptr: Option<NonNull<u8>>, 51 layout: Layout, 52 old_layout: Layout, 53 flags: Flags, 54 ) -> Result<NonNull<[u8]>, AllocError> { 55 let src = match ptr { 56 Some(src) => { 57 if old_layout.size() == 0 { 58 ptr::null_mut() 59 } else { 60 src.as_ptr() 61 } 62 } 63 None => ptr::null_mut(), 64 }; 65 66 if layout.size() == 0 { 67 // SAFETY: `src` is either NULL or was previously allocated with this `Allocator` 68 unsafe { libc_free(src.cast()) }; 69 70 return Ok(NonNull::slice_from_raw_parts( 71 crate::alloc::dangling_from_layout(layout), 72 0, 73 )); 74 } 75 76 // ISO C (ISO/IEC 9899:2011) defines `aligned_alloc`: 77 // 78 // > The value of alignment shall be a valid alignment supported by the implementation 79 // [...]. 80 // 81 // As an example of the "supported by the implementation" requirement, POSIX.1-2001 (IEEE 82 // 1003.1-2001) defines `posix_memalign`: 83 // 84 // > The value of alignment shall be a power of two multiple of sizeof (void *). 85 // 86 // and POSIX-based implementations of `aligned_alloc` inherit this requirement. At the time 87 // of writing, this is known to be the case on macOS (but not in glibc). 88 // 89 // Satisfy the stricter requirement to avoid spurious test failures on some platforms. 90 let min_align = core::mem::size_of::<*const crate::ffi::c_void>(); 91 let layout = layout.align_to(min_align).map_err(|_| AllocError)?; 92 let layout = layout.pad_to_align(); 93 94 // SAFETY: Returns either NULL or a pointer to a memory allocation that satisfies or 95 // exceeds the given size and alignment requirements. 96 let dst = unsafe { libc_aligned_alloc(layout.align(), layout.size()) }.cast::<u8>(); 97 let dst = NonNull::new(dst).ok_or(AllocError)?; 98 99 if flags.contains(__GFP_ZERO) { 100 // SAFETY: The preceding calls to `libc_aligned_alloc` and `NonNull::new` 101 // guarantee that `dst` points to memory of at least `layout.size()` bytes. 102 unsafe { dst.as_ptr().write_bytes(0, layout.size()) }; 103 } 104 105 if !src.is_null() { 106 // SAFETY: 107 // - `src` has previously been allocated with this `Allocator`; `dst` has just been 108 // newly allocated, hence the memory regions do not overlap. 109 // - both` src` and `dst` are properly aligned and valid for reads and writes 110 unsafe { 111 ptr::copy_nonoverlapping( 112 src, 113 dst.as_ptr(), 114 cmp::min(layout.size(), old_layout.size()), 115 ) 116 }; 117 } 118 119 // SAFETY: `src` is either NULL or was previously allocated with this `Allocator` 120 unsafe { libc_free(src.cast()) }; 121 122 Ok(NonNull::slice_from_raw_parts(dst, layout.size())) 123 } 124 } 125