support aligned_alloc for unixes support.

This commit is contained in:
David Carlier 2024-05-07 23:11:48 +00:00
parent 7e5b9e215d
commit 5ea21ca486
No known key found for this signature in database
GPG key ID: CEF290BB40D2086B
6 changed files with 128 additions and 0 deletions

View file

@ -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()),
}
}
}

View file

@ -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" => {

View file

@ -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),
}

View file

@ -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
}
}

View file

@ -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

View file

@ -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();
@ -254,6 +292,8 @@ fn main() {
target_os = "wasi",
)))]
test_reallocarray();
#[cfg(not(target_os = "windows"))]
test_aligned_alloc();
test_memcpy();
test_strcpy();