Skip to content

Commit b3d8aa8

Browse files
fbqojeda
authored andcommitted
rust: allocator: Prevent mis-aligned allocation
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/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]>
1 parent 6eaae19 commit b3d8aa8

File tree

2 files changed

+60
-15
lines changed

2 files changed

+60
-15
lines changed

rust/bindings/bindings_helper.h

+1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@
1313
#include <linux/sched.h>
1414

1515
/* `bindgen` gets confused at certain things. */
16+
const size_t BINDINGS_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN;
1617
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
1718
const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO;

rust/kernel/allocator.rs

+59-15
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,36 @@ use crate::bindings;
99

1010
struct KernelAllocator;
1111

12+
/// Calls `krealloc` with a proper size to alloc a new object aligned to `new_layout`'s alignment.
13+
///
14+
/// # Safety
15+
///
16+
/// - `ptr` can be either null or a pointer which has been allocated by this allocator.
17+
/// - `new_layout` must have a non-zero size.
18+
unsafe fn krealloc_aligned(ptr: *mut u8, new_layout: Layout, flags: bindings::gfp_t) -> *mut u8 {
19+
// Customized layouts from `Layout::from_size_align()` can have size < align, so pad first.
20+
let layout = new_layout.pad_to_align();
21+
22+
let mut size = layout.size();
23+
24+
if layout.align() > bindings::BINDINGS_ARCH_SLAB_MINALIGN {
25+
// The alignment requirement exceeds the slab guarantee, thus try to enlarge the size
26+
// to use the "power-of-two" size/alignment guarantee (see comments in `kmalloc()` for
27+
// more information).
28+
//
29+
// Note that `layout.size()` (after padding) is guaranteed to be a multiple of
30+
// `layout.align()`, so `next_power_of_two` gives enough alignment guarantee.
31+
size = size.next_power_of_two();
32+
}
33+
34+
// SAFETY:
35+
// - `ptr` is either null or a pointer returned from a previous `k{re}alloc()` by the
36+
// function safety requirement.
37+
// - `size` is greater than 0 since it's either a `layout.size()` (which cannot be zero
38+
// according to the function safety requirement) or a result from `next_power_of_two()`.
39+
unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags) as *mut u8 }
40+
}
41+
1242
unsafe impl GlobalAlloc for KernelAllocator {
1343
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
1444
// `krealloc()` is used instead of `kmalloc()` because the latter is
@@ -30,10 +60,20 @@ static ALLOCATOR: KernelAllocator = KernelAllocator;
3060
// to extract the object file that has them from the archive. For the moment,
3161
// let's generate them ourselves instead.
3262
//
63+
// Note: Although these are *safe* functions, they are called by the compiler
64+
// with parameters that obey the same `GlobalAlloc` function safety
65+
// requirements: size and align should form a valid layout, and size is
66+
// greater than 0.
67+
//
3368
// Note that `#[no_mangle]` implies exported too, nowadays.
3469
#[no_mangle]
35-
fn __rust_alloc(size: usize, _align: usize) -> *mut u8 {
36-
unsafe { bindings::krealloc(core::ptr::null(), size, bindings::GFP_KERNEL) as *mut u8 }
70+
fn __rust_alloc(size: usize, align: usize) -> *mut u8 {
71+
// SAFETY: See assumption above.
72+
let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
73+
74+
// SAFETY: `ptr::null_mut()` is null, per assumption above the size of `layout` is greater
75+
// than 0.
76+
unsafe { krealloc_aligned(ptr::null_mut(), layout, bindings::GFP_KERNEL) }
3777
}
3878

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

4484
#[no_mangle]
45-
fn __rust_realloc(ptr: *mut u8, _old_size: usize, _align: usize, new_size: usize) -> *mut u8 {
46-
unsafe {
47-
bindings::krealloc(
48-
ptr as *const core::ffi::c_void,
49-
new_size,
50-
bindings::GFP_KERNEL,
51-
) as *mut u8
52-
}
85+
fn __rust_realloc(ptr: *mut u8, _old_size: usize, align: usize, new_size: usize) -> *mut u8 {
86+
// SAFETY: See assumption above.
87+
let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, align) };
88+
89+
// SAFETY: Per assumption above, `ptr` is allocated by `__rust_*` before, and the size of
90+
// `new_layout` is greater than 0.
91+
unsafe { krealloc_aligned(ptr, new_layout, bindings::GFP_KERNEL) }
5392
}
5493

5594
#[no_mangle]
56-
fn __rust_alloc_zeroed(size: usize, _align: usize) -> *mut u8 {
95+
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8 {
96+
// SAFETY: See assumption above.
97+
let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
98+
99+
// SAFETY: `ptr::null_mut()` is null, per assumption above the size of `layout` is greater
100+
// than 0.
57101
unsafe {
58-
bindings::krealloc(
59-
core::ptr::null(),
60-
size,
102+
krealloc_aligned(
103+
ptr::null_mut(),
104+
layout,
61105
bindings::GFP_KERNEL | bindings::__GFP_ZERO,
62-
) as *mut u8
106+
)
63107
}
64108
}

0 commit comments

Comments
 (0)