How can I allocate aligned memory in Rust?

For FFI, I'd like to allocate aligned memory with a size that's defined at runtime. It'd be great if it's a vector, but it doesn't look like it's possible. Is there a cross-platform way to allocate aligned memory?

To be clear, this is what I'm looking for, approximately:

let size = get_object_size();
let alignment = size; // can only be 1-8 bytes
let v: Vec<u8> = Vec::new();
v.allocate_aligned(size, alignment);
assert_eq!(v.as_ptr() as i64 & (alignment - 1), 0);
assert_eq!(v.len(), size);
2 Likes

By default, Vec<T> guarantees that memory is properly aligned for type T. If you want to, say, allocate an over-aligned slice of bytes, you may find the "alloc" module of std interesting.

Once you have gotten you allocations the way you want, you can use std::slice::from_raw_parts[_mut] to wrap them behind a nicer Rust slice interface, but be sure to check that function's documentation as it has some important preconditions.

One (not so) obvious bit which the slice::from_raw_parts documentation does not mention is that you must initialize your data before building a Rust slice pointing to it.

Vec::from_raw_parts is possible as well, but probably too dangerous, because any reallocation would destroy all your hard work!

2 Likes

It would also cause undefined behavior as memory must be deallocated with the same alignment it was allocated with.

2 Likes

Nice catch, thanks!

1 Like

Sounds like you're looking for std::alloc::alloc(). It's given a Layout (size + alignment) and will return a raw pointer to some allocated memory using the global allocator.

This is all unsafe of course, but if you're doing FFI then I don't see it being a problem.

There's also Alloc::alloc_array(), but that requires a handle to an existing allocator, and the allocator API isn't stable yet.

2 Likes

Quick-and-dirty hack

In the case where the alignment is one of 1, 2, 4, 8, picking 8 since the beginning removes the "runtime" dependency and allows to just allocate at least size bytes in memory by creating a Vec::<u64>::with_capacity(size / 8 + 1).

The proper way:

use ::core::ptr;
use ::std::alloc; // or extern crate alloc; use ::alloc::alloc;

fn alloc (numbytes: usize, alignment: usize)
  -> Option<ptr::NonNull<()>>
{Some({
    if numbytes == 0 { return None; }
    let layout =
        alloc::Layout::from_size_align(numbytes, alignment)
            .map_err(|err| eprintln!("Layout error: {}", err))
            .ok()?
    ;
    ptr::NonNull::new(unsafe {
        // # Safety
        //
        //   - numbytes != 0
        alloc::alloc(layout)
    })?
        .cast::<()>()
})}

/// # Safety
///
///   - `ptr`, when `NonNull`, must be a value returned by `alloc(numbytes, alignment)`
#[require_unsafe_in_body] // or #[allow(unused_unsafe)]
unsafe
fn free (
    ptr: Option<ptr::NonNull:<()>>,
    numbytes: usize,
    alignment: usize,
)
{
    let ptr = if let Some(ptr) = ptr { ptr } else { return; };
    let layout =
        alloc::Layout::from_size_align(numbytes, alignment)
            .unwrap_or_else(|err| {
                // if same layout as input this should not happen,
                // so it is a very bad bug if this is reached
                eprintln!("Layout error: {}", err);
                ::std::proces::abort();
            })
    ;
    unsafe {
        // # Safety
        //
        //   - `ptr` came from alloc::alloc(layout);
        alloc::dealloc(ptr.cast::<u8>().as_ptr(), layout);
    }
}
  • Where I have used #[require_unsafe_in_body] to remove Rust mistakenly not requiring unsafe blocks in unsafe fn function bodies.
2 Likes

If the entire function is marked as unsafe, why do you need to write an explicit unsafe block? Documentation?

1 Like

To get more maintainable code:

  • the fact that a function is unsafe "just" means that the caller may have to uphold some invariant to call the function, so that the caller is responsible of any resulting UB if they fail to uphold such invariant.

  • Requiring unsafe within code / in a function body is the reversed situation: we are now a "caller" and need to [prove that we] uphold some invariant(s) when doing certain things, else our code is unsound.

  • Often the invariants required to use / call some unsafe code may be the same invariants that are required at call site, so, the "proof" for an inner unsafe {} block would be: "This property upholds as part of the safety contract of the API".

    1. Is having to write this explicitely that bad? I don't think so.

    2. Are all the unsafe usages in an unsafe fn sound because of / thanks to the safety invariants in the contract itself? No!!

