Skip to content

Commit

Permalink
rust: allocator: Prevent mis-aligned allocation
Browse files Browse the repository at this point in the history
Currently the rust allocator simply passes the size of the type Layout
to krealloc(), and in theory the alignment requirement from the type
Layout may be larger than the guarantee provided by SLAB, which means
the allocated object is mis-aligned.

Fix this by adjusting the allocation size to the nearest power of two,
which SLAB always guarantees a size-aligned allocation. And because Rust
guarantees that the original size must be a multiple of alignment and
the alignment must be a power of two, then the alignment requirement is
satisfied.

Suggested-by: Vlastimil Babka <[email protected]>
Co-developed-by: "Andreas Hindborg (Samsung)" <[email protected]>
Signed-off-by: "Andreas Hindborg (Samsung)" <[email protected]>
Signed-off-by: Boqun Feng <[email protected]>
Cc: [email protected] # v6.1+
Acked-by: Vlastimil Babka <[email protected]>
Fixes: 247b365 ("rust: add `kernel` crate")
Link: Rust-for-Linux#974
Link: https://lore.kernel.org/r/[email protected]
[ Applied rewording of comment as discussed in the mailing list. ]
Signed-off-by: Miguel Ojeda <[email protected]>
  • Loading branch information
fbq authored and ojeda committed Aug 4, 2023
1 parent 6eaae19 commit b3d8aa8
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 15 deletions.
1 change: 1 addition & 0 deletions rust/bindings/bindings_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
#include <linux/sched.h>

/* `bindgen` gets confused at certain things. */
const size_t BINDINGS_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN;
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO;
74 changes: 59 additions & 15 deletions rust/kernel/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,36 @@ use crate::bindings;

struct KernelAllocator;

/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment.
///
/// # Safety
///
/// - `ptr` can be either null or a pointer which has been allocated by this allocator.
/// - `new_layout` must have a non-zero size.
unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: bindings::gfp_t) -> *mut u8 {
// Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
let layout = new_layout.pad_to_align();

let mut size = layout.size();

if layout.align() > bindings::BINDINGS_ARCH_SLAB_MINALIGN {
// The alignment requirement exceeds the slab guarantee, thus try to enlarge the size
// to use the "power-of-two" size/alignment guarantee (see comments in `kmalloc()` for
// more information).
//
// Note that `layout.size()` (after padding) is guaranteed to be a multiple of
// `layout.align()`, so `next_power_of_two` gives enough alignment guarantee.
size = size.next_power_of_two();
}

// SAFETY:
// - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the
// function safety requirement.
// - `size` is greater than 0 since it's either a `layout.size()` (which cannot be zero
// according to the function safety requirement) or a result from `next_power_of_two()`.
unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags) as *mut u8 }
}

unsafe impl GlobalAlloc for KernelAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// `krealloc()` is used instead of `kmalloc()` because the latter is
Expand All @@ -30,10 +60,20 @@ static ALLOCATOR: KernelAllocator = KernelAllocator;
// to extract the object file that has them from the archive. For the moment,
// let's generate them ourselves instead.
//
// Note: Although these are *safe* functions, they are called by the compiler
// with parameters that obey the same `GlobalAlloc` function safety
// requirements: size and align should form a valid layout, and size is
// greater than 0.
//
// Note that `#[no_mangle]` implies exported too, nowadays.
#[no_mangle]
fn __rust_alloc(size: usize, _align: usize) -> *mut u8 {
unsafe { bindings::krealloc(core::ptr::null(), size, bindings::GFP_KERNEL) as *mut u8 }
fn __rust_alloc(size: usize, align: usize) -> *mut u8 {
// SAFETY: See assumption above.
let layout = unsafe { Layout::from_size_align_unchecked(size, align) };

// SAFETY: `ptr::null_mut()` is null, per assumption above the size of `layout` is greater
// than 0.
unsafe { krealloc_aligned(ptr::null_mut(), layout, bindings::GFP_KERNEL) }
}

#[no_mangle]
Expand All @@ -42,23 +82,27 @@ fn __rust_dealloc(ptr: *mut u8, _size: usize, _align: usize) {
}

#[no_mangle]
fn __rust_realloc(ptr: *mut u8, _old_size: usize, _align: usize, new_size: usize) -> *mut u8 {
unsafe {
bindings::krealloc(
ptr as *const core::ffi::c_void,
new_size,
bindings::GFP_KERNEL,
) as *mut u8
}
fn __rust_realloc(ptr: *mut u8, _old_size: usize, align: usize, new_size: usize) -> *mut u8 {
// SAFETY: See assumption above.
let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, align) };

// SAFETY: Per assumption above, `ptr` is allocated by `__rust_*` before, and the size of
// `new_layout` is greater than 0.
unsafe { krealloc_aligned(ptr, new_layout, bindings::GFP_KERNEL) }
}

#[no_mangle]
fn __rust_alloc_zeroed(size: usize, _align: usize) -> *mut u8 {
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
// SAFETY: See assumption above.
let layout = unsafe { Layout::from_size_align_unchecked(size, align) };

// SAFETY: `ptr::null_mut()` is null, per assumption above the size of `layout` is greater
// than 0.
unsafe {
bindings::krealloc(
core::ptr::null(),
size,
krealloc_aligned(
ptr::null_mut(),
layout,
bindings::GFP_KERNEL | bindings::__GFP_ZERO,
) as *mut u8
)
}
}

0 comments on commit b3d8aa8

Please sign in to comment.