Skip to content

Commit

Permalink
support aligned_alloc for unixes support.
Browse files Browse the repository at this point in the history
  • Loading branch information
devnexen committed May 18, 2024
1 parent 3ab1107 commit d654b73
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 0 deletions.
38 changes: 38 additions & 0 deletions src/shims/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,42 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
}
}

fn aligned_alloc(
&mut self,
align: u64,
size: u64,
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
let this = self.eval_context_mut();
// Alignment must be a power of 2, and "supported by the implementation".
// We decide that "supported by the implementation" means that the
// size must be a multiple of the alignment. (This restriction seems common
// enough that it is stated on <https://en.cppreference.com/w/c/memory/aligned_alloc>
// as a general rule, but the actual standard has no such rule.)
// If any of these are violated, we have to return NULL.
// All fundamental alignments must be supported.
//
// macOS and Illumos are buggy in that they require the alignment
// to be at least the size of a pointer, so they do not support all fundamental
// alignments. We do not emulate those platform bugs.
//
// Linux also sets errno to EINVAL, but that's non-standard behavior that we do not
// emulate.
// FreeBSD says some of these cases are UB but that's violating the C standard.
// http://en.cppreference.com/w/cpp/memory/c/aligned_alloc
// Linux: https://linux.die.net/man/3/aligned_alloc
// FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=aligned_alloc&apropos=0&sektion=3&manpath=FreeBSD+9-current&format=html
match size.checked_rem(align) {
Some(0) if align.is_power_of_two() => {
let align = align.max(this.malloc_align(size).bytes());
let ptr = this.allocate_ptr(
Size::from_bytes(size),
Align::from_bytes(align).unwrap(),
MiriMemoryKind::C.into(),
)?;
Ok(ptr.into())
}
_ => Ok(Pointer::null()),
}
}
}
11 changes: 11 additions & 0 deletions src/shims/unix/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
}
}
"aligned_alloc" => {
// This is a C11 function, we assume all Unixes have it.
// (MSVC explicitly does not support this.)
let [align, size] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let align = this.read_target_usize(align)?;
let size = this.read_target_usize(size)?;

let res = this.aligned_alloc(align, size)?;
this.write_pointer(res, dest)?;
}

// Dynamic symbol loading
"dlsym" => {
Expand Down
9 changes: 9 additions & 0 deletions src/shims/wasi/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let result = this.posix_memalign(memptr, align, size)?;
this.write_scalar(result, dest)?;
}
"aligned_alloc" => {
let [align, size] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let align = this.read_target_usize(align)?;
let size = this.read_target_usize(size)?;

let res = this.aligned_alloc(align, size)?;
this.write_pointer(res, dest)?;
}

_ => return Ok(EmulateItemResult::NotSupported),
}
Expand Down
15 changes: 15 additions & 0 deletions tests/fail-dep/libc/aligned_alloc_size_zero_leak.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//@ignore-target-windows: Windows does not support the standard C11 aligned_alloc.

fn main() {
// libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689),
// so we declare it ourselves.
extern "C" {
fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
}

// Make sure even zero-sized allocations need to be freed.

unsafe {
aligned_alloc(2, 0); //~ERROR: memory leaked
}
}
15 changes: 15 additions & 0 deletions tests/fail-dep/libc/aligned_alloc_size_zero_leak.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: memory leaked: ALLOC (C heap, size: 0, align: 2), allocated here:
--> $DIR/aligned_alloc_size_zero_leak.rs:LL:CC
|
LL | aligned_alloc(2, 0);
| ^^^^^^^^^^^^^^^^^^^
|
= note: BACKTRACE:
= note: inside `main` at $DIR/aligned_alloc_size_zero_leak.rs:LL:CC

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check

error: aborting due to 1 previous error

40 changes: 40 additions & 0 deletions tests/pass-dep/libc/libc-mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,44 @@ fn test_reallocarray() {
}
}

#[cfg(not(target_os = "windows"))]
fn test_aligned_alloc() {
// libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689),
// so we declare it ourselves.
extern "C" {
fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
}
// size not a multiple of the alignment
unsafe {
let p = aligned_alloc(16, 3);
assert_eq!(p, ptr::null_mut());
}

// alignment not power of 2
unsafe {
let p = aligned_alloc(63, 8);
assert_eq!(p, ptr::null_mut());
}

// alignment lesser than a word but still a successful allocation
unsafe {
let p = aligned_alloc(1, 4);
assert!(!p.is_null());
assert!(p.is_aligned_to(4));
libc::free(p);
}

// repeated tests on correct alignment/size
for _ in 0..16 {
unsafe {
let p = aligned_alloc(16, 16);
assert!(!p.is_null());
assert!(p.is_aligned_to(16));
libc::free(p);
}
}
}

fn main() {
test_malloc();
test_calloc();
Expand All @@ -254,6 +292,8 @@ fn main() {
target_os = "wasi",
)))]
test_reallocarray();
#[cfg(not(target_os = "windows"))]
test_aligned_alloc();

test_memcpy();
test_strcpy();
Expand Down

0 comments on commit d654b73

Please sign in to comment.