Example

Consider the following function:

/// Calls the function with the given integer
/// (after having set the pointer to `Some(0)` if it was `None`).
///
/// # Safety
///
///   - `ptr` must be a non-null, well-aligned pointer to a
///     valid and read-writable `Option<i32>`.
///
///   - `ptr` may be aliased but only within the same thread.
unsafe
fn handle_opt (ptr: *mut Option<i32>, f: impl FnOnce(i32))
{
    use ::std::cell::Cell;
    // single-threaded aliased *mut _ <=> *const Cell<_>
    let at_opt: &Cell<Option<i32>> = {
        &*(ptr as *const Cell<_>)
    };
    match at_opt.get() {
        | Some(it) => {
            f(it);
        },
        | None => {
            at_opt.set(Some(0));
            f(0);
        }
    }
    // Debug: the ptr points to:
    match at_opt.get() {
        | Some(value) => {
            eprintln!("ptr now points to `Some({})`", value);
        },
        | None => {
            ::std::hint::unreachable_unchecked();
        },
    }
}
  • (I am aware that such function should actually be taking a &Cell<Option<i32>> directly instead, since those are the exact requirements for the given raw pointer, but I just wanted an excuse to define an unsafe fn)
fn handle_opt_safe (ptr: &'_ Cell<Option<i32>>, f: impl FnOnce(i32))
{
    unsafe {
        // # Safety: a `&Cell<Option<i32>>` verifies all the required properties
        handle_opt(
            ptr as *const Cell<Option<i32>> as *mut Option<i32>,
            f,
        )
    }
}

The above function is actually unsound, but the exact reason may not be obvious given the lack of unsafe blocks. Here, we are performing two "unrelated" unsafe operations:

  • we are saying that a valid (yadda yadda) *mut Option<i32> aliased only from within the same thread can be seen as as &Cell<Option<i32>>. This is not a simple property, so most of the mental effort may be sent verifying this, but this is actually fine :white_check_mark:

  • we are asserting that a code path may not be reached :question: From the looks of the function, this may seem quite innocent, and since all the attention is diverted by the previous property this one may most probably be overlooked. But if we had had to explicitly justify such assertions with required unsafe blocks, or at least if there had been an unsafe block around it, then we may hope that somebody eventually notices the problem.

The issue

The following call is UB:

fn main ()
{
    let my_cell = Cell::new(Some(42)); // whatever value
    let f = |_value| {
        my_cell.set(None);
    };
    handle_opt_safe(&my_cell, f);
}

#[require_unsafe_in_body]

/// Calls the function with a pointer to the given integer, after having 
/// zero-initialized the pointee if it was `None`.
///
/// # Safety
///
///   - `ptr` must be a non-null, well-aligned pointer to a
///     valid and read-writable `Option<i32>`.
///
///   - `ptr` may be aliased but it must then not be read or written in
///     parallel by another thread
#[require_unsafe_in_body] // or #[allow(unused_unsafe)] in the playground
unsafe
fn handle_opt (ptr: *mut Option<i32>, f: impl FnOnce(i32))
{
    // 
    let at_opt: &Cell<Option<i32>> = unsafe {
        // # Safety
        //
        //   - single-threaded-only aliased and valid *mut _ <=> *const Cell<_>
        //
        //   - the caller has been told to uphold these invariants.
        &*(ptr as *const Cell<_>)
    };
    match at_opt.get() {
        | Some(it) => {
            f(it);
        },
        | None => {
            at_opt.set(Some(0));
            f(0);
        }
    }
    // Debug: the ptr points to:
    match at_opt.get() {
        | Some(value) => {
            eprintln!("ptr now points to `Some({})`", value);
        },
        | None => unsafe {
            // # Safety (??)
            //
            //   - We have initialized / set our Option previously
            //
            //   - there is no possible mutation to the Option in the meantime
            //     because it cannot be aliased - err actually it can oh I see now...
            ::std::hint::unreachable_unchecked();
        },
    }
}

TL,DR:

It is not because a function requires that some invariant be upheld when called (unsafe fn) that it should be exempted from proving it upholds safety guarantees within its body (unsafe {}).

Although I expect Rust to eventually fix that design oversight in some future edition, in the meantime, you can use the #[require_unsafe_in_body] procedural macro to get such behavior in current stable Rust.

7 Likes